Merge "[AChoreographer] Add private api that decouples from ALooper."
diff --git a/aidl/gui/android/view/LayerMetadataKey.aidl b/aidl/gui/android/view/LayerMetadataKey.aidl
new file mode 100644
index 0000000..7026ca8
--- /dev/null
+++ b/aidl/gui/android/view/LayerMetadataKey.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+/** @hide */
+@Backing(type="int")
+enum LayerMetadataKey {
+ METADATA_OWNER_UID = 1,
+ METADATA_WINDOW_TYPE = 2,
+ METADATA_TASK_ID = 3,
+ METADATA_MOUSE_CURSOR = 4,
+}
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 08a31c1..402767a 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -15,6 +15,7 @@
cc_defaults {
name: "idlcli-defaults",
shared_libs: [
+ "android.hardware.vibrator-ndk_platform",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
@@ -24,7 +25,6 @@
"libhidlbase",
"liblog",
"libutils",
- "vintf-vibrator-ndk_platform",
],
cflags: [
"-DLOG_TAG=\"idlcli\"",
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index f8a68b4..6b14bee 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -639,10 +639,8 @@
if (delete_dir_contents(path, true) != 0) {
res = error("Failed to delete contents of " + path);
}
- path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname);
- if (delete_dir_contents(path, true) != 0) {
- res = error("Failed to delete contents of " + path);
- }
+ // Note that we explicitly don't delete OBBs - those are only removed on
+ // app uninstall.
}
}
}
diff --git a/cmds/installd/QuotaUtils.cpp b/cmds/installd/QuotaUtils.cpp
index a71e01c..e080291 100644
--- a/cmds/installd/QuotaUtils.cpp
+++ b/cmds/installd/QuotaUtils.cpp
@@ -101,6 +101,26 @@
}
}
+int64_t GetOccupiedSpaceForProjectId(const std::string& uuid, int projectId) {
+ const std::string device = FindQuotaDeviceForUuid(uuid);
+ if (device == "") {
+ return -1;
+ }
+ struct dqblk dq;
+ if (quotactl(QCMD(Q_GETQUOTA, PRJQUOTA), device.c_str(), projectId,
+ reinterpret_cast<char*>(&dq)) != 0) {
+ if (errno != ESRCH) {
+ PLOG(ERROR) << "Failed to quotactl " << device << " for Project ID " << projectId;
+ }
+ return -1;
+ } else {
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "quotactl() for Project ID " << projectId << " " << dq.dqb_curspace;
+#endif
+ return dq.dqb_curspace;
+ }
+}
+
int64_t GetOccupiedSpaceForGid(const std::string& uuid, gid_t gid) {
const std::string device = FindQuotaDeviceForUuid(uuid);
if (device == "") {
diff --git a/cmds/installd/QuotaUtils.h b/cmds/installd/QuotaUtils.h
index 9ad170f..96aca04 100644
--- a/cmds/installd/QuotaUtils.h
+++ b/cmds/installd/QuotaUtils.h
@@ -35,6 +35,8 @@
/* Get the current occupied space in bytes for a gid or -1 if fails */
int64_t GetOccupiedSpaceForGid(const std::string& uuid, gid_t gid);
+/* Get the current occupied space in bytes for a project id or -1 if fails */
+int64_t GetOccupiedSpaceForProjectId(const std::string& uuid, int projectId);
} // namespace installd
} // namespace android
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 141171b..ae74ac3 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -18,6 +18,9 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <binder/BpBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
#include <binder/Stability.h>
#include <cutils/android_filesystem_config.h>
#include <cutils/multiuser.h>
@@ -80,7 +83,7 @@
ServiceManager::~ServiceManager() {
// this should only happen in tests
- for (const auto& [name, callbacks] : mNameToCallback) {
+ for (const auto& [name, callbacks] : mNameToRegistrationCallback) {
CHECK(!callbacks.empty()) << name;
for (const auto& callback : callbacks) {
CHECK(callback != nullptr) << name;
@@ -108,10 +111,11 @@
auto ctx = mAccess->getCallingContext();
sp<IBinder> out;
+ Service* service = nullptr;
if (auto it = mNameToService.find(name); it != mNameToService.end()) {
- const Service& service = it->second;
+ service = &(it->second);
- if (!service.allowIsolated) {
+ if (!service->allowIsolated) {
uid_t appid = multiuser_get_app_id(ctx.uid);
bool isIsolated = appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END;
@@ -119,7 +123,7 @@
return nullptr;
}
}
- out = service.binder;
+ out = service->binder;
}
if (!mAccess->canFind(ctx, name)) {
@@ -130,6 +134,12 @@
tryStartService(name);
}
+ if (out) {
+ // Setting this guarantee each time we hand out a binder ensures that the client-checking
+ // loop knows about the event even if the client immediately drops the service
+ service->guaranteeClient = true;
+ }
+
return out;
}
@@ -182,15 +192,17 @@
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
- mNameToService[name] = Service {
+ auto entry = mNameToService.emplace(name, Service {
.binder = binder,
.allowIsolated = allowIsolated,
.dumpPriority = dumpPriority,
- };
+ .debugPid = ctx.debugPid,
+ });
- auto it = mNameToCallback.find(name);
- if (it != mNameToCallback.end()) {
+ auto it = mNameToRegistrationCallback.find(name);
+ if (it != mNameToRegistrationCallback.end()) {
for (const sp<IServiceCallback>& cb : it->second) {
+ entry.first->second.guaranteeClient = true;
// permission checked in registerForNotifications
cb->onRegistration(name, binder);
}
@@ -247,7 +259,7 @@
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
- mNameToCallback[name].push_back(callback);
+ mNameToRegistrationCallback[name].push_back(callback);
if (auto it = mNameToService.find(name); it != mNameToService.end()) {
const sp<IBinder>& binder = it->second.binder;
@@ -269,9 +281,9 @@
bool found = false;
- auto it = mNameToCallback.find(name);
- if (it != mNameToCallback.end()) {
- removeCallback(IInterface::asBinder(callback), &it, &found);
+ auto it = mNameToRegistrationCallback.find(name);
+ if (it != mNameToRegistrationCallback.end()) {
+ removeRegistrationCallback(IInterface::asBinder(callback), &it, &found);
}
if (!found) {
@@ -297,8 +309,8 @@
return Status::ok();
}
-void ServiceManager::removeCallback(const wp<IBinder>& who,
- CallbackMap::iterator* it,
+void ServiceManager::removeRegistrationCallback(const wp<IBinder>& who,
+ ServiceCallbackMap::iterator* it,
bool* found) {
std::vector<sp<IServiceCallback>>& listeners = (*it)->second;
@@ -312,7 +324,7 @@
}
if (listeners.empty()) {
- *it = mNameToCallback.erase(*it);
+ *it = mNameToRegistrationCallback.erase(*it);
} else {
(*it)++;
}
@@ -327,8 +339,12 @@
}
}
- for (auto it = mNameToCallback.begin(); it != mNameToCallback.end();) {
- removeCallback(who, &it, nullptr /*found*/);
+ for (auto it = mNameToRegistrationCallback.begin(); it != mNameToRegistrationCallback.end();) {
+ removeRegistrationCallback(who, &it, nullptr /*found*/);
+ }
+
+ for (auto it = mNameToClientCallback.begin(); it != mNameToClientCallback.end();) {
+ removeClientCallback(who, &it);
}
}
@@ -341,4 +357,183 @@
}).detach();
}
-} // namespace android
+Status ServiceManager::registerClientCallback(const std::string& name, const sp<IBinder>& service,
+ const sp<IClientCallback>& cb) {
+ if (cb == nullptr) {
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ }
+
+ auto ctx = mAccess->getCallingContext();
+ if (!mAccess->canAdd(ctx, name)) {
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+
+ auto serviceIt = mNameToService.find(name);
+ if (serviceIt == mNameToService.end()) {
+ LOG(ERROR) << "Could not add callback for nonexistent service: " << name;
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+ }
+
+ if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) {
+ LOG(WARNING) << "Only a server can register for client callbacks (for " << name << ")";
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+ }
+
+ if (serviceIt->second.binder != service) {
+ LOG(WARNING) << "Tried to register client callback for " << name
+ << " but a different service is registered under this name.";
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+ }
+
+ if (OK != IInterface::asBinder(cb)->linkToDeath(this)) {
+ LOG(ERROR) << "Could not linkToDeath when adding client callback for " << name;
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ }
+
+ mNameToClientCallback[name].push_back(cb);
+
+ return Status::ok();
+}
+
+void ServiceManager::removeClientCallback(const wp<IBinder>& who,
+ ClientCallbackMap::iterator* it) {
+ std::vector<sp<IClientCallback>>& listeners = (*it)->second;
+
+ for (auto lit = listeners.begin(); lit != listeners.end();) {
+ if (IInterface::asBinder(*lit) == who) {
+ lit = listeners.erase(lit);
+ } else {
+ ++lit;
+ }
+ }
+
+ if (listeners.empty()) {
+ *it = mNameToClientCallback.erase(*it);
+ } else {
+ (*it)++;
+ }
+}
+
+ssize_t ServiceManager::Service::getNodeStrongRefCount() {
+ sp<BpBinder> bpBinder = binder->remoteBinder();
+ if (bpBinder == nullptr) return -1;
+
+ return ProcessState::self()->getStrongRefCountForNodeByHandle(bpBinder->handle());
+}
+
+void ServiceManager::handleClientCallbacks() {
+ for (const auto& [name, service] : mNameToService) {
+ handleServiceClientCallback(name);
+ }
+}
+
+ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName) {
+ auto serviceIt = mNameToService.find(serviceName);
+ if (serviceIt == mNameToService.end() || mNameToClientCallback.count(serviceName) < 1) {
+ return -1;
+ }
+
+ Service& service = serviceIt->second;
+ ssize_t count = service.getNodeStrongRefCount();
+
+ // binder driver doesn't support this feature
+ if (count == -1) return count;
+
+ bool hasClients = count > 1; // this process holds a strong count
+
+ if (service.guaranteeClient) {
+ // we have no record of this client
+ if (!service.hasClients && !hasClients) {
+ sendClientCallbackNotifications(serviceName, true);
+ }
+
+ // guarantee is temporary
+ service.guaranteeClient = false;
+ }
+
+ if (hasClients && !service.hasClients) {
+ // client was retrieved in some other way
+ sendClientCallbackNotifications(serviceName, true);
+ }
+
+ // there are no more clients, but the callback has not been called yet
+ if (!hasClients && service.hasClients) {
+ sendClientCallbackNotifications(serviceName, false);
+ }
+
+ return count;
+}
+
+void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName, bool hasClients) {
+ auto serviceIt = mNameToService.find(serviceName);
+ if (serviceIt == mNameToService.end()) {
+ LOG(WARNING) << "sendClientCallbackNotifications could not find service " << serviceName;
+ return;
+ }
+ Service& service = serviceIt->second;
+
+ CHECK(hasClients != service.hasClients) << "Record shows: " << service.hasClients
+ << " so we can't tell clients again that we have client: " << hasClients;
+
+ LOG(INFO) << "Notifying " << serviceName << " they have clients: " << hasClients;
+
+ auto ccIt = mNameToClientCallback.find(serviceName);
+ CHECK(ccIt != mNameToClientCallback.end())
+ << "sendClientCallbackNotifications could not find callbacks for service ";
+
+ for (const auto& callback : ccIt->second) {
+ callback->onClients(service.binder, hasClients);
+ }
+
+ service.hasClients = hasClients;
+}
+
+Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IBinder>& binder) {
+ if (binder == nullptr) {
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ }
+
+ auto ctx = mAccess->getCallingContext();
+ if (!mAccess->canAdd(ctx, name)) {
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+
+ auto serviceIt = mNameToService.find(name);
+ if (serviceIt == mNameToService.end()) {
+ LOG(WARNING) << "Tried to unregister " << name
+ << ", but that service wasn't registered to begin with.";
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ }
+
+ if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) {
+ LOG(WARNING) << "Only a server can unregister itself (for " << name << ")";
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+ }
+
+ sp<IBinder> storedBinder = serviceIt->second.binder;
+
+ if (binder != storedBinder) {
+ LOG(WARNING) << "Tried to unregister " << name
+ << ", but a different service is registered under this name.";
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ }
+
+ int clients = handleServiceClientCallback(name);
+
+ // clients < 0: feature not implemented or other error. Assume clients.
+ // Otherwise:
+ // - kernel driver will hold onto one refcount (during this transaction)
+ // - servicemanager has a refcount (guaranteed by this transaction)
+ // So, if clients > 2, then at least one other service on the system must hold a refcount.
+ if (clients < 0 || clients > 2) {
+ // client callbacks are either disabled or there are other clients
+ LOG(INFO) << "Tried to unregister " << name << " but there are clients: " << clients;
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ }
+
+ mNameToService.erase(name);
+
+ return Status::ok();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 7dcdaa4..77f5250 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -17,12 +17,14 @@
#pragma once
#include <android/os/BnServiceManager.h>
+#include <android/os/IClientCallback.h>
#include <android/os/IServiceCallback.h>
#include "Access.h"
namespace android {
+using os::IClientCallback;
using os::IServiceCallback;
class ServiceManager : public os::BnServiceManager, public IBinder::DeathRecipient {
@@ -40,9 +42,13 @@
const sp<IServiceCallback>& callback) override;
binder::Status unregisterForNotifications(const std::string& name,
const sp<IServiceCallback>& callback) override;
- binder::Status isDeclared(const std::string& name, bool* outReturn) override;
+ binder::Status isDeclared(const std::string& name, bool* 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;
void binderDied(const wp<IBinder>& who) override;
+ void handleClientCallbacks();
protected:
virtual void tryStartService(const std::string& name);
@@ -52,20 +58,35 @@
sp<IBinder> binder; // not null
bool allowIsolated;
int32_t dumpPriority;
+ bool hasClients = false; // notifications sent on true -> false.
+ bool guaranteeClient = false; // forces the client check to true
+ pid_t debugPid = 0; // the process in which this service runs
+
+ // the number of clients of the service, including servicemanager itself
+ ssize_t getNodeStrongRefCount();
};
- using CallbackMap = std::map<std::string, std::vector<sp<IServiceCallback>>>;
+ using ServiceCallbackMap = std::map<std::string, std::vector<sp<IServiceCallback>>>;
+ using ClientCallbackMap = std::map<std::string, std::vector<sp<IClientCallback>>>;
using ServiceMap = std::map<std::string, Service>;
- // removes a callback from mNameToCallback, removing it if the vector is empty
+ // removes a callback from mNameToRegistrationCallback, removing it if the vector is empty
// this updates iterator to the next location
- void removeCallback(const wp<IBinder>& who,
- CallbackMap::iterator* it,
+ void removeRegistrationCallback(const wp<IBinder>& who,
+ ServiceCallbackMap::iterator* it,
bool* found);
+ ssize_t handleServiceClientCallback(const std::string& serviceName);
+ // Also updates mHasClients (of what the last callback was)
+ void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients);
+ // removes a callback from mNameToClientCallback, deleting the entry if the vector is empty
+ // this updates the iterator to the next location
+ void removeClientCallback(const wp<IBinder>& who, ClientCallbackMap::iterator* it);
+
sp<IBinder> tryGetService(const std::string& name, bool startIfNotFound);
- CallbackMap mNameToCallback;
ServiceMap mNameToService;
+ ServiceCallbackMap mNameToRegistrationCallback;
+ ClientCallbackMap mNameToClientCallback;
std::unique_ptr<Access> mAccess;
};
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 4b12fc6..2618906 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -18,18 +18,101 @@
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/Status.h>
+#include <sys/timerfd.h>
+#include <utils/Looper.h>
#include <utils/StrongPointer.h>
#include "Access.h"
#include "ServiceManager.h"
using ::android::Access;
+using ::android::sp;
+using ::android::Looper;
+using ::android::LooperCallback;
+using ::android::ProcessState;
using ::android::IPCThreadState;
using ::android::ProcessState;
using ::android::ServiceManager;
using ::android::os::IServiceManager;
using ::android::sp;
+class BinderCallback : public LooperCallback {
+public:
+ static sp<BinderCallback> setupTo(const sp<Looper>& looper) {
+ sp<BinderCallback> cb = new BinderCallback;
+
+ int binder_fd = -1;
+ IPCThreadState::self()->setupPolling(&binder_fd);
+ LOG_ALWAYS_FATAL_IF(binder_fd < 0, "Failed to setupPolling: %d", binder_fd);
+
+ // Flush after setupPolling(), to make sure the binder driver
+ // knows about this thread handling commands.
+ IPCThreadState::self()->flushCommands();
+
+ int ret = looper->addFd(binder_fd,
+ Looper::POLL_CALLBACK,
+ Looper::EVENT_INPUT,
+ cb,
+ nullptr /*data*/);
+ LOG_ALWAYS_FATAL_IF(ret != 1, "Failed to add binder FD to Looper");
+
+ return cb;
+ }
+
+ int handleEvent(int /* fd */, int /* events */, void* /* data */) override {
+ IPCThreadState::self()->handlePolledCommands();
+ return 1; // Continue receiving callbacks.
+ }
+};
+
+// LooperCallback for IClientCallback
+class ClientCallbackCallback : public LooperCallback {
+public:
+ static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) {
+ sp<ClientCallbackCallback> cb = new ClientCallbackCallback(manager);
+
+ int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/);
+ LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno);
+
+ itimerspec timespec {
+ .it_interval = {
+ .tv_sec = 5,
+ .tv_nsec = 0,
+ },
+ .it_value = {
+ .tv_sec = 5,
+ .tv_nsec = 0,
+ },
+ };
+
+ int timeRes = timerfd_settime(fdTimer, 0 /*flags*/, ×pec, nullptr);
+ LOG_ALWAYS_FATAL_IF(timeRes < 0, "Failed to timerfd_settime: res: %d err: %d", timeRes, errno);
+
+ int addRes = looper->addFd(fdTimer,
+ Looper::POLL_CALLBACK,
+ Looper::EVENT_INPUT,
+ cb,
+ nullptr);
+ LOG_ALWAYS_FATAL_IF(addRes != 1, "Failed to add client callback FD to Looper");
+
+ return cb;
+ }
+
+ int handleEvent(int fd, int /*events*/, void* /*data*/) override {
+ uint64_t expirations;
+ int ret = read(fd, &expirations, sizeof(expirations));
+ if (ret != sizeof(expirations)) {
+ ALOGE("Read failed to callback FD: ret: %d err: %d", ret, errno);
+ }
+
+ mManager->handleClientCallbacks();
+ return 1; // Continue receiving callbacks.
+ }
+private:
+ ClientCallbackCallback(const sp<ServiceManager>& manager) : mManager(manager) {}
+ sp<ServiceManager> mManager;
+};
+
int main(int argc, char** argv) {
if (argc > 2) {
LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
@@ -49,7 +132,14 @@
IPCThreadState::self()->setTheContextObject(manager);
ps->becomeContextManager(nullptr, nullptr);
- IPCThreadState::self()->joinThreadPool();
+ sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
+
+ BinderCallback::setupTo(looper);
+ ClientCallbackCallback::setupTo(looper, manager);
+
+ while(true) {
+ looper->pollAll(-1);
+ }
// should not be reached
return EXIT_FAILURE;
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index 792ff91..7f2f949 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -51,6 +51,7 @@
RelativeParentChange relative_parent = 18;
DetachChildrenChange detach_children = 19;
ReparentChildrenChange reparent_children = 20;
+ ShadowRadiusChange shadow_radius = 22;
}
}
@@ -199,3 +200,7 @@
message DetachChildrenChange {
required bool detach_children = 1;
}
+
+message ShadowRadiusChange {
+ required float radius = 1;
+}
\ No newline at end of file
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index a4a9b6a..0d6c31e 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -424,6 +424,9 @@
case SurfaceChange::SurfaceChangeCase::kDetachChildren:
setDetachChildrenChange(transaction, change.id(), change.detach_children());
break;
+ case SurfaceChange::SurfaceChangeCase::kShadowRadius:
+ setShadowRadiusChange(transaction, change.id(), change.shadow_radius());
+ break;
default:
status = 1;
break;
@@ -724,3 +727,8 @@
}
t.reparentChildren(mLayers[id], mLayers[c.parent_id()]->getHandle());
}
+
+void Replayer::setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
+ layer_id id, const ShadowRadiusChange& c) {
+ t.setShadowRadius(mLayers[id], c.radius());
+}
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index 3b94618..b547834 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -118,6 +118,8 @@
layer_id id, const DetachChildrenChange& c);
void setReparentChildrenChange(SurfaceComposerClient::Transaction& t,
layer_id id, const ReparentChildrenChange& c);
+ void setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
+ layer_id id, const ShadowRadiusChange& c);
void setDisplaySurface(SurfaceComposerClient::Transaction& t,
display_id id, const DispSurfaceChange& dsc);
diff --git a/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png
new file mode 100644
index 0000000..b6c0765
--- /dev/null
+++ b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png
new file mode 100644
index 0000000..4e975e5
--- /dev/null
+++ b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png
new file mode 100644
index 0000000..a331095
--- /dev/null
+++ b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png
new file mode 100644
index 0000000..41e6668
--- /dev/null
+++ b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png
Binary files differ
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index f852cd0..c44db51 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -119,7 +119,11 @@
/* These values are filled in by the WM and passed through SurfaceFlinger
* unless specified otherwise.
*/
+ // This value should NOT be used to uniquely identify the window. There may be different
+ // input windows that have the same token.
sp<IBinder> token;
+ // This uniquely identifies the input window.
+ int32_t id = 0;
std::string name;
int32_t layoutParamsFlags;
int32_t layoutParamsType;
@@ -157,7 +161,6 @@
bool hasFocus;
bool hasWallpaper;
bool paused;
- int32_t layer;
int32_t ownerPid;
int32_t ownerUid;
int32_t inputFeatures;
@@ -203,6 +206,8 @@
sp<IBinder> getToken() const;
+ int32_t getId() const { return mInfo.id; }
+
sp<IBinder> getApplicationToken() {
return mInfo.applicationInfo.token;
}
diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp
index 6479109..a9c2311 100644
--- a/libs/adbd_auth/adbd_auth.cpp
+++ b/libs/adbd_auth/adbd_auth.cpp
@@ -178,6 +178,10 @@
this->callbacks_.key_authorized(arg, id);
this->dispatched_prompt_ = std::nullopt;
+
+ // We need to dispatch pending prompts here upon success as well,
+ // since we might have multiple queued prompts.
+ DispatchPendingPrompt();
} else if (packet[0] == 'N' && packet[1] == 'O') {
CHECK_EQ(2UL, packet.length());
// TODO: Do we want a callback if the key is denied?
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 7ee4882..079dd82 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -90,6 +90,7 @@
"IResultReceiver.cpp",
"IServiceManager.cpp",
"IShellCallback.cpp",
+ "LazyServiceRegistrar.cpp",
"MemoryBase.cpp",
"MemoryDealer.cpp",
"MemoryHeapBase.cpp",
@@ -160,6 +161,7 @@
name: "libbinder_aidl",
srcs: [
"aidl/android/content/pm/IPackageManagerNative.aidl",
+ "aidl/android/os/IClientCallback.aidl",
"aidl/android/os/IServiceCallback.aidl",
"aidl/android/os/IServiceManager.aidl",
],
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index ca9c608..aeca12b 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -123,13 +123,10 @@
const std::unique_ptr<String16>& featureId, const String16& message) {
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
- ? service->noteOperation(op, uid, callingPackage, featureId)
+ ? service->noteOperation(op, uid, callingPackage, featureId, shouldCollectNotes(op),
+ message)
: APP_OPS_MANAGER_UNAVAILABLE_MODE;
- if (mode == AppOpsManager::MODE_ALLOWED) {
- markAppOpNoted(uid, callingPackage, op, featureId, message);
- }
-
return mode;
}
@@ -145,11 +142,8 @@
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
? service->startOperation(getClientId(), op, uid, callingPackage,
- featureId, startIfModeDefault) : APP_OPS_MANAGER_UNAVAILABLE_MODE;
-
- if (mode == AppOpsManager::MODE_ALLOWED) {
- markAppOpNoted(uid, callingPackage, op, featureId, message);
- }
+ featureId, startIfModeDefault, shouldCollectNotes(op), message)
+ : APP_OPS_MANAGER_UNAVAILABLE_MODE;
return mode;
}
@@ -196,40 +190,17 @@
}
}
+// check it the appops needs to be collected and cache result
bool AppOpsManager::shouldCollectNotes(int32_t opcode) {
- sp<IAppOpsService> service = getService();
- if (service != nullptr) {
- return service->shouldCollectNotes(opcode);
- }
- return false;
-}
-
-void AppOpsManager::markAppOpNoted(int32_t uid, const String16& packageName, int32_t opCode,
- const std::unique_ptr<String16>& featureId, const String16& message) {
- // check it the appops needs to be collected and cache result
- if (appOpsToNote[opCode] == 0) {
- if (shouldCollectNotes(opCode)) {
- appOpsToNote[opCode] = 2;
+ if (appOpsToNote[opcode] == 0) {
+ if (getService()->shouldCollectNotes(opcode)) {
+ appOpsToNote[opcode] = 2;
} else {
- appOpsToNote[opCode] = 1;
+ appOpsToNote[opcode] = 1;
}
}
- if (appOpsToNote[opCode] != 2) {
- return;
- }
-
- noteAsyncOp(std::unique_ptr<String16>(), uid, packageName, opCode, featureId, message);
-}
-
-void AppOpsManager::noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid,
- const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId,
- const String16& message) {
- sp<IAppOpsService> service = getService();
- if (service != nullptr) {
- return service->noteAsyncOp(callingPackageName, uid, packageName, opCode, featureId,
- message);
- }
+ return appOpsToNote[opcode] == 2;
}
} // namespace android
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index 7384466..a5555a3 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -47,13 +47,16 @@
}
virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
- const std::unique_ptr<String16>& featureId) {
+ const std::unique_ptr<String16>& featureId, bool shouldCollectAsyncNotedOp,
+ const String16& message) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeInt32(code);
data.writeInt32(uid);
data.writeString16(packageName);
data.writeString16(featureId);
+ data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
+ data.writeString16(message);
remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
// fail on exception
if (reply.readExceptionCode() != 0) return MODE_ERRORED;
@@ -62,7 +65,7 @@
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
const String16& packageName, const std::unique_ptr<String16>& featureId,
- bool startIfModeDefault) {
+ bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeStrongBinder(token);
@@ -71,6 +74,8 @@
data.writeString16(packageName);
data.writeString16(featureId);
data.writeInt32(startIfModeDefault ? 1 : 0);
+ data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
+ data.writeString16(message);
remote()->transact(START_OPERATION_TRANSACTION, data, &reply);
// fail on exception
if (reply.readExceptionCode() != 0) return MODE_ERRORED;
@@ -139,20 +144,6 @@
remote()->transact(SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION, data, &reply);
}
- virtual void noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid,
- const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId,
- const String16& message) {
- Parcel data, reply;
- data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
- data.writeString16(callingPackageName);
- data.writeInt32(uid);
- data.writeString16(packageName);
- data.writeInt32(opCode);
- data.writeString16(featureId);
- data.writeString16(message);
- remote()->transact(NOTE_ASYNC_OP_TRANSACTION, data, &reply);
- }
-
virtual bool shouldCollectNotes(int32_t opCode) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
@@ -193,7 +184,10 @@
String16 packageName = data.readString16();
std::unique_ptr<String16> featureId;
data.readString16(&featureId);
- int32_t res = noteOperation(code, uid, packageName, featureId);
+ bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
+ String16 message = data.readString16();
+ int32_t res = noteOperation(code, uid, packageName, featureId,
+ shouldCollectAsyncNotedOp, message);
reply->writeNoException();
reply->writeInt32(res);
return NO_ERROR;
@@ -207,8 +201,10 @@
std::unique_ptr<String16> featureId;
data.readString16(&featureId);
bool startIfModeDefault = data.readInt32() == 1;
+ bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
+ String16 message = data.readString16();
int32_t res = startOperation(token, code, uid, packageName, featureId,
- startIfModeDefault);
+ startIfModeDefault, shouldCollectAsyncNotedOp, message);
reply->writeNoException();
reply->writeInt32(res);
return NO_ERROR;
@@ -267,20 +263,6 @@
reply->writeNoException();
return NO_ERROR;
} break;
- case NOTE_ASYNC_OP_TRANSACTION: {
- CHECK_INTERFACE(IAppOpsService, data, reply);
- std::unique_ptr<String16> callingPackageName;
- data.readString16(&callingPackageName);
- int32_t uid = data.readInt32();
- String16 packageName = data.readString16();
- int32_t opCode = data.readInt32();
- std::unique_ptr<String16> featureId;
- data.readString16(&featureId);
- String16 message = data.readString16();
- noteAsyncOp(callingPackageName, uid, packageName, opCode, featureId, message);
- reply->writeNoException();
- return NO_ERROR;
- } break;
case SHOULD_COLLECT_NOTES_TRANSACTION: {
CHECK_INTERFACE(IAppOpsService, data, reply);
int32_t opCode = data.readInt32();
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index bac8b66..5ca9156 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -271,6 +271,8 @@
std::unique_lock<std::mutex> lock(mMutex);
mBinder = binder;
lock.unlock();
+ // Flushing here helps ensure the service's ref count remains accurate
+ IPCThreadState::self()->flushCommands();
mCv.notify_one();
return Status::ok();
}
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
new file mode 100644
index 0000000..dc9482c
--- /dev/null
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AidlLazyServiceRegistrar"
+
+#include <binder/LazyServiceRegistrar.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <android/os/BnClientCallback.h>
+#include <android/os/IServiceManager.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace binder {
+namespace internal {
+
+using AidlServiceManager = android::os::IServiceManager;
+
+class ClientCounterCallback : public ::android::os::BnClientCallback {
+public:
+ ClientCounterCallback() : mNumConnectedServices(0) {}
+
+ bool registerService(const sp<IBinder>& service, const std::string& name,
+ bool allowIsolated, int dumpFlags);
+
+protected:
+ Status onClients(const sp<IBinder>& service, bool clients) override;
+
+private:
+ /**
+ * Unregisters all services that we can. If we can't unregister all, re-register other
+ * services.
+ */
+ void tryShutdown();
+
+ /**
+ * Counter of the number of services that currently have at least one client.
+ */
+ size_t mNumConnectedServices;
+
+ struct Service {
+ sp<IBinder> service;
+ std::string name;
+ bool allowIsolated;
+ int dumpFlags;
+ };
+ /**
+ * Number of services that have been registered.
+ */
+ std::vector<Service> mRegisteredServices;
+};
+
+bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
+ bool allowIsolated, int dumpFlags) {
+ auto manager = interface_cast<AidlServiceManager>(
+ ProcessState::self()->getContextObject(nullptr));
+
+ ALOGI("Registering service %s", name.c_str());
+
+ if (!manager->addService(name.c_str(), service, allowIsolated, dumpFlags).isOk()) {
+ ALOGE("Failed to register service %s", name.c_str());
+ return false;
+ }
+
+ if (!manager->registerClientCallback(name, service, this).isOk())
+ {
+ ALOGE("Failed to add client callback for service %s", name.c_str());
+ return false;
+ }
+
+ mRegisteredServices.push_back({service, name, allowIsolated, dumpFlags});
+
+ return true;
+}
+
+/**
+ * onClients is oneway, so no need to worry about multi-threading. Note that this means multiple
+ * invocations could occur on different threads however.
+ */
+Status ClientCounterCallback::onClients(const sp<IBinder>& service, bool clients) {
+ if (clients) {
+ mNumConnectedServices++;
+ } else {
+ mNumConnectedServices--;
+ }
+
+ ALOGI("Process has %zu (of %zu available) client(s) in use after notification %s has clients: %d",
+ mNumConnectedServices, mRegisteredServices.size(),
+ String8(service->getInterfaceDescriptor()).string(), clients);
+
+ if (mNumConnectedServices == 0) {
+ tryShutdown();
+ }
+
+ return Status::ok();
+}
+
+void ClientCounterCallback::tryShutdown() {
+ ALOGI("Trying to shut down the service. No clients in use for any service in process.");
+
+ // This makes the same assumption as IServiceManager.cpp. Could dedupe if used in more places.
+ auto manager = interface_cast<AidlServiceManager>(
+ ProcessState::self()->getContextObject(nullptr));
+
+ auto unRegisterIt = mRegisteredServices.begin();
+ for (; unRegisterIt != mRegisteredServices.end(); ++unRegisterIt) {
+ auto& entry = (*unRegisterIt);
+
+ bool success = manager->tryUnregisterService(entry.name, entry.service).isOk();
+
+ if (!success) {
+ ALOGI("Failed to unregister service %s", entry.name.c_str());
+ break;
+ }
+ }
+
+ if (unRegisterIt == mRegisteredServices.end()) {
+ ALOGI("Unregistered all clients and exiting");
+ exit(EXIT_SUCCESS);
+ }
+
+ for (auto reRegisterIt = mRegisteredServices.begin(); reRegisterIt != unRegisterIt;
+ reRegisterIt++) {
+ auto& entry = (*reRegisterIt);
+
+ // re-register entry
+ if (!registerService(entry.service, entry.name, entry.allowIsolated, entry.dumpFlags)) {
+ // Must restart. Otherwise, clients will never be able to get a hold of this service.
+ ALOGE("Bad state: could not re-register services");
+ }
+ }
+}
+
+} // namespace internal
+
+LazyServiceRegistrar::LazyServiceRegistrar() {
+ mClientCC = std::make_shared<internal::ClientCounterCallback>();
+}
+
+LazyServiceRegistrar& LazyServiceRegistrar::getInstance() {
+ static auto registrarInstance = new LazyServiceRegistrar();
+ return *registrarInstance;
+}
+
+status_t LazyServiceRegistrar::registerService(const sp<IBinder>& service, const std::string& name,
+ bool allowIsolated, int dumpFlags) {
+ if (!mClientCC->registerService(service, name, allowIsolated, dumpFlags)) {
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/libs/binder/aidl/android/os/IClientCallback.aidl b/libs/binder/aidl/android/os/IClientCallback.aidl
new file mode 100644
index 0000000..36d7ee6
--- /dev/null
+++ b/libs/binder/aidl/android/os/IClientCallback.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * @hide
+ */
+oneway interface IClientCallback {
+ /**
+ * This is called when there is a transition between having >= 1 clients and having 0 clients
+ * (or vice versa).
+ *
+ * Upon receiving hasClients false, if the process decides to exit, it is recommended to try to
+ * unregister using IServiceManager's tryUnregister before quitting in case another client
+ * associates.
+ *
+ * @param registered binder 'server' registered with IServiceManager's registerClientCallback
+ * @param hasClients whether there are currently clients
+ * true - when there are >= 1 clients. This must be called as soon as IServiceManager::get
+ * is called (no race).
+ * false - when there are 0 clients. This may be delayed if it is thought that another
+ * may be used again soon.
+ */
+ void onClients(IBinder registered, boolean hasClients);
+}
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index b965881..ff15460 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -16,6 +16,7 @@
package android.os;
+import android.os.IClientCallback;
import android.os.IServiceCallback;
/**
@@ -96,4 +97,15 @@
* manifest.
*/
boolean isDeclared(@utf8InCpp String name);
+
+ /**
+ * Request a callback when the number of clients of the service changes.
+ * Used by LazyServiceRegistrar to dynamically stop services that have no clients.
+ */
+ void registerClientCallback(@utf8InCpp String name, IBinder service, IClientCallback callback);
+
+ /**
+ * Attempt to unregister and remove a service. Will fail if the service is still in use.
+ */
+ void tryUnregisterService(@utf8InCpp String name, IBinder service);
}
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 22a0179..5b6eb68 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -151,17 +151,12 @@
void stopWatchingMode(const sp<IAppOpsCallback>& callback);
int32_t permissionToOpCode(const String16& permission);
void setCameraAudioRestriction(int32_t mode);
- void noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid,
- const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId,
- const String16& message);
private:
Mutex mLock;
sp<IAppOpsService> mService;
sp<IAppOpsService> getService();
- void markAppOpNoted(int32_t uid, const String16& packageName, int32_t opCode,
- const std::unique_ptr<String16>& featureId, const String16& message);
bool shouldCollectNotes(int32_t opCode);
};
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index 68a917e..1b4bcce 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -36,10 +36,11 @@
virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
- const std::unique_ptr<String16>& featureId) = 0;
+ const std::unique_ptr<String16>& featureId, bool shouldCollectAsyncNotedOp,
+ const String16& message) = 0;
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
const String16& packageName, const std::unique_ptr<String16>& featureId,
- bool startIfModeDefault) = 0;
+ bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) = 0;
virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
const String16& packageName, const std::unique_ptr<String16>& featureId) = 0;
virtual void startWatchingMode(int32_t op, const String16& packageName,
@@ -49,9 +50,6 @@
virtual int32_t checkAudioOperation(int32_t code, int32_t usage,int32_t uid,
const String16& packageName) = 0;
virtual void setCameraAudioRestriction(int32_t mode) = 0;
- virtual void noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid,
- const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId,
- const String16& message) = 0;
virtual bool shouldCollectNotes(int32_t opCode) = 0;
enum {
@@ -63,9 +61,8 @@
STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5,
PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
- NOTE_ASYNC_OP_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
- SHOULD_COLLECT_NOTES_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+9,
- SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+10,
+ SHOULD_COLLECT_NOTES_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
+ SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+9,
};
enum {
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
new file mode 100644
index 0000000..efdecc4
--- /dev/null
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace binder {
+namespace internal {
+class ClientCounterCallback;
+} // namespace internal
+
+/** Exits when all services registered through this object have 0 clients */
+class LazyServiceRegistrar {
+ public:
+ static LazyServiceRegistrar& getInstance();
+ status_t registerService(const sp<IBinder>& service,
+ const std::string& name = "default",
+ bool allowIsolated = false,
+ int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
+
+ private:
+ std::shared_ptr<internal::ClientCounterCallback> mClientCC;
+ LazyServiceRegistrar();
+};
+
+} // namespace binder
+} // namespace android
\ No newline at end of file
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index e1693cf..00f7484 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -33,16 +33,16 @@
],
shared_libs: [
+ "android.hardware.graphics.common-ndk_platform",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
"liblog",
- "vintf-graphics-common-ndk_platform",
],
export_shared_lib_headers: [
+ "android.hardware.graphics.common-ndk_platform",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
- "vintf-graphics-common-ndk_platform",
],
export_include_dirs: [
diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp
index 85137f5..f2d0943 100644
--- a/libs/graphicsenv/GpuStatsInfo.cpp
+++ b/libs/graphicsenv/GpuStatsInfo.cpp
@@ -87,6 +87,7 @@
if ((status = parcel->writeInt64Vector(angleDriverLoadingTime)) != OK) return status;
if ((status = parcel->writeBool(cpuVulkanInUse)) != OK) return status;
if ((status = parcel->writeBool(falsePrerotation)) != OK) return status;
+ if ((status = parcel->writeBool(gles1InUse)) != OK) return status;
return OK;
}
@@ -99,6 +100,7 @@
if ((status = parcel->readInt64Vector(&angleDriverLoadingTime)) != OK) return status;
if ((status = parcel->readBool(&cpuVulkanInUse)) != OK) return status;
if ((status = parcel->readBool(&falsePrerotation)) != OK) return status;
+ if ((status = parcel->readBool(&gles1InUse)) != OK) return status;
return OK;
}
@@ -108,6 +110,7 @@
StringAppendF(&result, "driverVersionCode = %" PRIu64 "\n", driverVersionCode);
StringAppendF(&result, "cpuVulkanInUse = %d\n", cpuVulkanInUse);
StringAppendF(&result, "falsePrerotation = %d\n", falsePrerotation);
+ StringAppendF(&result, "gles1InUse = %d\n", gles1InUse);
result.append("glDriverLoadingTime:");
for (int32_t loadingTime : glDriverLoadingTime) {
StringAppendF(&result, " %d", loadingTime);
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
index 7959652..9aba69f 100644
--- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -71,6 +71,7 @@
std::vector<int64_t> angleDriverLoadingTime = {};
bool cpuVulkanInUse = false;
bool falsePrerotation = false;
+ bool gles1InUse = false;
};
/*
@@ -95,6 +96,7 @@
enum Stats {
CPU_VULKAN_IN_USE = 0,
FALSE_PREROTATION = 1,
+ GLES_1_IN_USE = 2,
};
GpuStatsInfo() = default;
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 1ae148c..5959340 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -40,6 +40,7 @@
defaults: ["libgui_bufferqueue-defaults"],
srcs: [
+ ":framework_native_aidl",
":libgui_bufferqueue_sources",
"BitTube.cpp",
@@ -106,6 +107,10 @@
"libdvr_headers",
"libpdx_headers",
],
+
+ aidl: {
+ export_aidl_headers: true,
+ }
}
// Used by media codec services exclusively as a static lib for
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 3a7cb44..6418e8c 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -44,6 +44,28 @@
namespace android {
+// Macros for include BufferQueueCore information in log messages
+#define BQ_LOGV(x, ...) \
+ ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGD(x, ...) \
+ ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGI(x, ...) \
+ ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGW(x, ...) \
+ ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGE(x, ...) \
+ ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+
BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
mCore(core),
mSlots(core->mSlots),
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index d6009d6..3b0120b 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -42,6 +42,23 @@
namespace android {
+// Macros for include BufferQueueCore information in log messages
+#define BQ_LOGV(x, ...) \
+ ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGD(x, ...) \
+ ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGI(x, ...) \
+ ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGW(x, ...) \
+ ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGE(x, ...) \
+ ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+
static String8 getUniqueName() {
static volatile int32_t counter = 0;
return String8::format("unnamed-%d-%d", getpid(),
@@ -54,6 +71,19 @@
return id | counter++;
}
+static status_t getProcessName(int pid, String8& name) {
+ FILE* fp = fopen(String8::format("/proc/%d/cmdline", pid), "r");
+ if (NULL != fp) {
+ const size_t size = 64;
+ char proc_name[size];
+ fgets(proc_name, size, fp);
+ fclose(fp);
+ name = proc_name;
+ return NO_ERROR;
+ }
+ return INVALID_OPERATION;
+}
+
BufferQueueCore::BufferQueueCore() :
mMutex(),
mIsAbandoned(false),
@@ -132,6 +162,20 @@
mTransformHintInUse, mAutoPrerotation);
outResult->appendFormat("%sFIFO(%zu):\n", prefix.string(), mQueue.size());
+
+ outResult->appendFormat("%s(mConsumerName=%s, ", prefix.string(), mConsumerName.string());
+
+ outResult->appendFormat("mConnectedApi=%d, mConsumerUsageBits=%" PRIu64 ", ", mConnectedApi,
+ mConsumerUsageBits);
+
+ String8 producerProcName = String8("\?\?\?");
+ String8 consumerProcName = String8("\?\?\?");
+ int32_t pid = getpid();
+ getProcessName(mConnectedPid, producerProcName);
+ getProcessName(pid, consumerProcName);
+ outResult->appendFormat("mId=%" PRIx64 ", producer=[%d:%s], consumer=[%d:%s])\n", mUniqueId,
+ mConnectedPid, producerProcName.string(), pid,
+ consumerProcName.string());
Fifo::const_iterator current(mQueue.begin());
while (current != mQueue.end()) {
double timestamp = current->mTimestamp / 1e9;
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index a159f85..e6df757 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -44,6 +44,28 @@
namespace android {
+// Macros for include BufferQueueCore information in log messages
+#define BQ_LOGV(x, ...) \
+ ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGD(x, ...) \
+ ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGI(x, ...) \
+ ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGW(x, ...) \
+ ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGE(x, ...) \
+ ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+
static constexpr uint32_t BQ_LAYER_COUNT = 1;
BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 3ce7a4a..ab4d51e 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -525,6 +525,88 @@
return static_cast<status_t>(reply.readInt32());
}
+ virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
+ bool* outSupport) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ status_t result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("getAutoLowLatencyModeSupport failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::GET_AUTO_LOW_LATENCY_MODE_SUPPORT, data,
+ &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getAutoLowLatencyModeSupport failed to transact: %d", result);
+ return result;
+ }
+ return reply.readBool(outSupport);
+ }
+
+ virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to writeInterfaceToken: %d", result);
+ return;
+ }
+
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to writeStrongBinder: %d", result);
+ return;
+ }
+ result = data.writeBool(on);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to writeBool: %d", result);
+ return;
+ }
+ result = remote()->transact(BnSurfaceComposer::SET_AUTO_LOW_LATENCY_MODE, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to transact: %d", result);
+ return;
+ }
+ }
+
+ virtual status_t getGameContentTypeSupport(const sp<IBinder>& display, bool* outSupport) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ status_t result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("getGameContentTypeSupport failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::GET_GAME_CONTENT_TYPE_SUPPORT, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getGameContentTypeSupport failed to transact: %d", result);
+ return result;
+ }
+ return reply.readBool(outSupport);
+ }
+
+ virtual void setGameContentType(const sp<IBinder>& display, bool on) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to writeInterfaceToken: %d", result);
+ return;
+ }
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to writeStrongBinder: %d", result);
+ return;
+ }
+ result = data.writeBool(on);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to writeBool: %d", result);
+ return;
+ }
+ result = remote()->transact(BnSurfaceComposer::SET_GAME_CONTENT_TYPE, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to transact: %d", result);
+ }
+ }
+
virtual status_t clearAnimationFrameStats() {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -1354,6 +1436,75 @@
result = reply->writeInt32(result);
return result;
}
+
+ case GET_AUTO_LOW_LATENCY_MODE_SUPPORT: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("getAutoLowLatencyModeSupport failed to readStrongBinder: %d", result);
+ return result;
+ }
+ bool supported = false;
+ result = getAutoLowLatencyModeSupport(display, &supported);
+ if (result == NO_ERROR) {
+ result = reply->writeBool(supported);
+ }
+ return result;
+ }
+
+ case SET_AUTO_LOW_LATENCY_MODE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to readStrongBinder: %d", result);
+ return result;
+ }
+ bool setAllm = false;
+ result = data.readBool(&setAllm);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to readBool: %d", result);
+ return result;
+ }
+ setAutoLowLatencyMode(display, setAllm);
+ return result;
+ }
+
+ case GET_GAME_CONTENT_TYPE_SUPPORT: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("getGameContentTypeSupport failed to readStrongBinder: %d", result);
+ return result;
+ }
+ bool supported = false;
+ result = getGameContentTypeSupport(display, &supported);
+ if (result == NO_ERROR) {
+ result = reply->writeBool(supported);
+ }
+ return result;
+ }
+
+ case SET_GAME_CONTENT_TYPE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to readStrongBinder: %d", result);
+ return result;
+ }
+ bool setGameContentTypeOn = false;
+ result = data.readBool(&setGameContentTypeOn);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to readBool: %d", result);
+ return result;
+ }
+ setGameContentType(display, setGameContentTypeOn);
+ return result;
+ }
+
case CLEAR_ANIMATION_FRAME_STATS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
status_t result = clearAnimationFrameStats();
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
index 04d2871..b3eb994 100644
--- a/libs/gui/LayerMetadata.cpp
+++ b/libs/gui/LayerMetadata.cpp
@@ -18,6 +18,8 @@
#include <binder/Parcel.h>
#include <gui/LayerMetadata.h>
+#include "android/view/LayerMetadataKey.h"
+
using android::base::StringPrintf;
namespace android {
@@ -113,12 +115,12 @@
std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const {
if (!has(key)) return std::string();
- switch (key) {
- case METADATA_OWNER_UID:
+ switch (static_cast<view::LayerMetadataKey>(key)) {
+ case view::LayerMetadataKey::METADATA_OWNER_UID:
return StringPrintf("ownerUID%s%d", separator, getInt32(key, 0));
- case METADATA_WINDOW_TYPE:
+ case view::LayerMetadataKey::METADATA_WINDOW_TYPE:
return StringPrintf("windowType%s%d", separator, getInt32(key, 0));
- case METADATA_TASK_ID:
+ case view::LayerMetadataKey::METADATA_TASK_ID:
return StringPrintf("taskId%s%d", separator, getInt32(key, 0));
default:
return StringPrintf("%d%s%dbytes", key, separator,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index d10bc81..f378fc5 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -484,14 +484,22 @@
mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator(
callbackIds.begin()),
std::make_move_iterator(callbackIds.end()));
- // register surface controls for this listener that is merging
- for (const auto& surfaceControl : surfaceControls) {
- registerSurfaceControlForCallback(surfaceControl);
- }
- mListenerCallbacks[listener]
- .surfaceControls.insert(std::make_move_iterator(surfaceControls.begin()),
- std::make_move_iterator(surfaceControls.end()));
+ mListenerCallbacks[listener].surfaceControls.insert(surfaceControls.begin(),
+ surfaceControls.end());
+
+ auto& currentProcessCallbackInfo =
+ mListenerCallbacks[TransactionCompletedListener::getIInstance()];
+ currentProcessCallbackInfo.surfaceControls
+ .insert(std::make_move_iterator(surfaceControls.begin()),
+ std::make_move_iterator(surfaceControls.end()));
+
+ // register all surface controls for all callbackIds for this listener that is merging
+ for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) {
+ TransactionCompletedListener::getInstance()
+ ->addSurfaceControlToCallbacks(surfaceControl,
+ currentProcessCallbackInfo.callbackIds);
+ }
}
mInputWindowCommands.merge(other.mInputWindowCommands);
@@ -1644,6 +1652,26 @@
return ComposerService::getComposerService()->setActiveColorMode(display, colorMode);
}
+bool SurfaceComposerClient::getAutoLowLatencyModeSupport(const sp<IBinder>& display) {
+ bool supported = false;
+ ComposerService::getComposerService()->getAutoLowLatencyModeSupport(display, &supported);
+ return supported;
+}
+
+void SurfaceComposerClient::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
+ ComposerService::getComposerService()->setAutoLowLatencyMode(display, on);
+}
+
+bool SurfaceComposerClient::getGameContentTypeSupport(const sp<IBinder>& display) {
+ bool supported = false;
+ ComposerService::getComposerService()->getGameContentTypeSupport(display, &supported);
+ return supported;
+}
+
+void SurfaceComposerClient::setGameContentType(const sp<IBinder>& display, bool on) {
+ ComposerService::getComposerService()->setGameContentType(display, on);
+}
+
void SurfaceComposerClient::setDisplayPowerMode(const sp<IBinder>& token,
int mode) {
ComposerService::getComposerService()->setPowerMode(token, mode);
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 3c96089..557c28b 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -34,12 +34,6 @@
#include <mutex>
#include <condition_variable>
-#define BQ_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGD(x, ...) ALOGD("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGI(x, ...) ALOGI("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGW(x, ...) ALOGW("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGE(x, ...) ALOGE("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-
#define ATRACE_BUFFER_INDEX(index) \
do { \
if (ATRACE_ENABLED()) { \
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 024f2d6..998973c 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -195,6 +195,37 @@
ui::ColorMode colorMode) = 0;
/**
+ * Returns true if the connected display reports support for HDMI 2.1 Auto
+ * Low Latency Mode.
+ * For more information, see the HDMI 2.1 specification.
+ */
+ virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
+ bool* outSupport) const = 0;
+
+ /**
+ * Switches Auto Low Latency Mode on/off on the connected display, if it is
+ * available. This should only be called if #getAutoLowLatencyMode returns
+ * true.
+ * For more information, see the HDMI 2.1 specification.
+ */
+ virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) = 0;
+
+ /**
+ * Returns true if the connected display reports support for Game Content Type.
+ * For more information, see the HDMI 1.4 specification.
+ */
+ virtual status_t getGameContentTypeSupport(const sp<IBinder>& display,
+ bool* outSupport) const = 0;
+
+ /**
+ * This will start sending infoframes to the connected display with
+ * ContentType=Game (if on=true). This will switch the disply to Game mode.
+ * This should only be called if #getGameContentTypeSupport returns true.
+ * For more information, see the HDMI 1.4 specification.
+ */
+ virtual void setGameContentType(const sp<IBinder>& display, bool on) = 0;
+
+ /**
* Capture the specified screen. This requires READ_FRAME_BUFFER
* permission. This function will fail if there is a secure window on
* screen.
@@ -515,6 +546,10 @@
CAPTURE_SCREEN_BY_ID,
NOTIFY_POWER_HINT,
SET_GLOBAL_SHADOW_SETTINGS,
+ GET_AUTO_LOW_LATENCY_MODE_SUPPORT,
+ SET_AUTO_LOW_LATENCY_MODE,
+ GET_GAME_CONTENT_TYPE_SUPPORT,
+ SET_GAME_CONTENT_TYPE,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 873fac0..86468a4 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -148,6 +148,21 @@
static status_t setActiveColorMode(const sp<IBinder>& display,
ui::ColorMode colorMode);
+ // Reports whether the connected display supports Auto Low Latency Mode
+ static bool getAutoLowLatencyModeSupport(const sp<IBinder>& display);
+
+ // Switches on/off Auto Low Latency Mode on the connected display. This should only be
+ // called if the connected display supports Auto Low Latency Mode as reported by
+ // #getAutoLowLatencyModeSupport
+ static void setAutoLowLatencyMode(const sp<IBinder>& display, bool on);
+
+ // Reports whether the connected display supports Game content type
+ static bool getGameContentTypeSupport(const sp<IBinder>& display);
+
+ // Turns Game mode on/off on the connected display. This should only be called
+ // if the display supports Game content type, as reported by #getGameContentTypeSupport
+ static void setGameContentType(const sp<IBinder>& display, bool on);
+
/* Triggers screen on/off or low power mode and waits for it to complete */
static void setDisplayPowerMode(const sp<IBinder>& display, int mode);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index eaffc10..0445755 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -750,6 +750,16 @@
bool /*captureSecureLayers*/) override {
return NO_ERROR;
}
+ status_t getAutoLowLatencyModeSupport(const sp<IBinder>& /*display*/,
+ bool* /*outSupport*/) const override {
+ return NO_ERROR;
+ }
+ void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
+ status_t getGameContentTypeSupport(const sp<IBinder>& /*display*/,
+ bool* /*outSupport*/) const override {
+ return NO_ERROR;
+ }
+ void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
status_t captureScreen(uint64_t /*displayOrLayerStack*/, ui::Dataspace* /*outDataspace*/,
sp<GraphicBuffer>* /*outBuffer*/) override {
return NO_ERROR;
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index ec28757..03ca459 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -73,6 +73,7 @@
status_t s = output.writeStrongBinder(token);
if (s != OK) return s;
+ output.writeInt32(id);
output.writeString8(String8(name.c_str()));
output.writeInt32(layoutParamsFlags);
output.writeInt32(layoutParamsType);
@@ -90,7 +91,6 @@
output.writeBool(hasFocus);
output.writeBool(hasWallpaper);
output.writeBool(paused);
- output.writeInt32(layer);
output.writeInt32(ownerPid);
output.writeInt32(ownerUid);
output.writeInt32(inputFeatures);
@@ -116,6 +116,7 @@
}
ret.token = token;
+ ret.id = from.readInt32();
ret.name = from.readString8().c_str();
ret.layoutParamsFlags = from.readInt32();
ret.layoutParamsType = from.readInt32();
@@ -133,7 +134,6 @@
ret.hasFocus = from.readBool();
ret.hasWallpaper = from.readBool();
ret.paused = from.readBool();
- ret.layer = from.readInt32();
ret.ownerPid = from.readInt32();
ret.ownerUid = from.readInt32();
ret.inputFeatures = from.readInt32();
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index 6db18ab..d1cb527 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -40,6 +40,7 @@
sp<IBinder> touchableRegionCropHandle = new BBinder();
InputWindowInfo i;
i.token = new BBinder();
+ i.id = 1;
i.name = "Foobar";
i.layoutParamsFlags = 7;
i.layoutParamsType = 39;
@@ -57,7 +58,6 @@
i.hasFocus = false;
i.hasWallpaper = false;
i.paused = false;
- i.layer = 7;
i.ownerPid = 19;
i.ownerUid = 24;
i.inputFeatures = 29;
@@ -72,6 +72,7 @@
p.setDataPosition(0);
InputWindowInfo i2 = InputWindowInfo::read(p);
ASSERT_EQ(i.token, i2.token);
+ ASSERT_EQ(i.id, i2.id);
ASSERT_EQ(i.name, i2.name);
ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags);
ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
@@ -89,7 +90,6 @@
ASSERT_EQ(i.hasFocus, i2.hasFocus);
ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
ASSERT_EQ(i.paused, i2.paused);
- ASSERT_EQ(i.layer, i2.layer);
ASSERT_EQ(i.ownerPid, i2.ownerPid);
ASSERT_EQ(i.ownerUid, i2.ownerUid);
ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 124bda2..043f2ca 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -82,6 +82,7 @@
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
"android.hardware.graphics.allocator@4.0",
+ "android.hardware.graphics.common-ndk_platform",
"android.hardware.graphics.common@1.2",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@2.1",
@@ -94,14 +95,13 @@
"libsync",
"libutils",
"liblog",
- "vintf-graphics-common-ndk_platform",
],
export_shared_lib_headers: [
"android.hardware.graphics.common@1.2",
+ "android.hardware.graphics.common-ndk_platform",
"android.hardware.graphics.mapper@4.0",
"libgralloctypes",
- "vintf-graphics-common-ndk_platform",
],
static_libs: [
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 1fc7927..5162ba4 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -984,6 +984,8 @@
if (attr == EGL_CONTEXT_CLIENT_VERSION) {
if (value == 1) {
version = egl_connection_t::GLESv1_INDEX;
+ android::GraphicsEnv::getInstance().setTargetStats(
+ android::GpuStatsInfo::Stats::GLES_1_IN_USE);
} else if (value == 2 || value == 3) {
version = egl_connection_t::GLESv2_INDEX;
}
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index 67babd4..7fff6ed 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -145,6 +145,9 @@
case GpuStatsInfo::Stats::FALSE_PREROTATION:
mAppStats[appStatsKey].falsePrerotation = true;
break;
+ case GpuStatsInfo::Stats::GLES_1_IN_USE:
+ mAppStats[appStatsKey].gles1InUse = true;
+ break;
default:
break;
}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 9686cea..246e735 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -109,8 +109,14 @@
uint32_t consumeSeq;
InputEvent* event;
+ std::chrono::time_point start = std::chrono::steady_clock::now();
status_t result = WOULD_BLOCK;
while (result == WOULD_BLOCK) {
+ std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
+ if (elapsed > 10ms) {
+ ALOGE("Waited too long for consumer to produce an event, giving up");
+ break;
+ }
result = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq,
&event);
}
@@ -170,7 +176,6 @@
mInfo.hasFocus = true;
mInfo.hasWallpaper = false;
mInfo.paused = false;
- mInfo.layer = 0;
mInfo.ownerPid = INJECTOR_PID;
mInfo.ownerUid = INJECTOR_UID;
mInfo.inputFeatures = 0;
@@ -283,12 +288,9 @@
dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
- MotionEvent event = generateMotionEvent();
-
for (auto _ : state) {
+ MotionEvent event = generateMotionEvent();
// Send ACTION_DOWN
- event.setAction(AMOTION_EVENT_ACTION_DOWN);
- event.setDownTime(now());
dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT,
POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 5a49b5e..26c2d3f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -240,6 +240,22 @@
return removed;
}
+static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) {
+ if (first == second) {
+ return true;
+ }
+
+ if (first == nullptr || second == nullptr) {
+ return false;
+ }
+
+ return first->getToken() == second->getToken();
+}
+
+static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
+ return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
+}
+
// --- InputDispatcherThread ---
class InputDispatcher::InputDispatcherThread : public Thread {
@@ -731,10 +747,6 @@
#endif
}
-bool InputDispatcher::isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
- return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
-}
-
bool InputDispatcher::haveCommandsLocked() const {
return !mCommandQueue.empty();
}
@@ -3278,9 +3290,9 @@
// Since we compare the pointer of input window handles across window updates, we need
// to make sure the handle object for the same window stays unchanged across updates.
const std::vector<sp<InputWindowHandle>>& oldHandles = getWindowHandlesLocked(displayId);
- std::unordered_map<sp<IBinder>, sp<InputWindowHandle>, IBinderHash> oldHandlesByTokens;
+ std::unordered_map<int32_t /*id*/, sp<InputWindowHandle>> oldHandlesById;
for (const sp<InputWindowHandle>& handle : oldHandles) {
- oldHandlesByTokens[handle->getToken()] = handle;
+ oldHandlesById[handle->getId()] = handle;
}
std::vector<sp<InputWindowHandle>> newHandles;
@@ -3311,8 +3323,8 @@
continue;
}
- if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) {
- const sp<InputWindowHandle> oldHandle = oldHandlesByTokens.at(handle->getToken());
+ if (oldHandlesById.find(handle->getId()) != oldHandlesById.end()) {
+ const sp<InputWindowHandle>& oldHandle = oldHandlesById.at(handle->getId());
oldHandle->updateFrom(handle);
newHandles.push_back(oldHandle);
} else {
@@ -3370,7 +3382,7 @@
sp<InputWindowHandle> oldFocusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
- if (oldFocusedWindowHandle != newFocusedWindowHandle) {
+ if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
if (oldFocusedWindowHandle != nullptr) {
if (DEBUG_FOCUS) {
ALOGD("Focus left window: %s in display %" PRId32,
@@ -3781,12 +3793,10 @@
dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
"portalToDisplayId=%d, paused=%s, hasFocus=%s, "
- "hasWallpaper=%s, "
- "visible=%s, canReceiveKeys=%s, flags=0x%08x, "
- "type=0x%08x, layer=%d, "
+ "hasWallpaper=%s, visible=%s, canReceiveKeys=%s, "
+ "flags=0x%08x, type=0x%08x, "
"frame=[%d,%d][%d,%d], globalScale=%f, "
- "windowScale=(%f,%f), "
- "touchableRegion=",
+ "windowScale=(%f,%f), touchableRegion=",
i, windowInfo->name.c_str(), windowInfo->displayId,
windowInfo->portalToDisplayId,
toString(windowInfo->paused),
@@ -3795,11 +3805,10 @@
toString(windowInfo->visible),
toString(windowInfo->canReceiveKeys),
windowInfo->layoutParamsFlags,
- windowInfo->layoutParamsType, windowInfo->layer,
- windowInfo->frameLeft, windowInfo->frameTop,
- windowInfo->frameRight, windowInfo->frameBottom,
- windowInfo->globalScaleFactor, windowInfo->windowXScale,
- windowInfo->windowYScale);
+ windowInfo->layoutParamsType, windowInfo->frameLeft,
+ windowInfo->frameTop, windowInfo->frameRight,
+ windowInfo->frameBottom, windowInfo->globalScaleFactor,
+ windowInfo->windowXScale, windowInfo->windowYScale);
dumpRegion(dump, windowInfo->touchableRegion);
dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 96a09e3..f9eca01 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -168,9 +168,6 @@
bool isAppSwitchPendingLocked() REQUIRES(mLock);
void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock);
- // Stale event latency optimization.
- static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry);
-
// Blocked event latency optimization. Drops old events when the user intends
// to transfer focus to a new application.
EventEntry* mNextUnblockedEvent GUARDED_BY(mLock);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index b7f7ac5..5ffc89d 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -21,6 +21,7 @@
#include <gtest/gtest.h>
#include <linux/input.h>
+#include <vector>
namespace android::inputdispatcher {
@@ -37,6 +38,10 @@
static const int32_t INJECTOR_PID = 999;
static const int32_t INJECTOR_UID = 1001;
+struct PointF {
+ float x;
+ float y;
+};
// --- FakeInputDispatcherPolicy ---
@@ -411,6 +416,11 @@
class FakeInputReceiver {
public:
+ explicit FakeInputReceiver(const sp<InputChannel>& clientChannel, const std::string name)
+ : mName(name) {
+ mConsumer = std::make_unique<InputConsumer>(clientChannel);
+ }
+
InputEvent* consume() {
uint32_t consumeSeq;
InputEvent* event;
@@ -440,7 +450,7 @@
return nullptr;
}
- status = mConsumer->sendFinishedSignal(consumeSeq, handled());
+ status = mConsumer->sendFinishedSignal(consumeSeq, true);
if (status != OK) {
ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
}
@@ -478,6 +488,89 @@
}
}
+ void assertNoEvents() {
+ InputEvent* event = consume();
+ ASSERT_EQ(nullptr, event)
+ << mName.c_str()
+ << ": should not have received any events, so consume() should return NULL";
+ }
+
+ sp<IBinder> getToken() { return mConsumer->getChannel()->getConnectionToken(); }
+
+protected:
+ std::unique_ptr<InputConsumer> mConsumer;
+ PreallocatedInputEventFactory mEventFactory;
+
+ std::string mName;
+};
+
+class FakeWindowHandle : public InputWindowHandle {
+public:
+ static const int32_t WIDTH = 600;
+ static const int32_t HEIGHT = 800;
+ const std::string mName;
+
+ FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<InputDispatcher>& dispatcher, const std::string name,
+ int32_t displayId, sp<IBinder> token = nullptr)
+ : mName(name) {
+ if (token == nullptr) {
+ sp<InputChannel> serverChannel, clientChannel;
+ InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
+ mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
+ dispatcher->registerInputChannel(serverChannel);
+ token = serverChannel->getConnectionToken();
+ }
+
+ inputApplicationHandle->updateInfo();
+ mInfo.applicationInfo = *inputApplicationHandle->getInfo();
+
+ mInfo.token = token;
+ mInfo.id = 0;
+ mInfo.name = name;
+ mInfo.layoutParamsFlags = 0;
+ mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+ mInfo.frameLeft = 0;
+ mInfo.frameTop = 0;
+ mInfo.frameRight = WIDTH;
+ mInfo.frameBottom = HEIGHT;
+ mInfo.globalScaleFactor = 1.0;
+ mInfo.touchableRegion.clear();
+ mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
+ mInfo.visible = true;
+ mInfo.canReceiveKeys = true;
+ mInfo.hasFocus = false;
+ mInfo.hasWallpaper = false;
+ mInfo.paused = false;
+ mInfo.ownerPid = INJECTOR_PID;
+ mInfo.ownerUid = INJECTOR_UID;
+ mInfo.inputFeatures = 0;
+ mInfo.displayId = displayId;
+ }
+
+ virtual bool updateInfo() { return true; }
+
+ void setFocus() { mInfo.hasFocus = true; }
+
+ void setFrame(const Rect& frame) {
+ mInfo.frameLeft = frame.left;
+ mInfo.frameTop = frame.top;
+ mInfo.frameRight = frame.right;
+ mInfo.frameBottom = frame.bottom;
+ mInfo.touchableRegion.clear();
+ mInfo.addTouchableRegion(frame);
+ }
+
+ void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; }
+
+ void setId(int32_t id) { mInfo.id = id; }
+
+ void setWindowScale(float xScale, float yScale) {
+ mInfo.windowXScale = xScale;
+ mInfo.windowYScale = yScale;
+ }
+
void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
expectedFlags);
@@ -493,102 +586,30 @@
expectedFlags);
}
+ void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
+ int32_t expectedFlags) {
+ ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver";
+ mInputReceiver->consumeEvent(expectedEventType, expectedAction, expectedDisplayId,
+ expectedFlags);
+ }
+
+ InputEvent* consume() {
+ if (mInputReceiver == nullptr) {
+ return nullptr;
+ }
+ return mInputReceiver->consume();
+ }
+
void assertNoEvents() {
- InputEvent* event = consume();
- ASSERT_EQ(nullptr, event)
- << mName.c_str()
- << ": should not have received any events, so consume() should return NULL";
+ ASSERT_NE(mInputReceiver, nullptr)
+ << "Call 'assertNoEvents' on a window with an InputReceiver";
+ mInputReceiver->assertNoEvents();
}
-protected:
- explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher,
- const std::string name, int32_t displayId) :
- mDispatcher(dispatcher), mName(name), mDisplayId(displayId) {
- InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
- mConsumer = std::make_unique<InputConsumer>(mClientChannel);
- }
+ sp<IBinder> getToken() { return mInfo.token; }
- virtual ~FakeInputReceiver() {
- }
-
- // return true if the event has been handled.
- virtual bool handled() {
- return false;
- }
-
- sp<InputDispatcher> mDispatcher;
- sp<InputChannel> mServerChannel, mClientChannel;
- std::unique_ptr<InputConsumer> mConsumer;
- PreallocatedInputEventFactory mEventFactory;
-
- std::string mName;
- int32_t mDisplayId;
-};
-
-class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver {
-public:
- static const int32_t WIDTH = 600;
- static const int32_t HEIGHT = 800;
-
- FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId) :
- FakeInputReceiver(dispatcher, name, displayId),
- mFocused(false), mFrame(Rect(0, 0, WIDTH, HEIGHT)), mLayoutParamFlags(0) {
- mDispatcher->registerInputChannel(mServerChannel);
-
- inputApplicationHandle->updateInfo();
- mInfo.applicationInfo = *inputApplicationHandle->getInfo();
- }
-
- virtual bool updateInfo() {
- mInfo.token = mServerChannel ? mServerChannel->getConnectionToken() : nullptr;
- mInfo.name = mName;
- mInfo.layoutParamsFlags = mLayoutParamFlags;
- mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
- mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
- mInfo.frameLeft = mFrame.left;
- mInfo.frameTop = mFrame.top;
- mInfo.frameRight = mFrame.right;
- mInfo.frameBottom = mFrame.bottom;
- mInfo.globalScaleFactor = 1.0;
- mInfo.touchableRegion.clear();
- mInfo.addTouchableRegion(mFrame);
- mInfo.visible = true;
- mInfo.canReceiveKeys = true;
- mInfo.hasFocus = mFocused;
- mInfo.hasWallpaper = false;
- mInfo.paused = false;
- mInfo.layer = 0;
- mInfo.ownerPid = INJECTOR_PID;
- mInfo.ownerUid = INJECTOR_UID;
- mInfo.inputFeatures = 0;
- mInfo.displayId = mDisplayId;
-
- return true;
- }
-
- void setFocus() {
- mFocused = true;
- }
-
- void setFrame(const Rect& frame) {
- mFrame.set(frame);
- }
-
- void setLayoutParamFlags(int32_t flags) {
- mLayoutParamFlags = flags;
- }
-
- void releaseChannel() {
- mServerChannel.clear();
- InputWindowHandle::releaseChannel();
- }
-protected:
- virtual bool handled() override { return true; }
-
- bool mFocused;
- Rect mFrame;
- int32_t mLayoutParamFlags;
+private:
+ std::unique_ptr<FakeInputReceiver> mInputReceiver;
};
static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
@@ -659,31 +680,43 @@
return args;
}
-static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) {
- PointerProperties pointerProperties[1];
- PointerCoords pointerCoords[1];
+static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId,
+ const std::vector<PointF>& points) {
+ size_t pointerCount = points.size();
+ if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP) {
+ EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer";
+ }
- pointerProperties[0].clear();
- pointerProperties[0].id = 0;
- pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ PointerProperties pointerProperties[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
- pointerCoords[0].clear();
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
+ for (size_t i = 0; i < pointerCount; i++) {
+ pointerProperties[i].clear();
+ pointerProperties[i].id = i;
+ pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ pointerCoords[i].clear();
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, points[i].x);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, points[i].y);
+ }
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid motion event.
NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, source, displayId,
POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0,
AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, pointerProperties, pointerCoords,
- /* xPrecision */ 0, /* yPrecision */ 0,
+ AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
+ pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {});
return args;
}
+static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) {
+ return generateMotionArgs(action, source, displayId, {PointF{100, 200}});
+}
+
TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
@@ -857,15 +890,37 @@
0 /*expectedFlags*/);
}
-class FakeMonitorReceiver : public FakeInputReceiver, public RefBase {
+class FakeMonitorReceiver {
public:
FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
- int32_t displayId, bool isGestureMonitor = false)
- : FakeInputReceiver(dispatcher, name, displayId) {
- mDispatcher->registerInputMonitor(mServerChannel, displayId, isGestureMonitor);
+ int32_t displayId, bool isGestureMonitor = false) {
+ sp<InputChannel> serverChannel, clientChannel;
+ InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
+ mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
+ dispatcher->registerInputMonitor(serverChannel, displayId, isGestureMonitor);
}
- sp<IBinder> getToken() { return mServerChannel->getConnectionToken(); }
+ sp<IBinder> getToken() { return mInputReceiver->getToken(); }
+
+ void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN,
+ expectedDisplayId, expectedFlags);
+ }
+
+ void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN,
+ expectedDisplayId, expectedFlags);
+ }
+
+ void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP,
+ expectedDisplayId, expectedFlags);
+ }
+
+ void assertNoEvents() { mInputReceiver->assertNoEvents(); }
+
+private:
+ std::unique_ptr<FakeInputReceiver> mInputReceiver;
};
// Tests for gesture monitors
@@ -875,15 +930,14 @@
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
- sp<FakeMonitorReceiver> monitor =
- new FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
- true /*isGestureMonitor*/);
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- monitor->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
}
TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) {
@@ -896,14 +950,13 @@
mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
- sp<FakeMonitorReceiver> monitor =
- new FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
- true /*isGestureMonitor*/);
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
- monitor->assertNoEvents();
+ monitor.assertNoEvents();
}
TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStream) {
@@ -912,24 +965,23 @@
new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
- sp<FakeMonitorReceiver> monitor =
- new FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
- true /*isGestureMonitor*/);
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- monitor->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
window->releaseChannel();
- mDispatcher->pilferPointers(monitor->getToken());
+ mDispatcher->pilferPointers(monitor.getToken());
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- monitor->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
}
TEST_F(InputDispatcherTest, TestMoveEvent) {
@@ -1047,28 +1099,28 @@
// Test per-display input monitors for motion event.
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) {
- sp<FakeMonitorReceiver> monitorInPrimary =
- new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
- sp<FakeMonitorReceiver> monitorInSecondary =
- new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+ FakeMonitorReceiver monitorInPrimary =
+ FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitorInSecondary =
+ FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test touch down on primary display.
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- monitorInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
- monitorInSecondary->assertNoEvents();
+ monitorInSecondary.assertNoEvents();
// Test touch down on second display.
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
windowInPrimary->assertNoEvents();
- monitorInPrimary->assertNoEvents();
+ monitorInPrimary.assertNoEvents();
windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
- monitorInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
+ monitorInSecondary.consumeMotionDown(SECOND_DISPLAY_ID);
// Test inject a non-pointer motion event.
// If specific a display, it will dispatch to the focused window of particular display,
@@ -1077,26 +1129,26 @@
AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
windowInPrimary->assertNoEvents();
- monitorInPrimary->assertNoEvents();
+ monitorInPrimary.assertNoEvents();
windowInSecondary->consumeMotionDown(ADISPLAY_ID_NONE);
- monitorInSecondary->consumeMotionDown(ADISPLAY_ID_NONE);
+ monitorInSecondary.consumeMotionDown(ADISPLAY_ID_NONE);
}
// Test per-display input monitors for key event.
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) {
//Input monitor per display.
- sp<FakeMonitorReceiver> monitorInPrimary =
- new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
- sp<FakeMonitorReceiver> monitorInSecondary =
- new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+ FakeMonitorReceiver monitorInPrimary =
+ FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitorInSecondary =
+ FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test inject a key down.
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
windowInPrimary->assertNoEvents();
- monitorInPrimary->assertNoEvents();
+ monitorInPrimary.assertNoEvents();
windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
- monitorInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
+ monitorInSecondary.consumeKeyDown(ADISPLAY_ID_NONE);
}
class InputFilterTest : public InputDispatcherTest {
@@ -1259,4 +1311,132 @@
mFakePolicy->assertOnPointerDownWasNotCalled();
}
+// These tests ensures we can send touch events to a single client when there are multiple input
+// windows that point to the same client token.
+class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest {
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ mWindow1 = new FakeWindowHandle(application, mDispatcher, "Fake Window 1",
+ ADISPLAY_ID_DEFAULT);
+ // Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window.
+ // We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows.
+ mWindow1->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
+ InputWindowInfo::FLAG_SPLIT_TOUCH);
+ mWindow1->setId(0);
+ mWindow1->setFrame(Rect(0, 0, 100, 100));
+
+ mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2",
+ ADISPLAY_ID_DEFAULT, mWindow1->getToken());
+ mWindow2->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
+ InputWindowInfo::FLAG_SPLIT_TOUCH);
+ mWindow2->setId(1);
+ mWindow2->setFrame(Rect(100, 100, 200, 200));
+
+ mDispatcher->setInputWindows({mWindow1, mWindow2}, ADISPLAY_ID_DEFAULT);
+ }
+
+protected:
+ sp<FakeWindowHandle> mWindow1;
+ sp<FakeWindowHandle> mWindow2;
+
+ // Helper function to convert the point from screen coordinates into the window's space
+ static PointF getPointInWindow(const InputWindowInfo* windowInfo, const PointF& point) {
+ float x = windowInfo->windowXScale * (point.x - windowInfo->frameLeft);
+ float y = windowInfo->windowYScale * (point.y - windowInfo->frameTop);
+ return {x, y};
+ }
+
+ void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction,
+ const std::vector<PointF>& points) {
+ std::string name = window->mName;
+ InputEvent* event = window->consume();
+
+ ASSERT_NE(nullptr, event) << name.c_str()
+ << ": consumer should have returned non-NULL event.";
+
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
+ << name.c_str() << "expected " << inputEventTypeToString(AINPUT_EVENT_TYPE_MOTION)
+ << " event, got " << inputEventTypeToString(event->getType()) << " event";
+
+ const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+ EXPECT_EQ(expectedAction, motionEvent.getAction());
+
+ for (size_t i = 0; i < points.size(); i++) {
+ float expectedX = points[i].x;
+ float expectedY = points[i].y;
+
+ EXPECT_EQ(expectedX, motionEvent.getX(i))
+ << "expected " << expectedX << " for x[" << i << "] coord of " << name.c_str()
+ << ", got " << motionEvent.getX(i);
+ EXPECT_EQ(expectedY, motionEvent.getY(i))
+ << "expected " << expectedY << " for y[" << i << "] coord of " << name.c_str()
+ << ", got " << motionEvent.getY(i);
+ }
+ }
+};
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchSameScale) {
+ // Touch Window 1
+ PointF touchedPoint = {10, 10};
+ PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchedPoint});
+ mDispatcher->notifyMotion(&motionArgs);
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+
+ // Release touch on Window 1
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchedPoint});
+ mDispatcher->notifyMotion(&motionArgs);
+ // consume the UP event
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint});
+
+ // Touch Window 2
+ touchedPoint = {150, 150};
+ expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchedPoint});
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Consuming from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) {
+ mWindow2->setWindowScale(0.5f, 0.5f);
+
+ // Touch Window 1
+ PointF touchedPoint = {10, 10};
+ PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchedPoint});
+ mDispatcher->notifyMotion(&motionArgs);
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+
+ // Release touch on Window 1
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchedPoint});
+ mDispatcher->notifyMotion(&motionArgs);
+ // consume the UP event
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint});
+
+ // Touch Window 2
+ touchedPoint = {150, 150};
+ expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchedPoint});
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Consuming from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+}
+
} // namespace android::inputdispatcher
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 054acc5..bdecdb7 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -770,17 +770,20 @@
// After buffer info is updated, the drawingState from the real layer needs to be copied into
// the cloned. This is because some properties of drawingState can change when latchBuffer is
- // called. However, copying the drawingState would also overwrite the cloned layer's relatives.
- // Therefore, temporarily store the relatives so they can be set in the cloned drawingState
- // again.
+ // called. However, copying the drawingState would also overwrite the cloned layer's relatives
+ // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in
+ // the cloned drawingState again.
wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf;
SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives;
+ wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop;
+ InputWindowInfo tmpInputInfo = mDrawingState.inputInfo;
+
mDrawingState = clonedFrom->mDrawingState;
- // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple
- // InputWindows per client token yet.
- mDrawingState.inputInfo.token = nullptr;
+
+ mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop;
mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf;
mDrawingState.zOrderRelatives = tmpZOrderRelatives;
+ mDrawingState.inputInfo = tmpInputInfo;
}
} // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 945abd7..e4e4bc7 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -455,6 +455,10 @@
mLastFrameNumberReceived = item.mFrameNumber;
mQueueItemCondition.broadcast();
}
+
+ const int32_t layerId = getSequence();
+ mFlinger->mFrameTracer->traceTimestamp(layerId, item.mGraphicBuffer->getId(), item.mFrameNumber,
+ systemTime(), FrameTracer::FrameEvent::QUEUE);
mConsumer->onBufferAvailable(item);
}
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 2ba781d..a64fdbf 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -79,6 +79,9 @@
// The bounds of the layer in layer local coordinates
FloatRect geomLayerBounds;
+ // length of the shadow in screen space
+ float shadowRadius;
+
/*
* Geometry state
*/
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 11cfccc..00baa89 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -52,6 +52,9 @@
// The visibleRegion transformed to output space
Region outputSpaceVisibleRegion;
+ // Region cast by the layer's shadow
+ Region shadowRegion;
+
// If true, client composition will be used on this output
bool forceClientComposition{false};
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 1ca03dc..e740529 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -51,6 +51,9 @@
out.append(" ");
dumpVal(out, "geomLayerBounds", geomLayerBounds);
+ out.append(" ");
+ dumpVal(out, "shadowRadius", shadowRadius);
+
out.append("\n ");
dumpVal(out, "blend", toString(blendMode), blendMode);
dumpVal(out, "alpha", alpha);
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 7e5a720..28d2653 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -408,6 +408,11 @@
*/
Region transparentRegion;
+ /*
+ * shadowRegion: Region cast by the layer's shadow.
+ */
+ Region shadowRegion;
+
// handle hidden surfaces by setting the visible region to empty
if (CC_UNLIKELY(!layerFEState.isVisible)) {
return;
@@ -418,7 +423,18 @@
// Get the visible region
// TODO(b/121291683): Is it worth creating helper methods on LayerFEState
// for computations like this?
- visibleRegion.set(Rect(tr.transform(layerFEState.geomLayerBounds)));
+ const Rect visibleRect(tr.transform(layerFEState.geomLayerBounds));
+ visibleRegion.set(visibleRect);
+
+ if (layerFEState.shadowRadius > 0.0f) {
+ // if the layer casts a shadow, offset the layers visible region and
+ // calculate the shadow region.
+ const int32_t inset = layerFEState.shadowRadius * -1.0f;
+ Rect visibleRectWithShadows(visibleRect);
+ visibleRectWithShadows.inset(inset, inset, inset, inset);
+ visibleRegion.set(visibleRectWithShadows);
+ shadowRegion = visibleRegion.subtract(visibleRect);
+ }
if (visibleRegion.isEmpty()) {
return;
@@ -444,7 +460,7 @@
// Otherwise we don't try and compute the opaque region since there may
// be errors at the edges, and we treat the entire layer as
// translucent.
- opaqueRegion = visibleRegion;
+ opaqueRegion.set(visibleRect);
}
// Clip the covered region to the visible region
@@ -510,7 +526,7 @@
// Compute the visible non-transparent region
Region visibleNonTransparentRegion = visibleRegion.subtract(transparentRegion);
- // Peform the final check to see if this layer is visible on this output
+ // Perform the final check to see if this layer is visible on this output
// TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below)
const auto& outputState = getState();
Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion));
@@ -519,6 +535,8 @@
return;
}
+ Region visibleNonShadowRegion = visibleRegion.subtract(shadowRegion);
+
// The layer is visible. Either reuse the existing outputLayer if we have
// one, or create a new one if we do not.
auto result = ensureOutputLayer(prevOutputLayerIndex, layer, layerFE);
@@ -529,8 +547,9 @@
outputLayerState.visibleRegion = visibleRegion;
outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
outputLayerState.coveredRegion = coveredRegion;
- outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform(
- outputLayerState.visibleRegion.intersect(outputState.viewport));
+ outputLayerState.outputSpaceVisibleRegion =
+ outputState.transform.transform(visibleNonShadowRegion.intersect(outputState.viewport));
+ outputLayerState.shadowRegion = shadowRegion;
}
void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) {
@@ -884,7 +903,7 @@
continue;
}
- bool clientComposition = layer->requiresClientComposition();
+ const bool clientComposition = layer->requiresClientComposition();
// We clear the client target for non-client composed layers if
// requested by the HWC. We skip this if the layer is not an opaque
@@ -921,8 +940,15 @@
}
}
- layer->editState().clientCompositionTimestamp = systemTime();
- clientCompositionLayers.push_back(*result);
+ // If the layer casts a shadow but the content casting the shadow is occluded, skip
+ // composing the non-shadow content and only draw the shadows.
+ const bool skipNonShadowContentComposition = clientComposition &&
+ layerState.visibleRegion.subtract(layerState.shadowRegion).isEmpty();
+
+ if (!skipNonShadowContentComposition) {
+ layer->editState().clientCompositionTimestamp = systemTime();
+ clientCompositionLayers.push_back(*result);
+ }
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index ad668b6..cc3c54c 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -51,6 +51,9 @@
dumpVal(out, "output visibleRegion", outputSpaceVisibleRegion);
out.append(" ");
+ dumpVal(out, "shadowRegion", shadowRegion);
+
+ out.append(" ");
dumpVal(out, "forceClientComposition", forceClientComposition);
dumpVal(out, "clearClientTarget", clearClientTarget);
dumpVal(out, "displayFrame", displayFrame);
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 364661b..9971791 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -86,6 +86,9 @@
MOCK_METHOD4(setActiveConfigWithConstraints,
status_t(DisplayId, size_t, const HWC2::VsyncPeriodChangeConstraints&,
HWC2::VsyncPeriodChangeTimeline*));
+ MOCK_METHOD2(setAutoLowLatencyMode, status_t(DisplayId, bool));
+ MOCK_METHOD2(getSupportedContentTypes, status_t(DisplayId, std::vector<HWC2::ContentType>*));
+ MOCK_METHOD2(setContentType, status_t(DisplayId, HWC2::ContentType));
MOCK_CONST_METHOD1(dump, void(std::string&));
MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 87beb0d..cb6c50c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1439,6 +1439,87 @@
EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kExpectedLayerVisibleRegion));
}
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, coverageAccumulatesWithShadowsTest) {
+ ui::Transform translate;
+ translate.set(50, 50);
+ mLayerFEState.geomLayerTransform = translate;
+ mLayerFEState.shadowRadius = 10.0f;
+
+ mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+ // half of the layer including the casting shadow is covered and opaque
+ mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 100, 260));
+ mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 100, 260));
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ const Region kExpectedDirtyRegion = Region(Rect(0, 0, 500, 500));
+ const Region kExpectedAboveCoveredRegion = Region(Rect(40, 40, 160, 260));
+ // add starting opaque region to the opaque half of the casting layer bounds
+ const Region kExpectedAboveOpaqueRegion =
+ Region(Rect(40, 40, 100, 260)).orSelf(Rect(100, 50, 150, 250));
+ const Region kExpectedLayerVisibleRegion = Region(Rect(100, 40, 160, 260));
+ const Region kExpectedoutputSpaceLayerVisibleRegion = Region(Rect(100, 50, 150, 250));
+ const Region kExpectedLayerCoveredRegion = Region(Rect(40, 40, 100, 260));
+ const Region kExpectedLayerVisibleNonTransparentRegion = Region(Rect(100, 40, 160, 260));
+ const Region kExpectedLayerShadowRegion =
+ Region(Rect(40, 40, 160, 260)).subtractSelf(Rect(50, 50, 150, 250));
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kExpectedDirtyRegion));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kExpectedAboveCoveredRegion));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kExpectedAboveOpaqueRegion));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion,
+ RegionEq(kExpectedLayerVisibleNonTransparentRegion));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kExpectedLayerCoveredRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion,
+ RegionEq(kExpectedoutputSpaceLayerVisibleRegion));
+ EXPECT_THAT(mOutputLayerState.shadowRegion, RegionEq(kExpectedLayerShadowRegion));
+ EXPECT_FALSE(kExpectedLayerVisibleRegion.subtract(kExpectedLayerShadowRegion).isEmpty());
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, shadowRegionOnlyTest) {
+ ui::Transform translate;
+ translate.set(50, 50);
+ mLayerFEState.geomLayerTransform = translate;
+ mLayerFEState.shadowRadius = 10.0f;
+
+ mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+ // Casting layer is covered by an opaque region leaving only part of its shadow to be drawn
+ mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 150, 260));
+ mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 150, 260));
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ const Region kExpectedLayerVisibleRegion = Region(Rect(150, 40, 160, 260));
+ const Region kExpectedLayerShadowRegion =
+ Region(Rect(40, 40, 160, 260)).subtractSelf(Rect(50, 50, 150, 250));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+ EXPECT_THAT(mOutputLayerState.shadowRegion, RegionEq(kExpectedLayerShadowRegion));
+ EXPECT_TRUE(kExpectedLayerVisibleRegion.subtract(kExpectedLayerShadowRegion).isEmpty());
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifLayerWithShadowIsCovered) {
+ ui::Transform translate;
+ translate.set(50, 50);
+ mLayerFEState.geomLayerTransform = translate;
+ mLayerFEState.shadowRadius = 10.0f;
+
+ mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+ // Casting layer and its shadows are covered by an opaque region
+ mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 160, 260));
+ mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 160, 260));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
/*
* Output::present()
*/
@@ -3635,5 +3716,67 @@
EXPECT_EQ(rightLayer.mRELayerSettings, requests[1]);
}
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ shadowRegionOnlyVisibleSkipsContentComposition) {
+ const Rect kContentWithShadow(40, 40, 70, 90);
+ const Rect kContent(50, 50, 60, 80);
+ const Region kShadowRegion = Region(kContentWithShadow).subtract(kContent);
+ const Region kPartialShadowRegion = Region(kContentWithShadow).subtract(Rect(40, 40, 60, 80));
+
+ renderengine::LayerSettings mREShadowSettings;
+ mREShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+
+ mLayers[2].mOutputLayerState.visibleRegion = kPartialShadowRegion;
+ mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion;
+
+ EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(_))
+ .WillOnce(Return(mLayers[2].mRELayerSettings));
+ EXPECT_CALL(mLayers[2].mLayerFE,
+ prepareShadowClientComposition(mLayers[2].mRELayerSettings, kDisplayViewport,
+ kDisplayDataspace))
+ .WillOnce(Return(mREShadowSettings));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
+ ASSERT_EQ(1u, requests.size());
+
+ EXPECT_EQ(mREShadowSettings, requests[0]);
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ shadowRegionWithContentVisibleRequestsContentAndShadowComposition) {
+ const Rect kContentWithShadow(40, 40, 70, 90);
+ const Rect kContent(50, 50, 60, 80);
+ const Region kShadowRegion = Region(kContentWithShadow).subtract(kContent);
+ const Region kPartialContentWithPartialShadowRegion =
+ Region(kContentWithShadow).subtract(Rect(40, 40, 50, 80));
+
+ renderengine::LayerSettings mREShadowSettings;
+ mREShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+
+ mLayers[2].mOutputLayerState.visibleRegion = kPartialContentWithPartialShadowRegion;
+ mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion;
+
+ EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(_))
+ .WillOnce(Return(mLayers[2].mRELayerSettings));
+ EXPECT_CALL(mLayers[2].mLayerFE,
+ prepareShadowClientComposition(mLayers[2].mRELayerSettings, kDisplayViewport,
+ kDisplayDataspace))
+ .WillOnce(Return(mREShadowSettings));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
+ ASSERT_EQ(2u, requests.size());
+
+ EXPECT_EQ(mREShadowSettings, requests[0]);
+ EXPECT_EQ(mLayers[2].mRELayerSettings, requests[1]);
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index dc71128..eb032f3 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -1281,6 +1281,45 @@
return error;
}
+V2_4::Error Composer::setAutoLowLatencyMode(Display display, bool on) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ return mClient_2_4->setAutoLowLatencyMode(display, on);
+}
+
+V2_4::Error Composer::getSupportedContentTypes(
+ Display displayId, std::vector<IComposerClient::ContentType>* outSupportedContentTypes) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ Error error = kDefaultError_2_4;
+ mClient_2_4->getSupportedContentTypes(displayId,
+ [&](const auto& tmpError,
+ const auto& tmpSupportedContentTypes) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outSupportedContentTypes = tmpSupportedContentTypes;
+ });
+ return error;
+}
+
+V2_4::Error Composer::setContentType(Display display, IComposerClient::ContentType contentType) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ return mClient_2_4->setContentType(display, contentType);
+}
+
CommandReader::~CommandReader()
{
resetData();
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 336fdd8..301f54f 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -221,6 +221,13 @@
Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) = 0;
+
+ virtual V2_4::Error setAutoLowLatencyMode(Display displayId, bool on) = 0;
+ virtual V2_4::Error getSupportedContentTypes(
+ Display displayId,
+ std::vector<IComposerClient::ContentType>* outSupportedContentTypes) = 0;
+ virtual V2_4::Error setContentType(Display displayId,
+ IComposerClient::ContentType contentType) = 0;
};
namespace impl {
@@ -442,6 +449,12 @@
Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) override;
+ V2_4::Error setAutoLowLatencyMode(Display displayId, bool on) override;
+ V2_4::Error getSupportedContentTypes(
+ Display displayId,
+ std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
+ V2_4::Error setContentType(Display displayId,
+ IComposerClient::ContentType contentType) override;
private:
#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 4f96ad3..12b0ddd 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -42,8 +42,6 @@
using android::Rect;
using android::Region;
using android::sp;
-using android::hardware::Return;
-using android::hardware::Void;
namespace HWC2 {
@@ -60,188 +58,8 @@
return keys.find(key) != keys.end();
}
-class ComposerCallbackBridge : public Hwc2::IComposerCallback {
-public:
- ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId,
- bool vsyncSwitchingSupported)
- : mCallback(callback),
- mSequenceId(sequenceId),
- mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
-
- Return<void> onHotplug(Hwc2::Display display,
- IComposerCallback::Connection conn) override
- {
- HWC2::Connection connection = static_cast<HWC2::Connection>(conn);
- mCallback->onHotplugReceived(mSequenceId, display, connection);
- return Void();
- }
-
- Return<void> onRefresh(Hwc2::Display display) override
- {
- mCallback->onRefreshReceived(mSequenceId, display);
- return Void();
- }
-
- Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
- {
- if (!mVsyncSwitchingSupported) {
- mCallback->onVsyncReceived(mSequenceId, display, timestamp, std::nullopt);
- } else {
- ALOGW("Unexpected onVsync callback on composer >= 2.4, ignoring.");
- }
- return Void();
- }
-
- Return<void> onVsync_2_4(Hwc2::Display display, int64_t timestamp,
- Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override {
- if (mVsyncSwitchingSupported) {
- // TODO(b/140201379): use vsyncPeriodNanos in the new DispSync
- mCallback->onVsyncReceived(mSequenceId, display, timestamp,
- std::make_optional(vsyncPeriodNanos));
- } else {
- ALOGW("Unexpected onVsync_2_4 callback on composer <= 2.3, ignoring.");
- }
- return Void();
- }
-
- Return<void> onVsyncPeriodTimingChanged(
- Hwc2::Display display,
- const Hwc2::VsyncPeriodChangeTimeline& updatedTimeline) override {
- hwc_vsync_period_change_timeline_t timeline;
- timeline.newVsyncAppliedTimeNanos = updatedTimeline.newVsyncAppliedTimeNanos;
- timeline.refreshRequired = updatedTimeline.refreshRequired;
- timeline.refreshTimeNanos = updatedTimeline.refreshTimeNanos;
- mCallback->onVsyncPeriodTimingChangedReceived(mSequenceId, display, timeline);
- return Void();
- }
-
-private:
- ComposerCallback* mCallback;
- int32_t mSequenceId;
- const bool mVsyncSwitchingSupported;
-};
-
} // namespace anonymous
-
-// Device methods
-
-Device::Device(std::unique_ptr<android::Hwc2::Composer> composer) : mComposer(std::move(composer)) {
- loadCapabilities();
-}
-
-void Device::registerCallback(ComposerCallback* callback, int32_t sequenceId) {
- if (mRegisteredCallback) {
- ALOGW("Callback already registered. Ignored extra registration "
- "attempt.");
- return;
- }
- mRegisteredCallback = true;
- sp<ComposerCallbackBridge> callbackBridge(
- new ComposerCallbackBridge(callback, sequenceId,
- mComposer->isVsyncPeriodSwitchSupported()));
- mComposer->registerCallback(callbackBridge);
-}
-
-// Required by HWC2 device
-
-std::string Device::dump() const
-{
- return mComposer->dumpDebugInfo();
-}
-
-uint32_t Device::getMaxVirtualDisplayCount() const
-{
- return mComposer->getMaxVirtualDisplayCount();
-}
-
-Error Device::getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
- std::vector<uint8_t>* outData) const {
- auto intError = mComposer->getDisplayIdentificationData(hwcDisplayId, outPort, outData);
- return static_cast<Error>(intError);
-}
-
-Error Device::createVirtualDisplay(uint32_t width, uint32_t height,
- PixelFormat* format, Display** outDisplay)
-{
- ALOGI("Creating virtual display");
-
- hwc2_display_t displayId = 0;
- auto intError = mComposer->createVirtualDisplay(width, height,
- format, &displayId);
- auto error = static_cast<Error>(intError);
- if (error != Error::None) {
- return error;
- }
-
- auto display = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities, displayId,
- DisplayType::Virtual);
- display->setConnected(true);
- *outDisplay = display.get();
- mDisplays.emplace(displayId, std::move(display));
- ALOGI("Created virtual display");
- return Error::None;
-}
-
-void Device::destroyDisplay(hwc2_display_t displayId)
-{
- ALOGI("Destroying display %" PRIu64, displayId);
- mDisplays.erase(displayId);
-}
-
-void Device::onHotplug(hwc2_display_t displayId, Connection connection) {
- if (connection == Connection::Connected) {
- // If we get a hotplug connected event for a display we already have,
- // destroy the display and recreate it. This will force us to requery
- // the display params and recreate all layers on that display.
- auto oldDisplay = getDisplayById(displayId);
- if (oldDisplay != nullptr && oldDisplay->isConnected()) {
- ALOGI("Hotplug connecting an already connected display."
- " Clearing old display state.");
- }
- mDisplays.erase(displayId);
-
- auto newDisplay = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities,
- displayId, DisplayType::Physical);
- newDisplay->setConnected(true);
- mDisplays.emplace(displayId, std::move(newDisplay));
- } else if (connection == Connection::Disconnected) {
- // The display will later be destroyed by a call to
- // destroyDisplay(). For now we just mark it disconnected.
- auto display = getDisplayById(displayId);
- if (display) {
- display->setConnected(false);
- } else {
- ALOGW("Attempted to disconnect unknown display %" PRIu64,
- displayId);
- }
- }
-}
-
-// Other Device methods
-
-Display* Device::getDisplayById(hwc2_display_t id) {
- auto iter = mDisplays.find(id);
- return iter == mDisplays.end() ? nullptr : iter->second.get();
-}
-
-// Device initialization methods
-
-void Device::loadCapabilities()
-{
- static_assert(sizeof(Capability) == sizeof(int32_t),
- "Capability size has changed");
- auto capabilities = mComposer->getCapabilities();
- for (auto capability : capabilities) {
- mCapabilities.emplace(static_cast<Capability>(capability));
- }
-}
-
-Error Device::flushCommands()
-{
- return static_cast<Error>(mComposer->executeCommands());
-}
-
// Display methods
Display::~Display() = default;
@@ -812,6 +630,26 @@
return static_cast<Error>(intError);
}
+Error Display::setAutoLowLatencyMode(bool on) const {
+ auto intError = mComposer.setAutoLowLatencyMode(mId, on);
+ return static_cast<Error>(intError);
+}
+
+Error Display::getSupportedContentTypes(std::vector<ContentType>* outSupportedContentTypes) const {
+ std::vector<Hwc2::IComposerClient::ContentType> tmpSupportedContentTypes;
+ auto intError = mComposer.getSupportedContentTypes(mId, &tmpSupportedContentTypes);
+ for (Hwc2::IComposerClient::ContentType contentType : tmpSupportedContentTypes) {
+ outSupportedContentTypes->push_back(static_cast<ContentType>(contentType));
+ }
+ return static_cast<Error>(intError);
+}
+
+Error Display::setContentType(ContentType contentType) const {
+ using Hwc2_ContentType = Hwc2::IComposerClient::ContentType;
+ auto intError = mComposer.setContentType(mId, static_cast<Hwc2_ContentType>(contentType));
+ return static_cast<Error>(intError);
+}
+
// For use by Device
void Display::setConnected(bool connected) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 5abebab..8b532e3 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -52,7 +52,6 @@
namespace HWC2 {
-class Display;
class Layer;
using VsyncPeriodChangeConstraints = hwc_vsync_period_change_constraints_t;
using VsyncPeriodChangeTimeline = hwc_vsync_period_change_timeline_t;
@@ -81,56 +80,6 @@
virtual ~ComposerCallback() = default;
};
-// C++ Wrapper around hwc2_device_t. Load all functions pointers
-// and handle callback registration.
-class Device
-{
-public:
- explicit Device(std::unique_ptr<android::Hwc2::Composer> composer);
-
- void registerCallback(ComposerCallback* callback, int32_t sequenceId);
-
- // Required by HWC2
-
- std::string dump() const;
-
- const std::unordered_set<Capability>& getCapabilities() const {
- return mCapabilities;
- };
-
- uint32_t getMaxVirtualDisplayCount() const;
- Error getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
- std::vector<uint8_t>* outData) const;
-
- Error createVirtualDisplay(uint32_t width, uint32_t height,
- android::ui::PixelFormat* format, Display** outDisplay);
- void destroyDisplay(hwc2_display_t displayId);
-
- void onHotplug(hwc2_display_t displayId, Connection connection);
-
- // Other Device methods
-
- Display* getDisplayById(hwc2_display_t id);
-
- android::Hwc2::Composer* getComposer() { return mComposer.get(); }
-
- // We buffer most state changes and flush them implicitly with
- // Display::validate, Display::present, and Display::presentOrValidate.
- // This method provides an explicit way to flush state changes to HWC.
- Error flushCommands();
-
-private:
- // Initialization methods
-
- void loadCapabilities();
-
- // Member variables
- std::unique_ptr<android::Hwc2::Composer> mComposer;
- std::unordered_set<Capability> mCapabilities;
- std::unordered_map<hwc2_display_t, std::unique_ptr<Display>> mDisplays;
- bool mRegisteredCallback = false;
-};
-
// Convenience C++ class to access hwc2_device_t Display functions directly.
class Display {
public:
@@ -283,6 +232,10 @@
const std::shared_ptr<const HWC2::Display::Config>& config,
const VsyncPeriodChangeConstraints& constraints,
VsyncPeriodChangeTimeline* outTimeline) = 0;
+ [[clang::warn_unused_result]] virtual Error setAutoLowLatencyMode(bool on) const = 0;
+ [[clang::warn_unused_result]] virtual Error getSupportedContentTypes(
+ std::vector<HWC2::ContentType>*) const = 0;
+ [[clang::warn_unused_result]] virtual Error setContentType(HWC2::ContentType) const = 0;
};
namespace impl {
@@ -346,7 +299,10 @@
Error setActiveConfigWithConstraints(const std::shared_ptr<const HWC2::Display::Config>& config,
const VsyncPeriodChangeConstraints& constraints,
VsyncPeriodChangeTimeline* outTimeline) override;
-
+ Error setAutoLowLatencyMode(bool on) const override;
+ Error getSupportedContentTypes(
+ std::vector<HWC2::ContentType>* outSupportedContentTypes) const override;
+ Error setContentType(HWC2::ContentType contentType) const override;
// Other Display methods
hwc2_display_t getId() const override { return mId; }
bool isConnected() const override { return mIsConnected; }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index d8dad0b..0a7009b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -65,14 +65,88 @@
#define RETURN_IF_HWC_ERROR(error, displayId, ...) \
RETURN_IF_HWC_ERROR_FOR(__FUNCTION__, error, displayId, __VA_ARGS__)
+namespace {
+
+using android::hardware::Return;
+using android::hardware::Void;
+
+class ComposerCallbackBridge : public android::Hwc2::IComposerCallback {
+public:
+ ComposerCallbackBridge(HWC2::ComposerCallback* callback, int32_t sequenceId,
+ bool vsyncSwitchingSupported)
+ : mCallback(callback),
+ mSequenceId(sequenceId),
+ mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
+
+ android::hardware::Return<void> onHotplug(
+ android::Hwc2::Display display,
+ android::Hwc2::IComposerCallback::Connection conn) override {
+ HWC2::Connection connection = static_cast<HWC2::Connection>(conn);
+ mCallback->onHotplugReceived(mSequenceId, display, connection);
+ return android::hardware::Void();
+ }
+
+ android::hardware::Return<void> onRefresh(android::Hwc2::Display display) override {
+ mCallback->onRefreshReceived(mSequenceId, display);
+ return android::hardware::Void();
+ }
+
+ android::hardware::Return<void> onVsync(android::Hwc2::Display display,
+ int64_t timestamp) override {
+ if (!mVsyncSwitchingSupported) {
+ mCallback->onVsyncReceived(mSequenceId, display, timestamp, std::nullopt);
+ } else {
+ ALOGW("Unexpected onVsync callback on composer >= 2.4, ignoring.");
+ }
+ return android::hardware::Void();
+ }
+
+ android::hardware::Return<void> onVsync_2_4(
+ android::Hwc2::Display display, int64_t timestamp,
+ android::Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override {
+ if (mVsyncSwitchingSupported) {
+ // TODO(b/140201379): use vsyncPeriodNanos in the new DispSync
+ mCallback->onVsyncReceived(mSequenceId, display, timestamp,
+ std::make_optional(vsyncPeriodNanos));
+ } else {
+ ALOGW("Unexpected onVsync_2_4 callback on composer <= 2.3, ignoring.");
+ }
+ return android::hardware::Void();
+ }
+
+ android::hardware::Return<void> onVsyncPeriodTimingChanged(
+ android::Hwc2::Display display,
+ const android::Hwc2::VsyncPeriodChangeTimeline& updatedTimeline) override {
+ hwc_vsync_period_change_timeline_t timeline;
+ timeline.newVsyncAppliedTimeNanos = updatedTimeline.newVsyncAppliedTimeNanos;
+ timeline.refreshRequired = updatedTimeline.refreshRequired;
+ timeline.refreshTimeNanos = updatedTimeline.refreshTimeNanos;
+ mCallback->onVsyncPeriodTimingChangedReceived(mSequenceId, display, timeline);
+ return android::hardware::Void();
+ }
+
+private:
+ HWC2::ComposerCallback* mCallback;
+ const int32_t mSequenceId;
+ const bool mVsyncSwitchingSupported;
+};
+
+} // namespace
+
namespace android {
HWComposer::~HWComposer() = default;
namespace impl {
-HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer)
- : mHwcDevice(std::make_unique<HWC2::Device>(std::move(composer))) {}
+HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer) : mComposer(std::move(composer)) {
+ loadCapabilities();
+}
+
+HWComposer::HWComposer(const std::string& composerServiceName)
+ : mComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) {
+ loadCapabilities();
+}
HWComposer::~HWComposer() {
mDisplayData.clear();
@@ -80,12 +154,21 @@
void HWComposer::registerCallback(HWC2::ComposerCallback* callback,
int32_t sequenceId) {
- mHwcDevice->registerCallback(callback, sequenceId);
+ if (mRegisteredCallback) {
+ ALOGW("Callback already registered. Ignored extra registration attempt.");
+ return;
+ }
+ mRegisteredCallback = true;
+ sp<ComposerCallbackBridge> callbackBridge(
+ new ComposerCallbackBridge(callback, sequenceId,
+ mComposer->isVsyncPeriodSwitchSupported()));
+ mComposer->registerCallback(callbackBridge);
}
bool HWComposer::getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
DisplayIdentificationData* outData) const {
- const auto error = mHwcDevice->getDisplayIdentificationData(hwcDisplayId, outPort, outData);
+ const auto error = static_cast<HWC2::Error>(
+ mComposer->getDisplayIdentificationData(hwcDisplayId, outPort, outData));
if (error != HWC2::Error::None) {
if (error != HWC2::Error::Unsupported) {
LOG_HWC_DISPLAY_ERROR(hwcDisplayId, to_string(error).c_str());
@@ -95,9 +178,8 @@
return true;
}
-bool HWComposer::hasCapability(HWC2::Capability capability) const
-{
- return mHwcDevice->getCapabilities().count(capability) > 0;
+bool HWComposer::hasCapability(HWC2::Capability capability) const {
+ return mCapabilities.count(capability) > 0;
}
bool HWComposer::hasDisplayCapability(const std::optional<DisplayId>& displayId,
@@ -133,13 +215,33 @@
hwcDisplayId == mInternalHwcDisplayId ? "internal" : "external",
to_string(info->id).c_str(), hwcDisplayId);
- mHwcDevice->onHotplug(hwcDisplayId, connection);
-
- // Disconnect is handled through HWComposer::disconnectDisplay via
- // SurfaceFlinger's onHotplugReceived callback handling
if (connection == HWC2::Connection::Connected) {
- mDisplayData[info->id].hwcDisplay = mHwcDevice->getDisplayById(hwcDisplayId);
+ auto& displayData = mDisplayData[info->id];
+ // If we get a hotplug connected event for a display we already have,
+ // destroy the display and recreate it. This will force us to requery
+ // the display params and recreate all layers on that display.
+ if (displayData.hwcDisplay != nullptr && displayData.hwcDisplay->isConnected()) {
+ ALOGI("Hotplug connecting an already connected display."
+ " Clearing old display state.");
+ }
+ displayData.hwcDisplay.reset();
+ auto newDisplay =
+ std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId,
+ HWC2::DisplayType::Physical);
+ newDisplay->setConnected(true);
+ displayData.hwcDisplay = std::move(newDisplay);
mPhysicalDisplayIdMap[hwcDisplayId] = info->id;
+ } else if (connection == HWC2::Connection::Disconnected) {
+ // The display will later be destroyed by a call to
+ // destroyDisplay(). For now we just mark it disconnected.
+ auto& displayData = mDisplayData[info->id];
+ if (displayData.hwcDisplay) {
+ displayData.hwcDisplay->setConnected(false);
+ } else {
+ ALOGW("Attempted to disconnect unknown display %" PRIu64, hwcDisplayId);
+ }
+ // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay
+ // via SurfaceFlinger's onHotplugReceived callback handling
}
return info;
@@ -197,14 +299,18 @@
height, SurfaceFlinger::maxVirtualDisplaySize);
return {};
}
- HWC2::Display* display;
- auto error = mHwcDevice->createVirtualDisplay(width, height, format,
- &display);
+ hwc2_display_t hwcDisplayId = 0;
+ const auto error = static_cast<HWC2::Error>(
+ mComposer->createVirtualDisplay(width, height, format, &hwcDisplayId));
if (error != HWC2::Error::None) {
ALOGE("%s: Failed to create HWC virtual display", __FUNCTION__);
return {};
}
+ auto display = std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities,
+ hwcDisplayId, HWC2::DisplayType::Virtual);
+ display->setConnected(true);
+
DisplayId displayId;
if (mFreeVirtualDisplayIds.empty()) {
displayId = getVirtualDisplayId(mNextVirtualDisplayId++);
@@ -214,7 +320,7 @@
}
auto& displayData = mDisplayData[displayId];
- displayData.hwcDisplay = display;
+ displayData.hwcDisplay = std::move(display);
displayData.isVirtual = true;
--mRemainingHwcVirtualDisplays;
@@ -224,9 +330,8 @@
HWC2::Layer* HWComposer::createLayer(DisplayId displayId) {
RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
- auto display = mDisplayData[displayId].hwcDisplay;
HWC2::Layer* layer;
- auto error = display->createLayer(&layer);
+ auto error = mDisplayData[displayId].hwcDisplay->createLayer(&layer);
RETURN_IF_HWC_ERROR(error, displayId, nullptr);
return layer;
}
@@ -234,8 +339,7 @@
void HWComposer::destroyLayer(DisplayId displayId, HWC2::Layer* layer) {
RETURN_IF_INVALID_DISPLAY(displayId);
- auto display = mDisplayData[displayId].hwcDisplay;
- auto error = display->destroyLayer(layer);
+ auto error = mDisplayData[displayId].hwcDisplay->destroyLayer(layer);
RETURN_IF_HWC_ERROR(error, displayId);
}
@@ -484,8 +588,8 @@
if (displayData.validateWasSkipped) {
// explicitly flush all pending commands
- auto error = mHwcDevice->flushCommands();
- RETURN_IF_HWC_ERROR_FOR("flushCommands", error, displayId, UNKNOWN_ERROR);
+ auto error = static_cast<HWC2::Error>(mComposer->executeCommands());
+ RETURN_IF_HWC_ERROR_FOR("executeCommands", error, displayId, UNKNOWN_ERROR);
RETURN_IF_HWC_ERROR_FOR("present", displayData.presentError, displayId, UNKNOWN_ERROR);
return NO_ERROR;
}
@@ -600,8 +704,6 @@
}
const auto hwcDisplayId = displayData.hwcDisplay->getId();
- mPhysicalDisplayIdMap.erase(hwcDisplayId);
- mDisplayData.erase(displayId);
// TODO(b/74619554): Select internal/external display from remaining displays.
if (hwcDisplayId == mInternalHwcDisplayId) {
@@ -609,8 +711,8 @@
} else if (hwcDisplayId == mExternalHwcDisplayId) {
mExternalHwcDisplayId.reset();
}
-
- mHwcDevice->destroyDisplay(hwcDisplayId);
+ mPhysicalDisplayIdMap.erase(hwcDisplayId);
+ mDisplayData.erase(displayId);
}
status_t HWComposer::setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
@@ -722,11 +824,46 @@
return getComposer()->isUsingVrComposer();
}
+status_t HWComposer::setAutoLowLatencyMode(DisplayId displayId, bool on) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on);
+ if (error == HWC2::Error::Unsupported) {
+ RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+ }
+ if (error == HWC2::Error::BadParameter) {
+ RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
+ }
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+ return NO_ERROR;
+}
+
+status_t HWComposer::getSupportedContentTypes(
+ DisplayId displayId, std::vector<HWC2::ContentType>* outSupportedContentTypes) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error =
+ mDisplayData[displayId].hwcDisplay->getSupportedContentTypes(outSupportedContentTypes);
+
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+
+ return NO_ERROR;
+}
+
+status_t HWComposer::setContentType(DisplayId displayId, HWC2::ContentType contentType) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error = mDisplayData[displayId].hwcDisplay->setContentType(contentType);
+ if (error == HWC2::Error::Unsupported) {
+ RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+ }
+ if (error == HWC2::Error::BadParameter) {
+ RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
+ }
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+
+ return NO_ERROR;
+}
+
void HWComposer::dump(std::string& result) const {
- // TODO: In order to provide a dump equivalent to HWC1, we need to shadow
- // all the state going into the layers. This is probably better done in
- // Layer itself, but it's going to take a bit of work to get there.
- result.append(mHwcDevice->dump());
+ result.append(mComposer->dumpDebugInfo());
}
std::optional<DisplayId> HWComposer::toPhysicalDisplayId(hwc2_display_t hwcDisplayId) const {
@@ -792,5 +929,17 @@
: "External display"};
}
+void HWComposer::loadCapabilities() {
+ static_assert(sizeof(HWC2::Capability) == sizeof(int32_t), "Capability size has changed");
+ auto capabilities = mComposer->getCapabilities();
+ for (auto capability : capabilities) {
+ mCapabilities.emplace(static_cast<HWC2::Capability>(capability));
+ }
+}
+
+uint32_t HWComposer::getMaxVirtualDisplayCount() const {
+ return mComposer->getMaxVirtualDisplayCount();
+}
+
} // namespace impl
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 077e452..a0dabb4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -184,6 +184,10 @@
DisplayId displayId, size_t configId,
const HWC2::VsyncPeriodChangeConstraints& constraints,
HWC2::VsyncPeriodChangeTimeline* outTimeline) = 0;
+ virtual status_t setAutoLowLatencyMode(DisplayId displayId, bool on) = 0;
+ virtual status_t getSupportedContentTypes(
+ DisplayId displayId, std::vector<HWC2::ContentType>* outSupportedContentTypes) = 0;
+ virtual status_t setContentType(DisplayId displayId, HWC2::ContentType contentType) = 0;
// for debugging ----------------------------------------------------------
virtual void dump(std::string& out) const = 0;
@@ -203,6 +207,7 @@
class HWComposer final : public android::HWComposer {
public:
explicit HWComposer(std::unique_ptr<Hwc2::Composer> composer);
+ explicit HWComposer(const std::string& composerServiceName);
~HWComposer() override;
@@ -313,11 +318,15 @@
status_t setActiveConfigWithConstraints(DisplayId displayId, size_t configId,
const HWC2::VsyncPeriodChangeConstraints& constraints,
HWC2::VsyncPeriodChangeTimeline* outTimeline) override;
+ status_t setAutoLowLatencyMode(DisplayId displayId, bool) override;
+ status_t getSupportedContentTypes(DisplayId displayId,
+ std::vector<HWC2::ContentType>*) override;
+ status_t setContentType(DisplayId displayId, HWC2::ContentType) override;
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
- Hwc2::Composer* getComposer() const override { return mHwcDevice->getComposer(); }
+ Hwc2::Composer* getComposer() const override { return mComposer.get(); }
// TODO(b/74619554): Remove special cases for internal/external display.
std::optional<hwc2_display_t> getInternalHwcDisplayId() const override {
@@ -335,11 +344,12 @@
friend TestableSurfaceFlinger;
std::optional<DisplayIdentificationInfo> onHotplugConnect(hwc2_display_t hwcDisplayId);
+ void loadCapabilities();
+ uint32_t getMaxVirtualDisplayCount() const;
struct DisplayData {
bool isVirtual = false;
-
- HWC2::Display* hwcDisplay = nullptr;
+ std::unique_ptr<HWC2::Display> hwcDisplay;
sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
buffer_handle_t outbufHandle = nullptr;
@@ -361,9 +371,9 @@
std::unordered_map<DisplayId, DisplayData> mDisplayData;
- // This must be destroyed before mDisplayData, because destructor may call back into HWComposer
- // and look up DisplayData.
- std::unique_ptr<HWC2::Device> mHwcDevice;
+ std::unique_ptr<android::Hwc2::Composer> mComposer;
+ std::unordered_set<HWC2::Capability> mCapabilities;
+ bool mRegisteredCallback = false;
std::unordered_map<hwc2_display_t, DisplayId> mPhysicalDisplayIdMap;
std::optional<hwc2_display_t> mInternalHwcDisplayId;
@@ -372,7 +382,7 @@
std::unordered_set<DisplayId> mFreeVirtualDisplayIds;
uint32_t mNextVirtualDisplayId = 0;
- uint32_t mRemainingHwcVirtualDisplays{mHwcDevice->getMaxVirtualDisplayCount()};
+ uint32_t mRemainingHwcVirtualDisplays{getMaxVirtualDisplayCount()};
};
} // namespace impl
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 35fc4be..6fd1629 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -430,6 +430,7 @@
compositionState.internalOnly = getPrimaryDisplayOnly();
compositionState.isVisible = isVisible();
compositionState.isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
+ compositionState.shadowRadius = mEffectiveShadowRadius;
compositionState.contentDirty = contentDirty;
contentDirty = false;
@@ -585,6 +586,7 @@
renderengine::LayerSettings shadowLayer = casterLayerSettings;
shadowLayer.shadow = shadow;
+ shadowLayer.geometry.boundaries = mBounds; // ignore transparent region
// If the casting layer is translucent, we need to fill in the shadow underneath the layer.
// Otherwise the generated shadow will only be shown around the casting layer.
@@ -1928,6 +1930,11 @@
[&]() { return layerInfo->mutable_visible_region(); });
LayerProtoHelper::writeToProto(surfaceDamageRegion,
[&]() { return layerInfo->mutable_damage_region(); });
+
+ if (hasColorTransform()) {
+ LayerProtoHelper::writeToProto(getColorTransform(),
+ layerInfo->mutable_color_transform());
+ }
}
if (traceFlags & SurfaceTracing::TRACE_EXTRA) {
@@ -1935,6 +1942,9 @@
[&]() { return layerInfo->mutable_source_bounds(); });
LayerProtoHelper::writeToProto(mScreenBounds,
[&]() { return layerInfo->mutable_screen_bounds(); });
+ LayerProtoHelper::writeToProto(getRoundedCornerState().cropRect,
+ [&]() { return layerInfo->mutable_corner_radius_crop(); });
+ layerInfo->set_shadow_radius(mEffectiveShadowRadius);
}
}
@@ -2025,6 +2035,7 @@
InputWindowInfo Layer::fillInputInfo() {
InputWindowInfo info = mDrawingState.inputInfo;
+ info.id = sequence;
if (info.displayId == ADISPLAY_ID_NONE) {
info.displayId = getLayerStack();
@@ -2081,9 +2092,29 @@
info.touchableRegion = info.touchableRegion.intersect(Rect{cropLayer->mScreenBounds});
}
+ // If the layer is a clone, we need to crop the input region to cloned root to prevent
+ // touches from going outside the cloned area.
+ if (isClone()) {
+ sp<Layer> clonedRoot = getClonedRoot();
+ if (clonedRoot != nullptr) {
+ Rect rect(clonedRoot->mScreenBounds);
+ info.touchableRegion = info.touchableRegion.intersect(rect);
+ }
+ }
+
return info;
}
+sp<Layer> Layer::getClonedRoot() {
+ if (mClonedChild != nullptr) {
+ return this;
+ }
+ if (mDrawingParent == nullptr || mDrawingParent.promote() == nullptr) {
+ return nullptr;
+ }
+ return mDrawingParent.promote()->getClonedRoot();
+}
+
bool Layer::hasInput() const {
return mDrawingState.inputInfo.token != nullptr;
}
@@ -2119,10 +2150,6 @@
// copy drawing state from cloned layer
mDrawingState = clonedFrom->mDrawingState;
mClonedFrom = clonedFrom;
-
- // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple
- // InputWindows per client token yet.
- mDrawingState.inputInfo.token = nullptr;
}
void Layer::updateMirrorInfo() {
@@ -2157,9 +2184,6 @@
if (isClonedFromAlive()) {
sp<Layer> clonedFrom = getClonedFrom();
mDrawingState = clonedFrom->mDrawingState;
- // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple
- // InputWindows per client token yet.
- mDrawingState.inputInfo.token = nullptr;
clonedLayersMap.emplace(clonedFrom, this);
}
@@ -2198,7 +2222,24 @@
}
}
-void Layer::updateClonedRelatives(std::map<sp<Layer>, sp<Layer>> clonedLayersMap) {
+void Layer::updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
+ auto cropLayer = mDrawingState.touchableRegionCrop.promote();
+ if (cropLayer != nullptr) {
+ if (clonedLayersMap.count(cropLayer) == 0) {
+ // Real layer had a crop layer but it's not in the cloned hierarchy. Just set to
+ // self as crop layer to avoid going outside bounds.
+ mDrawingState.touchableRegionCrop = this;
+ } else {
+ const sp<Layer>& clonedCropLayer = clonedLayersMap.at(cropLayer);
+ mDrawingState.touchableRegionCrop = clonedCropLayer;
+ }
+ }
+ // Cloned layers shouldn't handle watch outside since their z order is not determined by
+ // WM or the client.
+ mDrawingState.inputInfo.layoutParamsFlags &= ~InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH;
+}
+
+void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
mDrawingState.zOrderRelativeOf = nullptr;
mDrawingState.zOrderRelatives.clear();
@@ -2206,11 +2247,11 @@
return;
}
- sp<Layer> clonedFrom = getClonedFrom();
+ const sp<Layer>& clonedFrom = getClonedFrom();
for (wp<Layer>& relativeWeak : clonedFrom->mDrawingState.zOrderRelatives) {
- sp<Layer> relative = relativeWeak.promote();
- auto clonedRelative = clonedLayersMap[relative];
- if (clonedRelative != nullptr) {
+ const sp<Layer>& relative = relativeWeak.promote();
+ if (clonedLayersMap.count(relative) > 0) {
+ auto& clonedRelative = clonedLayersMap.at(relative);
mDrawingState.zOrderRelatives.add(clonedRelative);
}
}
@@ -2220,12 +2261,14 @@
// In that case, we treat the layer as if the relativeOf has been removed. This way, it will
// still traverse the children, but the layer with the missing relativeOf will not be shown
// on screen.
- sp<Layer> relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote();
- sp<Layer> clonedRelativeOf = clonedLayersMap[relativeOf];
- if (clonedRelativeOf != nullptr) {
+ const sp<Layer>& relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote();
+ if (clonedLayersMap.count(relativeOf) > 0) {
+ const sp<Layer>& clonedRelativeOf = clonedLayersMap.at(relativeOf);
mDrawingState.zOrderRelativeOf = clonedRelativeOf;
}
+ updateClonedInputInfo(clonedLayersMap);
+
for (sp<Layer>& child : mDrawingChildren) {
child->updateClonedRelatives(clonedLayersMap);
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 843d3ae..d697a6a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -497,8 +497,9 @@
void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
void updateClonedChildren(const sp<Layer>& mirrorRoot,
std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- void updateClonedRelatives(std::map<sp<Layer>, sp<Layer>> clonedLayersMap);
+ void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
void addChildToDrawing(const sp<Layer>& layer);
+ void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
public:
/*
@@ -972,6 +973,10 @@
// Returns true if the layer can draw shadows on its border.
virtual bool canDrawShadows() const { return true; }
+
+ // Find the root of the cloned hierarchy, this means the first non cloned parent.
+ // This will return null if first non cloned parent is not found.
+ sp<Layer> getClonedRoot();
};
} // namespace android
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index c94e439..b402270 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -155,5 +155,13 @@
}
}
+void LayerProtoHelper::writeToProto(const mat4 matrix, ColorTransformProto* colorTransformProto) {
+ for (int i = 0; i < mat4::ROW_SIZE; i++) {
+ for (int j = 0; j < mat4::COL_SIZE; j++) {
+ colorTransformProto->add_val(matrix[i][j]);
+ }
+ }
+}
+
} // namespace surfaceflinger
} // namespace android
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 1754a3f..502238d 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -43,6 +43,7 @@
static void writeToProto(const InputWindowInfo& inputInfo,
const wp<Layer>& touchableRegionBounds,
std::function<InputWindowInfoProto*()> getInputWindowInfoProto);
+ static void writeToProto(const mat4 matrix, ColorTransformProto* colorTransformProto);
};
} // namespace surfaceflinger
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 571c9ca..bd4b0ec 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -27,16 +27,14 @@
namespace android {
-DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset,
- nsecs_t offsetThresholdForNextVsync, bool traceVsync,
+DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
const char* name)
: mName(name),
mValue(base::StringPrintf("VSYNC-%s", name), 0),
mTraceVsync(traceVsync),
mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)),
mDispSync(dispSync),
- mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset),
- mOffsetThresholdForNextVsync(offsetThresholdForNextVsync) {}
+ mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset) {}
void DispSyncSource::setVSyncEnabled(bool enable) {
std::lock_guard lock(mVsyncMutex);
@@ -67,10 +65,6 @@
void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) {
std::lock_guard lock(mVsyncMutex);
const nsecs_t period = mDispSync->getPeriod();
- // Check if offset should be handled as negative
- if (phaseOffset >= mOffsetThresholdForNextVsync) {
- phaseOffset -= period;
- }
// Normalize phaseOffset to [-period, period)
const int numPeriods = phaseOffset / period;
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 536464e..328b8c1 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -26,8 +26,7 @@
class DispSyncSource final : public VSyncSource, private DispSync::Callback {
public:
- DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, nsecs_t offsetThresholdForNextVsync,
- bool traceVsync, const char* name);
+ DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name);
~DispSyncSource() override = default;
@@ -55,7 +54,6 @@
std::mutex mVsyncMutex;
TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex);
- const nsecs_t mOffsetThresholdForNextVsync;
bool mEnabled GUARDED_BY(mVsyncMutex) = false;
};
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index 6c502e6..fa46e6f 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -45,7 +45,6 @@
const char* getName() const override { return "inject"; }
void setVSyncEnabled(bool) override {}
void setPhaseOffset(nsecs_t) override {}
- void pauseVsyncCallback(bool) {}
private:
std::mutex mCallbackMutex;
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 12832a6..4330742 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -35,45 +35,48 @@
namespace android::scheduler {
-PhaseOffsets::~PhaseOffsets() = default;
+PhaseConfiguration::~PhaseConfiguration() = default;
namespace impl {
-PhaseOffsets::PhaseOffsets() {
- // Below defines the threshold when an offset is considered to be negative, i.e. targeting
- // for the N+2 vsync instead of N+1. This means that:
- // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync.
- // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync.
- const nsecs_t thresholdForNextVsync =
- getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
- .value_or(std::numeric_limits<nsecs_t>::max());
-
- mDefaultOffsets = getDefaultOffsets(thresholdForNextVsync);
- mHighFpsOffsets = getHighFpsOffsets(thresholdForNextVsync);
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const {
- // TODO(145561086): Once offsets are common for all refresh rates we can remove the magic
- // number for refresh rate
- if (fps > 65.0f) {
- return mHighFpsOffsets;
- } else {
- return mDefaultOffsets;
- }
-}
+PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+ : // Below defines the threshold when an offset is considered to be negative, i.e. targeting
+ // for the N+2 vsync instead of N+1. This means that:
+ // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync.
+ // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync.
+ mThresholdForNextVsync(getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+ .value_or(std::numeric_limits<nsecs_t>::max())),
+ mOffsets(initializeOffsets(refreshRateConfigs)),
+ mRefreshRateFps(refreshRateConfigs.getCurrentRefreshRate().fps) {}
void PhaseOffsets::dump(std::string& result) const {
- const auto [early, earlyGl, late, threshold] = getCurrentOffsets();
+ const auto [early, earlyGl, late] = getCurrentOffsets();
using base::StringAppendF;
StringAppendF(&result,
" app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n"
" early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n"
" GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n"
"next VSYNC threshold: %9" PRId64 " ns\n",
- late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf, threshold);
+ late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf,
+ mThresholdForNextVsync);
}
-PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t thresholdForNextVsync) {
+std::unordered_map<float, PhaseDurations::Offsets> PhaseOffsets::initializeOffsets(
+ const scheduler::RefreshRateConfigs& refreshRateConfigs) const {
+ std::unordered_map<float, PhaseDurations::Offsets> offsets;
+
+ for (const auto& [ignored, refreshRate] : refreshRateConfigs.getAllRefreshRates()) {
+ const nsecs_t vsyncDuration = static_cast<nsecs_t>(1e9f / refreshRate.fps);
+ if (refreshRate.fps > 65.0f) {
+ offsets.emplace(refreshRate.fps, getHighFpsOffsets(vsyncDuration));
+ } else {
+ offsets.emplace(refreshRate.fps, getDefaultOffsets(vsyncDuration));
+ }
+ }
+ return offsets;
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
const int64_t vsyncPhaseOffsetNs = sysprop::vsync_event_phase_offset_ns(1000000);
const int64_t sfVsyncPhaseOffsetNs = sysprop::vsync_sf_event_phase_offset_ns(1000000);
@@ -82,19 +85,32 @@
const auto earlyAppOffsetNs = getProperty("debug.sf.early_app_phase_offset_ns");
const auto earlyGlAppOffsetNs = getProperty("debug.sf.early_gl_app_phase_offset_ns");
- return {{earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
- earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs)},
+ return {
+ {
+ earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs) < mThresholdForNextVsync
+ ? earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs)
+ : earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs) - vsyncDuration,
- {earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
- earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs)},
+ earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs),
+ },
+ {
+ earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs) < mThresholdForNextVsync
+ ? earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs)
+ : earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs) - vsyncDuration,
- {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs},
+ earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs),
+ },
+ {
+ sfVsyncPhaseOffsetNs < mThresholdForNextVsync
+ ? sfVsyncPhaseOffsetNs
+ : sfVsyncPhaseOffsetNs - vsyncDuration,
- thresholdForNextVsync};
+ vsyncPhaseOffsetNs,
+ },
+ };
}
-PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t thresholdForNextVsync) {
- // TODO(b/122905996): Define these in device.mk.
+PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
const int highFpsLateAppOffsetNs =
getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000);
const int highFpsLateSfOffsetNs =
@@ -106,15 +122,195 @@
const auto highFpsEarlyGlAppOffsetNs =
getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
- return {{highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs),
- highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs)},
+ return {
+ {
+ highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) < mThresholdForNextVsync
+ ? highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs)
+ : highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) -
+ vsyncDuration,
- {highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs),
- highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs)},
+ highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs),
+ },
+ {
+ highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) <
+ mThresholdForNextVsync
+ ? highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs)
+ : highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) -
+ vsyncDuration,
- {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs},
+ highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs),
+ },
+ {
+ highFpsLateSfOffsetNs < mThresholdForNextVsync
+ ? highFpsLateSfOffsetNs
+ : highFpsLateSfOffsetNs - vsyncDuration,
- thresholdForNextVsync};
+ highFpsLateAppOffsetNs,
+ },
+ };
+}
+
+static void validateSysprops() {
+ const auto validatePropertyBool = [](const char* prop) {
+ LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop);
+ };
+
+ validatePropertyBool("debug.sf.use_phase_offsets_as_durations");
+
+ LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1,
+ "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting "
+ "duration");
+
+ LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1,
+ "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting "
+ "duration");
+
+ const auto validateProperty = [](const char* prop) {
+ LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(),
+ "%s is set to %" PRId64 " but expecting duration", prop,
+ getProperty(prop).value_or(-1));
+ };
+
+ validateProperty("debug.sf.early_phase_offset_ns");
+ validateProperty("debug.sf.early_gl_phase_offset_ns");
+ validateProperty("debug.sf.early_app_phase_offset_ns");
+ validateProperty("debug.sf.early_gl_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_late_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
+}
+
+static nsecs_t sfDurationToOffset(nsecs_t sfDuration, nsecs_t vsyncDuration) {
+ return sfDuration == -1 ? 1'000'000 : vsyncDuration - sfDuration % vsyncDuration;
+}
+
+static nsecs_t appDurationToOffset(nsecs_t appDuration, nsecs_t sfDuration, nsecs_t vsyncDuration) {
+ return sfDuration == -1 ? 1'000'000
+ : vsyncDuration - (appDuration + sfDuration) % vsyncDuration;
+}
+
+static std::vector<float> getRefreshRatesFromConfigs(
+ const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
+ const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
+ std::vector<float> refreshRates;
+ refreshRates.reserve(allRefreshRates.size());
+
+ for (const auto& [ignored, refreshRate] : allRefreshRates) {
+ refreshRates.emplace_back(refreshRate.fps);
+ }
+
+ return refreshRates;
+}
+
+std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
+ const std::vector<float>& refreshRates) const {
+ std::unordered_map<float, PhaseDurations::Offsets> offsets;
+
+ for (const auto fps : refreshRates) {
+ const nsecs_t vsyncDuration = static_cast<nsecs_t>(1e9f / fps);
+ offsets.emplace(fps,
+ Offsets{
+ {
+ mSfEarlyDuration < vsyncDuration
+ ? sfDurationToOffset(mSfEarlyDuration,
+ vsyncDuration)
+ : sfDurationToOffset(mSfEarlyDuration,
+ vsyncDuration) -
+ vsyncDuration,
+
+ appDurationToOffset(mAppEarlyDuration, mSfEarlyDuration,
+ vsyncDuration),
+ },
+ {
+ mSfEarlyGlDuration < vsyncDuration
+ ? sfDurationToOffset(mSfEarlyGlDuration,
+ vsyncDuration)
+ : sfDurationToOffset(mSfEarlyGlDuration,
+ vsyncDuration) -
+ vsyncDuration,
+
+ appDurationToOffset(mAppEarlyGlDuration, mSfEarlyGlDuration,
+ vsyncDuration),
+ },
+ {
+ mSfDuration < vsyncDuration
+ ? sfDurationToOffset(mSfDuration, vsyncDuration)
+ : sfDurationToOffset(mSfDuration, vsyncDuration) -
+ vsyncDuration,
+
+ appDurationToOffset(mAppDuration, mSfDuration,
+ vsyncDuration),
+ },
+ });
+ }
+ return offsets;
+}
+
+PhaseDurations::PhaseDurations(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+ : PhaseDurations(getRefreshRatesFromConfigs(refreshRateConfigs),
+ refreshRateConfigs.getCurrentRefreshRate().fps,
+ getProperty("debug.sf.late.sf.duration").value_or(-1),
+ getProperty("debug.sf.late.app.duration").value_or(-1),
+ getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
+ getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
+ getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration),
+ getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) {
+ validateSysprops();
+}
+
+PhaseDurations::PhaseDurations(const std::vector<float>& refreshRates, float currentFps,
+ nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
+ nsecs_t appEarlyDuration, nsecs_t sfEarlyGlDuration,
+ nsecs_t appEarlyGlDuration)
+ : mSfDuration(sfDuration),
+ mAppDuration(appDuration),
+ mSfEarlyDuration(sfEarlyDuration),
+ mAppEarlyDuration(appEarlyDuration),
+ mSfEarlyGlDuration(sfEarlyGlDuration),
+ mAppEarlyGlDuration(appEarlyGlDuration),
+ mOffsets(initializeOffsets(refreshRates)),
+ mRefreshRateFps(currentFps) {}
+
+PhaseOffsets::Offsets PhaseDurations::getOffsetsForRefreshRate(float fps) const {
+ const auto iter = mOffsets.find(fps);
+ LOG_ALWAYS_FATAL_IF(iter == mOffsets.end());
+ return iter->second;
+}
+
+void PhaseDurations::dump(std::string& result) const {
+ const auto [early, earlyGl, late] = getCurrentOffsets();
+ using base::StringAppendF;
+ StringAppendF(&result,
+ " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64
+ " ns\n"
+ " app duration: %9" PRId64 " ns\t SF duration: %9" PRId64
+ " ns\n"
+ " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64
+ " ns\n"
+ " early app duration: %9" PRId64 " ns\t early SF duration: %9" PRId64
+ " ns\n"
+ " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64
+ " ns\n"
+ " GL early app duration: %9" PRId64 " ns\tGL early SF duration: %9" PRId64
+ " ns\n",
+ late.app,
+
+ late.sf,
+
+ mAppDuration, mSfDuration,
+
+ early.app, early.sf,
+
+ mAppEarlyDuration, mSfEarlyDuration,
+
+ earlyGl.app,
+
+ earlyGl.sf,
+
+ mAppEarlyGlDuration, mSfEarlyGlDuration);
}
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index 7747f0c..c10efde 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -29,17 +29,11 @@
* different offsets will help us with latency. This class keeps track of
* which mode the device is on, and returns approprate offsets when needed.
*/
-class PhaseOffsets {
+class PhaseConfiguration {
public:
using Offsets = VSyncModulator::OffsetsConfig;
- virtual ~PhaseOffsets();
-
- nsecs_t getCurrentAppOffset() const { return getCurrentOffsets().late.app; }
- nsecs_t getCurrentSfOffset() const { return getCurrentOffsets().late.sf; }
- nsecs_t getOffsetThresholdForNextVsync() const {
- return getCurrentOffsets().thresholdForNextVsync;
- }
+ virtual ~PhaseConfiguration();
virtual Offsets getCurrentOffsets() const = 0;
virtual Offsets getOffsetsForRefreshRate(float fps) const = 0;
@@ -51,9 +45,51 @@
namespace impl {
-class PhaseOffsets : public scheduler::PhaseOffsets {
+/*
+ * This is the old implementation of phase offsets and considered as deprecated.
+ * PhaseDurations is the new implementation.
+ */
+class PhaseOffsets : public scheduler::PhaseConfiguration {
public:
- PhaseOffsets();
+ PhaseOffsets(const scheduler::RefreshRateConfigs&);
+
+ // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
+ Offsets getOffsetsForRefreshRate(float fps) const override {
+ const auto iter = mOffsets.find(fps);
+ LOG_ALWAYS_FATAL_IF(iter == mOffsets.end());
+ return iter->second;
+ }
+
+ // Returns early, early GL, and late offsets for Apps and SF.
+ Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
+
+ // This function should be called when the device is switching between different
+ // refresh rates, to properly update the offsets.
+ void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
+
+ // Returns current offsets in human friendly format.
+ void dump(std::string& result) const override;
+
+private:
+ std::unordered_map<float, PhaseOffsets::Offsets> initializeOffsets(
+ const scheduler::RefreshRateConfigs&) const;
+ Offsets getDefaultOffsets(nsecs_t vsyncDuration) const;
+ Offsets getHighFpsOffsets(nsecs_t vsyncDuration) const;
+
+ const nsecs_t mThresholdForNextVsync;
+ const std::unordered_map<float, Offsets> mOffsets;
+
+ std::atomic<float> mRefreshRateFps;
+};
+
+/*
+ * Class that encapsulates the phase offsets for SurfaceFlinger and App.
+ * The offsets are calculated from durations for each one of the (late, early, earlyGL)
+ * offset types.
+ */
+class PhaseDurations : public scheduler::PhaseConfiguration {
+public:
+ PhaseDurations(const scheduler::RefreshRateConfigs&);
// Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
Offsets getOffsetsForRefreshRate(float fps) const override;
@@ -68,14 +104,28 @@
// Returns current offsets in human friendly format.
void dump(std::string& result) const override;
+protected:
+ // Used for unit tests
+ PhaseDurations(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration,
+ nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+ nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration);
+
private:
- static Offsets getDefaultOffsets(nsecs_t thresholdForNextVsync);
- static Offsets getHighFpsOffsets(nsecs_t thresholdForNextVsync);
+ std::unordered_map<float, PhaseDurations::Offsets> initializeOffsets(
+ const std::vector<float>&) const;
- std::atomic<float> mRefreshRateFps = 0;
+ const nsecs_t mSfDuration;
+ const nsecs_t mAppDuration;
- Offsets mDefaultOffsets;
- Offsets mHighFpsOffsets;
+ const nsecs_t mSfEarlyDuration;
+ const nsecs_t mAppEarlyDuration;
+
+ const nsecs_t mSfEarlyGlDuration;
+ const nsecs_t mAppEarlyGlDuration;
+
+ const std::unordered_map<float, Offsets> mOffsets;
+
+ std::atomic<float> mRefreshRateFps;
};
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 8fe7fcb..ff9cf86 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -47,6 +47,10 @@
#include "OneShotTimer.h"
#include "SchedulerUtils.h"
#include "SurfaceFlingerProperties.h"
+#include "Timer.h"
+#include "VSyncDispatchTimerQueue.h"
+#include "VSyncPredictor.h"
+#include "VSyncReactor.h"
#define RETURN_IF_INVALID_HANDLE(handle, ...) \
do { \
@@ -58,11 +62,45 @@
namespace android {
+std::unique_ptr<DispSync> createDispSync() {
+ // TODO (140302863) remove this and use the vsync_reactor system.
+ if (property_get_bool("debug.sf.vsync_reactor", false)) {
+ // TODO (144707443) tune Predictor tunables.
+ static constexpr int default_rate = 60;
+ static constexpr auto initial_period =
+ std::chrono::duration<nsecs_t, std::ratio<1, default_rate>>(1);
+ static constexpr size_t vsyncTimestampHistorySize = 20;
+ static constexpr size_t minimumSamplesForPrediction = 6;
+ static constexpr uint32_t discardOutlierPercent = 20;
+ auto tracker = std::make_unique<
+ scheduler::VSyncPredictor>(std::chrono::duration_cast<std::chrono::nanoseconds>(
+ initial_period)
+ .count(),
+ vsyncTimestampHistorySize, minimumSamplesForPrediction,
+ discardOutlierPercent);
+
+ static constexpr auto vsyncMoveThreshold =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(3ms);
+ static constexpr auto timerSlack =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(500us);
+ auto dispatch = std::make_unique<
+ scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), *tracker,
+ timerSlack.count(), vsyncMoveThreshold.count());
+
+ static constexpr size_t pendingFenceLimit = 20;
+ return std::make_unique<scheduler::VSyncReactor>(std::make_unique<scheduler::SystemClock>(),
+ std::move(dispatch), std::move(tracker),
+ pendingFenceLimit);
+ } else {
+ return std::make_unique<impl::DispSync>("SchedulerDispSync",
+ sysprop::running_without_sync_framework(true));
+ }
+}
+
Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
const scheduler::RefreshRateConfigs& refreshRateConfig,
ISchedulerCallback& schedulerCallback)
- : mPrimaryDispSync(new impl::DispSync("SchedulerDispSync",
- sysprop::running_without_sync_framework(true))),
+ : mPrimaryDispSync(createDispSync()),
mEventControlThread(new impl::EventControlThread(std::move(function))),
mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
mSchedulerCallback(schedulerCallback),
@@ -125,18 +163,16 @@
return *mPrimaryDispSync;
}
-std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
- const char* name, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync) {
+std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(const char* name,
+ nsecs_t phaseOffsetNs) {
return std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), phaseOffsetNs,
- offsetThresholdForNextVsync, true /* traceVsync */,
- name);
+ true /* traceVsync */, name);
}
Scheduler::ConnectionHandle Scheduler::createConnection(
- const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
+ const char* connectionName, nsecs_t phaseOffsetNs,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
- auto vsyncSource =
- makePrimaryDispSyncSource(connectionName, phaseOffsetNs, offsetThresholdForNextVsync);
+ auto vsyncSource = makePrimaryDispSyncSource(connectionName, phaseOffsetNs);
auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),
std::move(interceptCallback));
return createConnection(std::move(eventThread));
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index d1d5715..2cdb757 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -66,7 +66,6 @@
using ConnectionHandle = scheduler::ConnectionHandle;
ConnectionHandle createConnection(const char* connectionName, nsecs_t phaseOffsetNs,
- nsecs_t offsetThresholdForNextVsync,
impl::EventThread::InterceptVSyncsCallback);
sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle,
@@ -150,8 +149,7 @@
Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback);
- std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs,
- nsecs_t offsetThresholdForNextVsync);
+ std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
// Create a connection on the given EventThread.
ConnectionHandle createConnection(std::unique_ptr<EventThread>);
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index 63c0feb..704a5d5 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -43,6 +43,10 @@
struct Offsets {
nsecs_t sf;
nsecs_t app;
+
+ bool operator==(const Offsets& other) const { return sf == other.sf && app == other.app; }
+
+ bool operator!=(const Offsets& other) const { return !(*this == other); }
};
struct OffsetsConfig {
@@ -50,7 +54,11 @@
Offsets earlyGl; // As above but while compositing with GL.
Offsets late; // Default.
- nsecs_t thresholdForNextVsync;
+ bool operator==(const OffsetsConfig& other) const {
+ return early == other.early && earlyGl == other.earlyGl && late == other.late;
+ }
+
+ bool operator!=(const OffsetsConfig& other) const { return !(*this == other); }
};
VSyncModulator(Scheduler&, ConnectionHandle appConnectionHandle,
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 47e3f4f..a652053 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -26,12 +26,15 @@
namespace android::scheduler {
Clock::~Clock() = default;
+nsecs_t SystemClock::now() const {
+ return systemTime(SYSTEM_TIME_MONOTONIC);
+}
VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit)
: mClock(std::move(clock)),
- mDispatch(std::move(dispatch)),
mTracker(std::move(tracker)),
+ mDispatch(std::move(dispatch)),
mPendingLimit(pendingFenceLimit) {}
VSyncReactor::~VSyncReactor() = default;
@@ -151,7 +154,7 @@
mTracker->addVsyncTimestamp(signalTime);
}
- return false; // TODO(b/144707443): add policy for turning on HWVsync.
+ return mMoreSamplesNeeded;
}
void VSyncReactor::setIgnorePresentFences(bool ignoration) {
@@ -173,14 +176,9 @@
}
void VSyncReactor::setPeriod(nsecs_t period) {
- mTracker->setPeriod(period);
- {
- std::lock_guard<std::mutex> lk(mMutex);
- mPeriodChangeInProgress = true;
- for (auto& entry : mCallbacks) {
- entry.second->setPeriod(period);
- }
- }
+ std::lock_guard lk(mMutex);
+ mLastHwVsync.reset();
+ mPeriodTransitioningTo = period;
}
nsecs_t VSyncReactor::getPeriod() {
@@ -191,15 +189,40 @@
void VSyncReactor::endResync() {}
+bool VSyncReactor::periodChangeDetected(nsecs_t vsync_timestamp) {
+ if (!mLastHwVsync || !mPeriodTransitioningTo) {
+ return false;
+ }
+ auto const distance = vsync_timestamp - *mLastHwVsync;
+ return std::abs(distance - *mPeriodTransitioningTo) < std::abs(distance - getPeriod());
+}
+
bool VSyncReactor::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
assert(periodFlushed);
- mTracker->addVsyncTimestamp(timestamp);
- {
- std::lock_guard<std::mutex> lk(mMutex);
- *periodFlushed = mPeriodChangeInProgress;
- mPeriodChangeInProgress = false;
+
+ std::lock_guard<std::mutex> lk(mMutex);
+ if (periodChangeDetected(timestamp)) {
+ mMoreSamplesNeeded = false;
+ *periodFlushed = true;
+
+ mTracker->setPeriod(*mPeriodTransitioningTo);
+ for (auto& entry : mCallbacks) {
+ entry.second->setPeriod(*mPeriodTransitioningTo);
+ }
+
+ mPeriodTransitioningTo.reset();
+ mLastHwVsync.reset();
+ } else if (mPeriodTransitioningTo) {
+ mLastHwVsync = timestamp;
+ mMoreSamplesNeeded = true;
+ *periodFlushed = false;
+ } else {
+ mMoreSamplesNeeded = false;
+ *periodFlushed = false;
}
- return false;
+
+ mTracker->addVsyncTimestamp(timestamp);
+ return mMoreSamplesNeeded;
}
status_t VSyncReactor::addEventListener(const char* name, nsecs_t phase,
@@ -245,4 +268,10 @@
return NO_ERROR;
}
+void VSyncReactor::dump(std::string& result) const {
+ result += "VsyncReactor in use\n"; // TODO (b/144927823): add more information!
+}
+
+void VSyncReactor::reset() {}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 837eb75..73a5e37 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -23,7 +23,7 @@
#include <unordered_map>
#include <vector>
#include "DispSync.h"
-
+#include "TimeKeeper.h"
namespace android::scheduler {
class Clock;
@@ -32,43 +32,56 @@
class CallbackRepeater;
// TODO (b/145217110): consider renaming.
-class VSyncReactor /* TODO (b/140201379): : public android::DispSync */ {
+class VSyncReactor : public android::DispSync {
public:
VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit);
~VSyncReactor();
- bool addPresentFence(const std::shared_ptr<FenceTime>& fence);
- void setIgnorePresentFences(bool ignoration);
+ bool addPresentFence(const std::shared_ptr<FenceTime>& fence) final;
+ void setIgnorePresentFences(bool ignoration) final;
- nsecs_t computeNextRefresh(int periodOffset) const;
- nsecs_t expectedPresentTime();
+ nsecs_t computeNextRefresh(int periodOffset) const final;
+ nsecs_t expectedPresentTime() final;
- void setPeriod(nsecs_t period);
- nsecs_t getPeriod();
+ void setPeriod(nsecs_t period) final;
+ nsecs_t getPeriod() final;
// TODO: (b/145626181) remove begin,endResync functions from DispSync i/f when possible.
- void beginResync();
- bool addResyncSample(nsecs_t timestamp, bool* periodFlushed);
- void endResync();
+ void beginResync() final;
+ bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) final;
+ void endResync() final;
status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback,
- nsecs_t lastCallbackTime);
- status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback);
- status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase);
+ nsecs_t lastCallbackTime) final;
+ status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback) final;
+ status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) final;
+
+ void dump(std::string& result) const final;
+ void reset() final;
private:
+ bool periodChangeDetected(nsecs_t vsync_timestamp) REQUIRES(mMutex);
+
std::unique_ptr<Clock> const mClock;
- std::unique_ptr<VSyncDispatch> const mDispatch;
std::unique_ptr<VSyncTracker> const mTracker;
+ std::unique_ptr<VSyncDispatch> const mDispatch;
size_t const mPendingLimit;
std::mutex mMutex;
bool mIgnorePresentFences GUARDED_BY(mMutex) = false;
std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
- bool mPeriodChangeInProgress GUARDED_BY(mMutex) = false;
+
+ bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false;
+ std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
+ std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
+
std::unordered_map<DispSync::Callback*, std::unique_ptr<CallbackRepeater>> mCallbacks
GUARDED_BY(mMutex);
};
+class SystemClock : public Clock {
+ nsecs_t now() const final;
+};
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 6e827ac..8c1d168 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -260,7 +260,6 @@
mFrameTracer(std::make_unique<FrameTracer>()),
mEventQueue(mFactory.createMessageQueue()),
mCompositionEngine(mFactory.createCompositionEngine()),
- mPhaseOffsets(mFactory.createPhaseOffsets()),
mPendingSyncInputWindows(false) {}
SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
@@ -582,8 +581,6 @@
void SurfaceFlinger::init() {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
- ALOGI("Phase offset: %" PRId64 " ns", mPhaseOffsets->getCurrentAppOffset());
-
Mutex::Autolock _l(mStateLock);
// Get a RenderEngine for the given display / config (can't fail)
@@ -817,7 +814,7 @@
info.ydpi = ydpi;
info.fps = 1e9 / hwConfig->getVsyncPeriod();
- const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(info.fps);
+ const auto offset = mPhaseConfiguration->getOffsetsForRefreshRate(info.fps);
info.appVsyncOffset = offset.late.app;
// This is how far in advance a buffer must be queued for
@@ -897,8 +894,8 @@
// DispSync model is locked.
mVSyncModulator->onRefreshRateChangeInitiated();
- mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
- mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
+ mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+ mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
}
mDesiredActiveConfigChanged = true;
@@ -951,8 +948,8 @@
auto refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId);
- mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
- mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
+ mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+ mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
ATRACE_INT("ActiveConfigFPS", refreshRate.fps);
if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
@@ -972,8 +969,8 @@
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
auto refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
- mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
- mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
+ mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+ mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
}
bool SurfaceFlinger::performSetActiveConfig() {
@@ -1134,6 +1131,86 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
+ bool* outSupport) const {
+ Mutex::Autolock _l(mStateLock);
+
+ if (!displayToken) {
+ ALOGE("getAutoLowLatencyModeSupport() failed. Missing display token.");
+ return BAD_VALUE;
+ }
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("getAutoLowLatencyModeSupport() failed. Display id for display token %p not found.",
+ displayToken.get());
+ return NAME_NOT_FOUND;
+ }
+ *outSupport = getHwComposer().hasDisplayCapability(displayId,
+ HWC2::DisplayCapability::AutoLowLatencyMode);
+ return NO_ERROR;
+}
+
+void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
+ postMessageAsync(new LambdaMessage([=] { setAutoLowLatencyModeInternal(displayToken, on); }));
+}
+
+void SurfaceFlinger::setAutoLowLatencyModeInternal(const sp<IBinder>& displayToken, bool on) {
+ if (!displayToken) {
+ ALOGE("setAutoLowLatencyMode() failed. Missing display token.");
+ return;
+ }
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("setAutoLowLatencyMode() failed. Display id for display token %p not found.",
+ displayToken.get());
+ return;
+ }
+
+ getHwComposer().setAutoLowLatencyMode(*displayId, on);
+}
+
+status_t SurfaceFlinger::getGameContentTypeSupport(const sp<IBinder>& displayToken,
+ bool* outSupport) const {
+ Mutex::Autolock _l(mStateLock);
+
+ if (!displayToken) {
+ ALOGE("getGameContentTypeSupport() failed. Missing display token.");
+ return BAD_VALUE;
+ }
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("getGameContentTypeSupport() failed. Display id for display token %p not found.",
+ displayToken.get());
+ return NAME_NOT_FOUND;
+ }
+
+ std::vector<HWC2::ContentType> outSupportedContentTypes;
+ getHwComposer().getSupportedContentTypes(*displayId, &outSupportedContentTypes);
+ *outSupport = std::find(outSupportedContentTypes.begin(), outSupportedContentTypes.end(),
+ HWC2::ContentType::Game) != outSupportedContentTypes.end();
+ return NO_ERROR;
+}
+
+void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
+ postMessageAsync(new LambdaMessage([=] { setGameContentTypeInternal(displayToken, on); }));
+}
+
+void SurfaceFlinger::setGameContentTypeInternal(const sp<IBinder>& displayToken, bool on) {
+ if (!displayToken) {
+ ALOGE("setGameContentType() failed. Missing display token.");
+ return;
+ }
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("setGameContentType() failed. Display id for display token %p not found.",
+ displayToken.get());
+ return;
+ }
+
+ const HWC2::ContentType type = on ? HWC2::ContentType::Game : HWC2::ContentType::None;
+ getHwComposer().setContentType(*displayId, type);
+}
+
status_t SurfaceFlinger::clearAnimationFrameStats() {
Mutex::Autolock _l(mStateLock);
mAnimFrameTracker.clearStats();
@@ -1621,10 +1698,8 @@
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
// fence N-2
- const sp<Fence>& fence =
- mVSyncModulator->getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync()
- ? mPreviousPresentFences[0]
- : mPreviousPresentFences[1];
+ const sp<Fence>& fence = mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
+ : mPreviousPresentFences[1];
if (fence == Fence::NO_FENCE) {
return false;
@@ -1642,10 +1717,8 @@
mScheduler->getDisplayStatInfo(&stats);
const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime();
// Inflate the expected present time if we're targetting the next vsync.
- mExpectedPresentTime.store(mVSyncModulator->getOffsets().sf <
- mPhaseOffsets->getOffsetThresholdForNextVsync()
- ? presentTime
- : presentTime + stats.vsyncPeriod);
+ mExpectedPresentTime.store(
+ mVSyncModulator->getOffsets().sf > 0 ? presentTime : presentTime + stats.vsyncPeriod);
}
void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS {
@@ -1889,11 +1962,12 @@
nsecs_t compositeToPresentLatency) {
// Integer division and modulo round toward 0 not -inf, so we need to
// treat negative and positive offsets differently.
- nsecs_t idealLatency = (mPhaseOffsets->getCurrentSfOffset() > 0)
- ? (stats.vsyncPeriod - (mPhaseOffsets->getCurrentSfOffset() % stats.vsyncPeriod))
- : ((-mPhaseOffsets->getCurrentSfOffset()) % stats.vsyncPeriod);
+ nsecs_t idealLatency = (mPhaseConfiguration->getCurrentOffsets().late.sf > 0)
+ ? (stats.vsyncPeriod -
+ (mPhaseConfiguration->getCurrentOffsets().late.sf % stats.vsyncPeriod))
+ : ((-mPhaseConfiguration->getCurrentOffsets().late.sf) % stats.vsyncPeriod);
- // Just in case mPhaseOffsets->getCurrentSfOffset() == -vsyncInterval.
+ // Just in case mPhaseConfiguration->getCurrentOffsets().late.sf == -vsyncInterval.
if (idealLatency <= 0) {
idealLatency = stats.vsyncPeriod;
}
@@ -1902,8 +1976,8 @@
// composition and present times, which often have >1ms of jitter.
// Reducing jitter is important if an app attempts to extrapolate
// something (such as user input) to an accurate diasplay time.
- // Snapping also allows an app to precisely calculate mPhaseOffsets->getCurrentSfOffset()
- // with (presentLatency % interval).
+ // Snapping also allows an app to precisely calculate
+ // mPhaseConfiguration->getCurrentOffsets().late.sf with (presentLatency % interval).
nsecs_t bias = stats.vsyncPeriod / 2;
int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod;
nsecs_t snappedCompositeToPresentLatency =
@@ -2595,24 +2669,24 @@
currentConfig, HWC_POWER_MODE_OFF);
mRefreshRateStats->setConfigMode(currentConfig);
+ mPhaseConfiguration = getFactory().createPhaseConfiguration(*mRefreshRateConfigs);
+
// start the EventThread
mScheduler =
getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
*mRefreshRateConfigs, *this);
mAppConnectionHandle =
- mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
- mPhaseOffsets->getOffsetThresholdForNextVsync(),
+ mScheduler->createConnection("app", mPhaseConfiguration->getCurrentOffsets().late.app,
impl::EventThread::InterceptVSyncsCallback());
mSfConnectionHandle =
- mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
- mPhaseOffsets->getOffsetThresholdForNextVsync(),
+ mScheduler->createConnection("sf", mPhaseConfiguration->getCurrentOffsets().late.sf,
[this](nsecs_t timestamp) {
mInterceptor->saveVSyncEvent(timestamp);
});
mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
- mPhaseOffsets->getCurrentOffsets());
+ mPhaseConfiguration->getCurrentOffsets());
mRegionSamplingThread =
new RegionSamplingThread(*this, *mScheduler,
@@ -4024,7 +4098,7 @@
mRefreshRateStats->dump(result);
result.append("\n");
- mPhaseOffsets->dump(result);
+ mPhaseConfiguration->dump(result);
StringAppendF(&result,
" present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
dispSyncPresentTimeOffset, getVsyncPeriod());
@@ -4466,6 +4540,10 @@
case SET_DESIRED_DISPLAY_CONFIG_SPECS:
case GET_DESIRED_DISPLAY_CONFIG_SPECS:
case SET_ACTIVE_COLOR_MODE:
+ case GET_AUTO_LOW_LATENCY_MODE_SUPPORT:
+ case SET_AUTO_LOW_LATENCY_MODE:
+ case GET_GAME_CONTENT_TYPE_SUPPORT:
+ case SET_GAME_CONTENT_TYPE:
case INJECT_VSYNC:
case SET_POWER_MODE:
case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 81000c6..2f84b13 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -438,6 +438,12 @@
ui::DisplayPrimaries &primaries);
ui::ColorMode getActiveColorMode(const sp<IBinder>& displayToken) override;
status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
+ status_t getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
+ bool* outSupported) const override;
+ void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) override;
+ status_t getGameContentTypeSupport(const sp<IBinder>& displayToken,
+ bool* outSupported) const override;
+ void setGameContentType(const sp<IBinder>& displayToken, bool on) override;
void setPowerMode(const sp<IBinder>& displayToken, int mode) override;
status_t setActiveConfig(const sp<IBinder>& displayToken, int id) override;
status_t clearAnimationFrameStats() override;
@@ -550,6 +556,11 @@
float minRefreshRate, float maxRefreshRate)
EXCLUDES(mStateLock);
+ // called on the main thread in response to setAutoLowLatencyMode()
+ void setAutoLowLatencyModeInternal(const sp<IBinder>& displayToken, bool on);
+ // called on the main thread in response to setGameContentType()
+ void setGameContentTypeInternal(const sp<IBinder>& displayToken, bool on);
+
// Returns whether the transaction actually modified any state
bool handleMessageTransaction();
@@ -1116,7 +1127,7 @@
scheduler::ConnectionHandle mSfConnectionHandle;
// Stores phase offsets configured per refresh rate.
- const std::unique_ptr<scheduler::PhaseOffsets> mPhaseOffsets;
+ std::unique_ptr<scheduler::PhaseConfiguration> mPhaseConfiguration;
// Optional to defer construction until scheduler connections are created.
std::optional<scheduler::VSyncModulator> mVSyncModulator;
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index bd4cdba..d5c2306 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -15,6 +15,7 @@
*/
#include <compositionengine/impl/CompositionEngine.h>
+#include <cutils/properties.h>
#include <ui/GraphicBuffer.h>
#include "BufferLayerConsumer.h"
@@ -51,16 +52,20 @@
}
std::unique_ptr<HWComposer> DefaultFactory::createHWComposer(const std::string& serviceName) {
- return std::make_unique<android::impl::HWComposer>(
- std::make_unique<Hwc2::impl::Composer>(serviceName));
+ return std::make_unique<android::impl::HWComposer>(serviceName);
}
std::unique_ptr<MessageQueue> DefaultFactory::createMessageQueue() {
return std::make_unique<android::impl::MessageQueue>();
}
-std::unique_ptr<scheduler::PhaseOffsets> DefaultFactory::createPhaseOffsets() {
- return std::make_unique<scheduler::impl::PhaseOffsets>();
+std::unique_ptr<scheduler::PhaseConfiguration> DefaultFactory::createPhaseConfiguration(
+ const scheduler::RefreshRateConfigs& refreshRateConfigs) {
+ if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
+ return std::make_unique<scheduler::impl::PhaseDurations>(refreshRateConfigs);
+ } else {
+ return std::make_unique<scheduler::impl::PhaseOffsets>(refreshRateConfigs);
+ }
}
std::unique_ptr<Scheduler> DefaultFactory::createScheduler(
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 1a24448..36fae21 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -30,7 +30,8 @@
std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) override;
std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
std::unique_ptr<MessageQueue> createMessageQueue() override;
- std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override;
+ std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ const scheduler::RefreshRateConfigs&) override;
std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 0db941d..951bd09 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -56,7 +56,7 @@
} // namespace compositionengine
namespace scheduler {
-class PhaseOffsets;
+class PhaseConfiguration;
class RefreshRateConfigs;
} // namespace scheduler
@@ -74,7 +74,8 @@
virtual std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) = 0;
virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
- virtual std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() = 0;
+ virtual std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ const scheduler::RefreshRateConfigs&) = 0;
virtual std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) = 0;
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 7e6c472..5e8910a 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -124,6 +124,7 @@
addRelativeParentLocked(transaction, layerId,
getLayerIdFromWeakRef(layer->mCurrentState.zOrderRelativeOf),
layer->mCurrentState.z);
+ addShadowRadiusLocked(transaction, layerId, layer->mCurrentState.shadowRadius);
}
void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment,
@@ -368,6 +369,13 @@
overrideChange->set_z(z);
}
+void SurfaceInterceptor::addShadowRadiusLocked(Transaction* transaction, int32_t layerId,
+ float shadowRadius) {
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ ShadowRadiusChange* overrideChange(change->mutable_shadow_radius());
+ overrideChange->set_radius(shadowRadius);
+}
+
void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction,
const layer_state_t& state)
{
@@ -441,6 +449,9 @@
addRelativeParentLocked(transaction, layerId,
getLayerIdFromHandle(state.relativeLayerHandle), state.z);
}
+ if (state.what & layer_state_t::eShadowRadiusChanged) {
+ addShadowRadiusLocked(transaction, layerId, state.shadowRadius);
+ }
}
void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction,
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 72b734b..c6f9e8a 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -165,6 +165,7 @@
void addDetachChildrenLocked(Transaction* transaction, int32_t layerId, bool detached);
void addRelativeParentLocked(Transaction* transaction, int32_t layerId, int32_t parentId,
int z);
+ void addShadowRadiusLocked(Transaction* transaction, int32_t layerId, float shadowRadius);
// Add display transactions to the trace
DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t sequenceId);
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index ef488bd..ef27847 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -112,7 +112,8 @@
outData.resize(dataStr.size());
memcpy(outData.data(), dataStr.data(), dataStr.size());
}
-
+ layer.cornerRadiusCrop = generateFloatRect(layerProto.corner_radius_crop());
+ layer.shadowRadius = layerProto.shadow_radius();
return layer;
}
@@ -307,8 +308,9 @@
first = false;
result.append(metadata.itemToString(entry.first, ":"));
}
- result.append("}");
-
+ result.append("},");
+ StringAppendF(&result, " cornerRadiusCrop=%s, ", cornerRadiusCrop.to_string().c_str());
+ StringAppendF(&result, " shadowRadius=%.3f, ", shadowRadius);
return result;
}
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index 54e02ca..774b0e1 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -111,6 +111,8 @@
bool isProtected;
float cornerRadius;
LayerMetadata metadata;
+ LayerProtoParser::FloatRect cornerRadiusCrop;
+ float shadowRadius;
std::string to_string() const;
};
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index c7fbff3..23df1bb 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -94,6 +94,14 @@
FloatRectProto screen_bounds = 46;
InputWindowInfoProto input_window_info = 47;
+
+ // Crop used to draw the rounded corner.
+ FloatRectProto corner_radius_crop = 48;
+
+ // length of the shadow to draw around the layer, it may be set on the
+ // layer or set by a parent layer.
+ float shadow_radius = 49;
+ ColorTransformProto color_transform = 50;
}
message PositionProto {
@@ -174,3 +182,8 @@
bool replace_touchable_region_with_crop = 14;
RectProto touchable_region_crop = 15;
}
+
+message ColorTransformProto {
+ // This will be a 4x4 matrix of float values
+ repeated float val = 1;
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index dcfc077..a4d9ff3 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -42,6 +42,7 @@
"libtrace_proto",
],
shared_libs: [
+ "android.hardware.graphics.common-ndk_platform",
"android.hardware.graphics.common@1.2",
"android.hardware.graphics.composer@2.1",
"libandroid",
@@ -56,7 +57,6 @@
"libtimestats_proto",
"libui",
"libutils",
- "vintf-graphics-common-ndk_platform",
]
}
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 420fb29..d51b9a1 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -31,42 +31,40 @@
void SetUp() override { mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); }
sp<IBinder> mDisplayToken;
- int32_t defaultConfigId;
- float minRefreshRate;
- float maxRefreshRate;
};
-TEST_F(RefreshRateRangeTest, simpleSetAndGet) {
- status_t res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, 1, 45, 75);
- EXPECT_EQ(res, NO_ERROR);
+TEST_F(RefreshRateRangeTest, setAllConfigs) {
+ int32_t initialDefaultConfig;
+ float initialMin;
+ float initialMax;
+ status_t res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken,
+ &initialDefaultConfig,
+ &initialMin, &initialMax);
+ ASSERT_EQ(res, NO_ERROR);
- res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfigId,
- &minRefreshRate, &maxRefreshRate);
- EXPECT_EQ(res, NO_ERROR);
- EXPECT_EQ(defaultConfigId, 1);
- EXPECT_EQ(minRefreshRate, 45);
- EXPECT_EQ(maxRefreshRate, 75);
+ Vector<DisplayInfo> configs;
+ res = SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &configs);
+ ASSERT_EQ(res, NO_ERROR);
+
+ for (size_t i = 0; i < configs.size(); i++) {
+ res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, i, configs[i].fps,
+ configs[i].fps);
+ ASSERT_EQ(res, NO_ERROR);
+
+ int defaultConfig;
+ float minFps;
+ float maxFps;
+ res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfig,
+ &minFps, &maxFps);
+ ASSERT_EQ(res, NO_ERROR);
+ ASSERT_EQ(defaultConfig, i);
+ ASSERT_EQ(minFps, configs[i].fps);
+ ASSERT_EQ(maxFps, configs[i].fps);
+ }
+
+ res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, initialDefaultConfig,
+ initialMin, initialMax);
+ ASSERT_EQ(res, NO_ERROR);
}
-TEST_F(RefreshRateRangeTest, complexSetAndGet) {
- status_t res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, 1, 45, 75);
- EXPECT_EQ(res, NO_ERROR);
-
- res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfigId,
- &minRefreshRate, &maxRefreshRate);
- EXPECT_EQ(res, NO_ERROR);
- EXPECT_EQ(defaultConfigId, 1);
- EXPECT_EQ(minRefreshRate, 45);
- EXPECT_EQ(maxRefreshRate, 75);
-
- // Second call overrides the first one.
- res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, 10, 145, 875);
- EXPECT_EQ(res, NO_ERROR);
- res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfigId,
- &minRefreshRate, &maxRefreshRate);
- EXPECT_EQ(res, NO_ERROR);
- EXPECT_EQ(defaultConfigId, 10);
- EXPECT_EQ(minRefreshRate, 145);
- EXPECT_EQ(maxRefreshRate, 875);
-}
} // namespace android
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index b196684..7786638 100644
--- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -1,5 +1,5 @@
{
"presubmit": {
- "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*:MultiDisplayLayerBoundsTest.*:InvalidHandleTest.*:VirtualDisplayTest.*:RelativeZTest.*:SetGeometryTest.*"
+ "filter": "*:-RefreshRateRangeTest.*:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/3:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/3"
}
}
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 59e9c00..1fa426d 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -51,6 +51,7 @@
constexpr float CORNER_RADIUS_UPDATE = 0.2f;
constexpr float POSITION_UPDATE = 121;
const Rect CROP_UPDATE(16, 16, 32, 32);
+const float SHADOW_RADIUS_UPDATE = 35.0f;
const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
constexpr auto TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface";
@@ -190,6 +191,7 @@
bool relativeParentUpdateFound(const SurfaceChange& change, bool found);
bool detachChildrenUpdateFound(const SurfaceChange& change, bool found);
bool reparentChildrenUpdateFound(const SurfaceChange& change, bool found);
+ bool shadowRadiusUpdateFound(const SurfaceChange& change, bool found);
bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase);
// Find all of the updates in the single trace
@@ -226,6 +228,7 @@
void relativeParentUpdate(Transaction&);
void detachChildrenUpdate(Transaction&);
void reparentChildrenUpdate(Transaction&);
+ void shadowRadiusUpdate(Transaction&);
void surfaceCreation(Transaction&);
void displayCreation(Transaction&);
void displayDeletion(Transaction&);
@@ -406,6 +409,10 @@
t.reparentChildren(mBGSurfaceControl, mFGSurfaceControl->getHandle());
}
+void SurfaceInterceptorTest::shadowRadiusUpdate(Transaction& t) {
+ t.setShadowRadius(mBGSurfaceControl, SHADOW_RADIUS_UPDATE);
+}
+
void SurfaceInterceptorTest::displayCreation(Transaction&) {
sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
SurfaceComposerClient::destroyDisplay(testDisplay);
@@ -435,6 +442,7 @@
runInTransaction(&SurfaceInterceptorTest::reparentChildrenUpdate);
runInTransaction(&SurfaceInterceptorTest::detachChildrenUpdate);
runInTransaction(&SurfaceInterceptorTest::relativeParentUpdate);
+ runInTransaction(&SurfaceInterceptorTest::shadowRadiusUpdate);
}
void SurfaceInterceptorTest::surfaceCreation(Transaction&) {
@@ -655,6 +663,17 @@
return found;
}
+bool SurfaceInterceptorTest::shadowRadiusUpdateFound(const SurfaceChange& change,
+ bool foundShadowRadius) {
+ bool hasShadowRadius(change.shadow_radius().radius() == SHADOW_RADIUS_UPDATE);
+ if (hasShadowRadius && !foundShadowRadius) {
+ foundShadowRadius = true;
+ } else if (hasShadowRadius && foundShadowRadius) {
+ []() { FAIL(); }();
+ }
+ return foundShadowRadius;
+}
+
bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace,
SurfaceChange::SurfaceChangeCase changeCase) {
bool foundUpdate = false;
@@ -718,6 +737,9 @@
case SurfaceChange::SurfaceChangeCase::kDetachChildren:
foundUpdate = detachChildrenUpdateFound(change, foundUpdate);
break;
+ case SurfaceChange::SurfaceChangeCase::kShadowRadius:
+ foundUpdate = shadowRadiusUpdateFound(change, foundUpdate);
+ break;
case SurfaceChange::SurfaceChangeCase::SURFACECHANGE_NOT_SET:
break;
}
@@ -920,6 +942,11 @@
SurfaceChange::SurfaceChangeCase::kDetachChildren);
}
+TEST_F(SurfaceInterceptorTest, InterceptShadowRadiusUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::shadowRadiusUpdate,
+ SurfaceChange::SurfaceChangeCase::kShadowRadius);
+}
+
TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
captureTest(&SurfaceInterceptorTest::runAllUpdates,
&SurfaceInterceptorTest::assertAllUpdatesFound);
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
index 6d79615..0c370a6 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -760,6 +760,22 @@
return V2_4::Error::UNSUPPORTED;
}
+V2_4::Error FakeComposerClient::setAutoLowLatencyMode(Display, bool) {
+ ALOGV("setAutoLowLatencyMode");
+ return V2_4::Error::UNSUPPORTED;
+}
+
+V2_4::Error FakeComposerClient::getSupportedContentTypes(
+ Display, std::vector<IComposerClient::ContentType>*) {
+ ALOGV("getSupportedContentTypes");
+ return V2_4::Error::UNSUPPORTED;
+}
+
+V2_4::Error FakeComposerClient::setContentType(Display, IComposerClient::ContentType) {
+ ALOGV("setContentType");
+ return V2_4::Error::UNSUPPORTED;
+}
+
//////////////////////////////////////////////////////////////////
void FakeComposerClient::requestVSync(uint64_t vsyncTime) {
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
index 2a08b9b..f9ff2bf 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
@@ -246,6 +246,11 @@
Display display, Config config,
const V2_4::IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) override;
+ V2_4::Error setAutoLowLatencyMode(Display display, bool on) override;
+ V2_4::Error getSupportedContentTypes(
+ Display display,
+ std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
+ V2_4::Error setContentType(Display display, IComposerClient::ContentType type) override;
void setClient(ComposerClient* client);
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index ccfa6c5..68adbfc 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -45,6 +45,7 @@
"OneShotTimerTest.cpp",
"LayerHistoryTest.cpp",
"LayerMetadataTest.cpp",
+ "PhaseOffsetsTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
"RefreshRateConfigsTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 76dea62..cce21ce 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -1154,8 +1154,10 @@
static void cleanup(CompositionTest* test) {
Layer::cleanupInjectedLayers(test);
- for (auto& hwcDisplay : test->mFlinger.mFakeHwcDisplays) {
- hwcDisplay->mutableLayers().clear();
+ for (auto& displayData : test->mFlinger.mutableHwcDisplayData()) {
+ static_cast<TestableSurfaceFlinger::HWC2Display*>(displayData.second.hwcDisplay.get())
+ ->mutableLayers()
+ .clear();
}
}
};
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index 0aa8cf5..2e705da 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -51,7 +51,6 @@
AsyncCallRecorder<void (*)(nsecs_t)> mVSyncEventCallRecorder;
static constexpr std::chrono::nanoseconds mPhaseOffset = 6ms;
- static constexpr std::chrono::nanoseconds mOffsetThresholdForNextVsync = 16ms;
static constexpr int mIterations = 100;
};
@@ -79,8 +78,7 @@
void DispSyncSourceTest::createDispSyncSource() {
createDispSync();
- mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(),
- mOffsetThresholdForNextVsync.count(), true,
+ mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), true,
"DispSyncSourceTest");
mDispSyncSource->setCallback(this);
}
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
index da4eea0..b50ddf5 100644
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -22,7 +22,7 @@
namespace android::scheduler {
-struct FakePhaseOffsets : PhaseOffsets {
+struct FakePhaseOffsets : PhaseConfiguration {
static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
Offsets getOffsetsForRefreshRate(float) const override { return getCurrentOffsets(); }
@@ -30,8 +30,7 @@
Offsets getCurrentOffsets() const override {
return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- FAKE_PHASE_OFFSET_NS};
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
}
void setRefreshRateFps(float) override {}
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
new file mode 100644
index 0000000..6360ec1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "Scheduler/PhaseOffsets.h"
+
+using namespace testing;
+
+namespace android {
+namespace scheduler {
+
+class TestablePhaseOffsetsAsDurations : public impl::PhaseDurations {
+public:
+ TestablePhaseOffsetsAsDurations(float currentFps, nsecs_t sfDuration, nsecs_t appDuration,
+ nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+ nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
+ : impl::PhaseDurations({60.0f, 90.0f}, currentFps, sfDuration, appDuration,
+ sfEarlyDuration, appEarlyDuration, sfEarlyGlDuration,
+ appEarlyGlDuration) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+ PhaseOffsetsTest()
+ : mPhaseOffsets(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
+ 38'000'000) {}
+
+ ~PhaseOffsetsTest() = default;
+
+ TestablePhaseOffsetsAsDurations mPhaseOffsets;
+};
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_60Hz) {
+ mPhaseOffsets.setRefreshRateFps(60.0f);
+ auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
+ auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(60.0f);
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sf, 6'166'667);
+
+ EXPECT_EQ(offsets.late.app, 2'333'334);
+
+ EXPECT_EQ(offsets.early.sf, 666'667);
+
+ EXPECT_EQ(offsets.early.app, 500'001);
+
+ EXPECT_EQ(offsets.earlyGl.sf, 3'166'667);
+
+ EXPECT_EQ(offsets.earlyGl.app, 15'166'668);
+}
+
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_90Hz) {
+ mPhaseOffsets.setRefreshRateFps(90.0f);
+ auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
+ auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(90.0f);
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sf, 611'111);
+
+ EXPECT_EQ(offsets.late.app, 2'333'333);
+
+ EXPECT_EQ(offsets.early.sf, -4'888'889);
+
+ EXPECT_EQ(offsets.early.app, 6'055'555);
+
+ EXPECT_EQ(offsets.earlyGl.sf, -2'388'889);
+
+ EXPECT_EQ(offsets.earlyGl.app, 4'055'555);
+}
+
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_DefaultOffsets) {
+ TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
+
+ auto validateOffsets = [](auto& offsets) {
+ EXPECT_EQ(offsets.late.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.late.app, 1'000'000);
+
+ EXPECT_EQ(offsets.early.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.early.app, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
+ };
+
+ phaseOffsetsWithDefaultValues.setRefreshRateFps(90.0f);
+ auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
+ auto offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
+ EXPECT_EQ(currentOffsets, offsets);
+ validateOffsets(offsets);
+
+ phaseOffsetsWithDefaultValues.setRefreshRateFps(60.0f);
+ currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
+ offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
+ EXPECT_EQ(currentOffsets, offsets);
+ validateOffsets(offsets);
+}
+
+} // namespace
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index f057c98..b5245e2 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -79,7 +79,8 @@
return std::make_unique<android::impl::MessageQueue>();
}
- std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override {
+ std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ const scheduler::RefreshRateConfigs& /*refreshRateConfigs*/) override {
return std::make_unique<scheduler::FakePhaseOffsets>();
}
@@ -207,6 +208,8 @@
scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats,
/*currentConfig=*/HwcConfigIndexType(0),
/*powerMode=*/HWC_POWER_MODE_OFF);
+ mFlinger->mPhaseConfiguration =
+ mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs);
mScheduler =
new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
@@ -218,7 +221,7 @@
mFlinger->mVSyncModulator.emplace(*mScheduler, mFlinger->mAppConnectionHandle,
mFlinger->mSfConnectionHandle,
- mFlinger->mPhaseOffsets->getCurrentOffsets());
+ mFlinger->mPhaseConfiguration->getCurrentOffsets());
}
void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
@@ -515,15 +518,13 @@
display->mutableIsConnected() = true;
display->setPowerMode(static_cast<HWC2::PowerMode>(mPowerMode));
- flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = display.get();
+ flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
if (mHwcDisplayType == HWC2::DisplayType::Physical) {
flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, mDisplayId);
(mIsPrimary ? flinger->mutableInternalHwcDisplayId()
: flinger->mutableExternalHwcDisplayId()) = mHwcDisplayId;
}
-
- flinger->mFakeHwcDisplays.push_back(std::move(display));
}
private:
@@ -632,9 +633,6 @@
surfaceflinger::test::Factory mFactory;
sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
TestableScheduler* mScheduler = nullptr;
-
- // We need to keep a reference to these so they are properly destroyed.
- std::vector<std::unique_ptr<HWC2Display>> mFakeHwcDisplays;
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 537cc80..72f8bb9 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -159,7 +159,6 @@
static constexpr nsecs_t mPhase = 3000;
static constexpr nsecs_t mAnotherPhase = 5200;
static constexpr nsecs_t period = 10000;
- static constexpr nsecs_t mAnotherPeriod = 23333;
static constexpr nsecs_t mFakeCbTime = 2093;
static constexpr nsecs_t mFakeNow = 2214;
static constexpr const char mName[] = "callbacky";
@@ -274,10 +273,38 @@
EXPECT_THAT(mReactor.getPeriod(), Eq(fakePeriod));
}
-TEST_F(VSyncReactorTest, setPeriod) {
- nsecs_t const fakePeriod = 4098;
- EXPECT_CALL(*mMockTracker, setPeriod(fakePeriod));
- mReactor.setPeriod(fakePeriod);
+TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) {
+ nsecs_t const newPeriod = 5000;
+ EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
+ mReactor.setPeriod(newPeriod);
+
+ bool periodFlushed = true;
+ EXPECT_TRUE(mReactor.addResyncSample(10000, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+
+ EXPECT_TRUE(mReactor.addResyncSample(20000, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+
+ Mock::VerifyAndClearExpectations(mMockTracker.get());
+ EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
+
+ EXPECT_FALSE(mReactor.addResyncSample(25000, &periodFlushed));
+ EXPECT_TRUE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) {
+ nsecs_t const newPeriod = 5000;
+ EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
+ mReactor.setPeriod(newPeriod);
+
+ bool periodFlushed = true;
+ EXPECT_TRUE(mReactor.addResyncSample(5000, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ Mock::VerifyAndClearExpectations(mMockTracker.get());
+
+ EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
+ EXPECT_FALSE(mReactor.addResyncSample(10000, &periodFlushed));
+ EXPECT_TRUE(periodFlushed);
}
TEST_F(VSyncReactorTest, addResyncSampleTypical) {
@@ -291,12 +318,43 @@
TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) {
bool periodFlushed = false;
- nsecs_t const fakeTimestamp = 4398;
- nsecs_t const newPeriod = 3490;
- EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
+ nsecs_t const newPeriod = 4000;
+
mReactor.setPeriod(newPeriod);
- EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, &periodFlushed));
+
+ auto time = 0;
+ auto constexpr numTimestampSubmissions = 10;
+ for (auto i = 0; i < numTimestampSubmissions; i++) {
+ time += period;
+ EXPECT_TRUE(mReactor.addResyncSample(time, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ }
+
+ time += newPeriod;
+ EXPECT_FALSE(mReactor.addResyncSample(time, &periodFlushed));
EXPECT_TRUE(periodFlushed);
+
+ for (auto i = 0; i < numTimestampSubmissions; i++) {
+ time += newPeriod;
+ EXPECT_FALSE(mReactor.addResyncSample(time, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ }
+}
+
+TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsHwVsync) {
+ auto time = 0;
+ bool periodFlushed = false;
+ nsecs_t const newPeriod = 4000;
+ mReactor.setPeriod(newPeriod);
+
+ time += period;
+ mReactor.addResyncSample(time, &periodFlushed);
+ EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+
+ time += newPeriod;
+ mReactor.addResyncSample(time, &periodFlushed);
+
+ EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
static nsecs_t computeWorkload(nsecs_t period, nsecs_t phase) {
@@ -399,20 +457,26 @@
mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
}
-TEST_F(VSyncReactorTest, changingPeriodChangesOfsetsOnNextCb) {
+TEST_F(VSyncReactorTest, changingPeriodChangesOffsetsOnNextCb) {
+ static constexpr nsecs_t anotherPeriod = 23333;
Sequence seq;
EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
.InSequence(seq)
.WillOnce(Return(mFakeToken));
EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
.InSequence(seq);
- EXPECT_CALL(*mMockTracker, setPeriod(mAnotherPeriod));
+ EXPECT_CALL(*mMockTracker, setPeriod(anotherPeriod));
EXPECT_CALL(*mMockDispatch,
- schedule(mFakeToken, computeWorkload(mAnotherPeriod, mPhase), mFakeNow))
+ schedule(mFakeToken, computeWorkload(anotherPeriod, mPhase), mFakeNow))
.InSequence(seq);
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
- mReactor.setPeriod(mAnotherPeriod);
+
+ bool periodFlushed = false;
+ mReactor.setPeriod(anotherPeriod);
+ EXPECT_TRUE(mReactor.addResyncSample(anotherPeriod, &periodFlushed));
+ EXPECT_FALSE(mReactor.addResyncSample(anotherPeriod * 2, &periodFlushed));
+
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
}
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 2453ccb..0f9dd5b 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -130,6 +130,10 @@
MOCK_METHOD4(setActiveConfigWithConstraints,
V2_4::Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
VsyncPeriodChangeTimeline*));
+ MOCK_METHOD2(setAutoLowLatencyMode, V2_4::Error(Display, bool));
+ MOCK_METHOD2(getSupportedContentTypes,
+ V2_4::Error(Display, std::vector<IComposerClient::ContentType>*));
+ MOCK_METHOD2(setContentType, V2_4::Error(Display, IComposerClient::ContentType));
};
} // namespace mock