Merge "Switch from allowed display configs to refresh rate range"
diff --git a/build/phone-xhdpi-2048-dalvik-heap.mk b/build/phone-xhdpi-2048-dalvik-heap.mk
index 042a6e6..7ccfc13 100644
--- a/build/phone-xhdpi-2048-dalvik-heap.mk
+++ b/build/phone-xhdpi-2048-dalvik-heap.mk
@@ -14,7 +14,7 @@
# limitations under the License.
#
-# Provides overrides to configure the Dalvik heap for a 2G phone
+# Provides overrides to configure the Dalvik heap for a 2GB phone
# 192m of RAM gives enough space for 5 8 megapixel camera bitmaps in RAM.
PRODUCT_PROPERTY_OVERRIDES += \
diff --git a/build/phone-xhdpi-4096-dalvik-heap.mk b/build/phone-xhdpi-4096-dalvik-heap.mk
new file mode 100644
index 0000000..2b84841
--- /dev/null
+++ b/build/phone-xhdpi-4096-dalvik-heap.mk
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+# Provides overrides to configure the Dalvik heap for a 4GB phone
+
+PRODUCT_PROPERTY_OVERRIDES += \
+ dalvik.vm.heapstartsize=8m \
+ dalvik.vm.heapgrowthlimit=192m \
+ dalvik.vm.heapsize=512m \
+ dalvik.vm.heaptargetutilization=0.6 \
+ dalvik.vm.heapminfree=8m \
+ dalvik.vm.heapmaxfree=16m
diff --git a/build/phone-xhdpi-6144-dalvik-heap.mk b/build/phone-xhdpi-6144-dalvik-heap.mk
new file mode 100644
index 0000000..2bacc4a
--- /dev/null
+++ b/build/phone-xhdpi-6144-dalvik-heap.mk
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+# Provides overrides to configure the Dalvik heap for a 6GB phone
+
+PRODUCT_PROPERTY_OVERRIDES += \
+ dalvik.vm.heapstartsize=16m \
+ dalvik.vm.heapgrowthlimit=256m \
+ dalvik.vm.heapsize=512m \
+ dalvik.vm.heaptargetutilization=0.5 \
+ dalvik.vm.heapminfree=8m \
+ dalvik.vm.heapmaxfree=32m
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index ccd74db..87ea520 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -81,8 +81,8 @@
binder::Status DumpstateService::startBugreport(int32_t calling_uid,
const std::string& calling_package,
- const android::base::unique_fd& bugreport_fd,
- const android::base::unique_fd& screenshot_fd,
+ android::base::unique_fd bugreport_fd,
+ android::base::unique_fd screenshot_fd,
int bugreport_mode,
const sp<IDumpstateListener>& listener) {
MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index 27954ad..6dc0225 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -39,8 +39,8 @@
status_t dump(int fd, const Vector<String16>& args) override;
binder::Status startBugreport(int32_t calling_uid, const std::string& calling_package,
- const android::base::unique_fd& bugreport_fd,
- const android::base::unique_fd& screenshot_fd, int bugreport_mode,
+ android::base::unique_fd bugreport_fd,
+ android::base::unique_fd screenshot_fd, int bugreport_mode,
const sp<IDumpstateListener>& listener) override;
// No-op
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index f1884f8..dac90d9 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -444,7 +444,7 @@
sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
android::binder::Status status =
- ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+ ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener);
// startBugreport is an async call. Verify binder call succeeded first, then wait till listener
// gets expected callbacks.
@@ -480,7 +480,7 @@
// Call startBugreport with bad arguments.
sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
android::binder::Status status =
- ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+ ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
2000, // invalid bugreport mode
listener);
EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
@@ -501,20 +501,24 @@
// Prepare arguments
unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
+ unique_fd bugreport_fd2(dup(bugreport_fd.get()));
unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
+ unique_fd screenshot_fd2(dup(screenshot_fd.get()));
EXPECT_NE(bugreport_fd.get(), -1);
+ EXPECT_NE(bugreport_fd2.get(), -1);
EXPECT_NE(screenshot_fd.get(), -1);
+ EXPECT_NE(screenshot_fd2.get(), -1);
sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout))));
android::binder::Status status =
- ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+ ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1);
EXPECT_TRUE(status.isOk());
// try to make another call to startBugreport. This should fail.
sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout))));
- status = ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+ status = ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd2), std::move(screenshot_fd2),
Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2);
EXPECT_FALSE(status.isOk());
WaitTillExecutionComplete(listener2.get());
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 5476319..08a31c1 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -20,15 +20,16 @@
"android.hardware.vibrator@1.2",
"android.hardware.vibrator@1.3",
"libbase",
- "libbinder",
+ "libbinder_ndk",
"libhidlbase",
"liblog",
"libutils",
- "vintf-vibrator-cpp",
+ "vintf-vibrator-ndk_platform",
],
cflags: [
"-DLOG_TAG=\"idlcli\"",
],
+ vendor_available: true,
}
cc_library {
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index 2f11923..ca5142d 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -13,13 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
#define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+#include <android/binder_manager.h>
#include <android/hardware/vibrator/1.3/IVibrator.h>
-#include <android/hardware/vibrator/IVibrator.h>
-#include <binder/IServiceManager.h>
#include "utils.h"
@@ -39,22 +38,27 @@
}
template <>
-inline binder::Status NullptrStatus() {
- using binder::Status;
- return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+inline ndk::ScopedAStatus NullptrStatus() {
+ return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_NULL_POINTER));
}
template <typename I>
-inline sp<I> getService() {
+inline auto getService() {
return I::getService();
}
template <>
-inline sp<hardware::vibrator::IVibrator> getService() {
- return waitForVintfService<hardware::vibrator::IVibrator>();
+inline auto getService<aidl::android::hardware::vibrator::IVibrator>() {
+ const auto instance =
+ std::string() + aidl::android::hardware::vibrator::IVibrator::descriptor + "/default";
+ auto vibBinder = ndk::SpAIBinder(AServiceManager_getService(instance.c_str()));
+ return aidl::android::hardware::vibrator::IVibrator::fromBinder(vibBinder);
}
template <typename I>
+using shared_ptr = std::result_of_t<decltype(getService<I>)&()>;
+
+template <typename I>
class HalWrapper {
public:
static std::unique_ptr<HalWrapper> Create() {
@@ -70,10 +74,10 @@
}
private:
- HalWrapper(sp<I>&& hal) : mHal(std::move(hal)) {}
+ HalWrapper(shared_ptr<I>&& hal) : mHal(std::move(hal)) {}
private:
- sp<I> mHal;
+ shared_ptr<I> mHal;
};
template <typename I>
@@ -95,7 +99,7 @@
namespace V1_1 = ::android::hardware::vibrator::V1_1;
namespace V1_2 = ::android::hardware::vibrator::V1_2;
namespace V1_3 = ::android::hardware::vibrator::V1_3;
-namespace aidl = ::android::hardware::vibrator;
+namespace aidl = ::aidl::android::hardware::vibrator;
} // namespace vibrator
} // namespace idlcli
diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp
index 705e40b..4721a5f 100644
--- a/cmds/idlcli/vibrator/CommandCompose.cpp
+++ b/cmds/idlcli/vibrator/CommandCompose.cpp
@@ -80,7 +80,7 @@
Status ret;
if (auto hal = getHal<aidl::IVibrator>()) {
auto status = hal->call(&aidl::IVibrator::compose, mComposite, nullptr);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else {
return UNAVAILABLE;
diff --git a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
index 30d8587..303a989 100644
--- a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
@@ -48,7 +48,7 @@
if (auto hal = getHal<aidl::IVibrator>()) {
auto status = hal->call(&aidl::IVibrator::getCapabilities, &cap);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else {
return UNAVAILABLE;
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
index b414307..10508bd 100644
--- a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
@@ -50,7 +50,7 @@
if (auto hal = getHal<aidl::IVibrator>()) {
auto status = hal->call(&aidl::IVibrator::getCompositionDelayMax, &maxDelayMs);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else {
return UNAVAILABLE;
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
index 360fc9d..900cb18 100644
--- a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
@@ -50,7 +50,7 @@
if (auto hal = getHal<aidl::IVibrator>()) {
auto status = hal->call(&aidl::IVibrator::getCompositionSizeMax, &maxSize);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else {
return UNAVAILABLE;
diff --git a/cmds/idlcli/vibrator/CommandOff.cpp b/cmds/idlcli/vibrator/CommandOff.cpp
index 53fada0..cedb9fe 100644
--- a/cmds/idlcli/vibrator/CommandOff.cpp
+++ b/cmds/idlcli/vibrator/CommandOff.cpp
@@ -47,7 +47,7 @@
if (auto hal = getHal<aidl::IVibrator>()) {
auto status = hal->call(&aidl::IVibrator::off);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else if (auto hal = getHal<V1_0::IVibrator>()) {
auto status = hal->call(&V1_0::IVibrator::off);
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
index ccb3c19..4e7e493 100644
--- a/cmds/idlcli/vibrator/CommandOn.cpp
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -55,7 +55,7 @@
if (auto hal = getHal<aidl::IVibrator>()) {
auto status = hal->call(&aidl::IVibrator::on, mDuration, nullptr);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else if (auto hal = getHal<V1_0::IVibrator>()) {
auto status = hal->call(&V1_0::IVibrator::on, mDuration);
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
index 58d4e0a..69c7e37 100644
--- a/cmds/idlcli/vibrator/CommandPerform.cpp
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -99,7 +99,7 @@
auto status =
hal->call(&aidl::IVibrator::perform, static_cast<aidl::Effect>(mEffect),
static_cast<aidl::EffectStrength>(mStrength), nullptr, &aidlLengthMs);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
lengthMs = static_cast<uint32_t>(aidlLengthMs);
ret = status.isOk() ? OK : ERROR;
} else {
diff --git a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
index 33d7eed..8b8058c 100644
--- a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
+++ b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
@@ -56,7 +56,7 @@
if (auto hal = getHal<aidl::IVibrator>()) {
auto status = hal->call(&aidl::IVibrator::setAmplitude,
static_cast<float>(mAmplitude) / UINT8_MAX);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else if (auto hal = getHal<V1_0::IVibrator>()) {
auto status = hal->call(&V1_0::IVibrator::setAmplitude, mAmplitude);
diff --git a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
index 5bc827e..1795793 100644
--- a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
+++ b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
@@ -53,7 +53,7 @@
if (auto hal = getHal<aidl::IVibrator>()) {
auto status = hal->call(&aidl::IVibrator::setExternalControl, mEnable);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else if (auto hal = getHal<V1_3::IVibrator>()) {
auto status = hal->call(&V1_3::IVibrator::setExternalControl, mEnable);
diff --git a/cmds/installd/.gitignore b/cmds/installd/.gitignore
new file mode 100644
index 0000000..abc921c
--- /dev/null
+++ b/cmds/installd/.gitignore
@@ -0,0 +1,2 @@
+# ignore the files generated by intellij
+.idea
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 75dec37..8ff4dd8 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -13,6 +13,7 @@
srcs: [
"CacheItem.cpp",
"CacheTracker.cpp",
+ "CrateManager.cpp",
"InstalldNativeService.cpp",
"QuotaUtils.cpp",
"dexopt.cpp",
@@ -163,6 +164,7 @@
name: "installd_aidl",
srcs: [
"binder/android/os/IInstalld.aidl",
+ "binder/android/os/storage/CrateMetadata.aidl",
],
path: "binder",
}
diff --git a/cmds/installd/CrateManager.cpp b/cmds/installd/CrateManager.cpp
new file mode 100644
index 0000000..344aefb
--- /dev/null
+++ b/cmds/installd/CrateManager.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+#include "CrateManager.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android/log.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <string>
+#include <utils.h>
+
+#include "utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+CrateManager::CrateManager(const char* uuid, userid_t userId, const std::string& packageName) {
+ mPackageName = packageName;
+ mRoot = create_data_user_ce_package_path(uuid, userId, (const char*)packageName.c_str());
+ mCratedFoldersRoot = StringPrintf("%s/crates", mRoot.c_str());
+}
+
+CrateManager::~CrateManager() {}
+
+static std::string getValidatedCratedPath(std::string path) {
+ size_t pos = path.rfind("/");
+ if (pos == std::string::npos) {
+ return path;
+ }
+
+ return path.substr(pos + 1, path.length());
+}
+
+void CrateManager::traverseChildDir(const std::string& targetDir,
+ std::function<void(FTSENT*)>& onVisitChildDir) {
+ char* argv[] = {(char*)targetDir.c_str(), nullptr};
+ FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr);
+ if (fts == nullptr) {
+ PLOG(WARNING) << "Failed to fts_open " << targetDir;
+ return;
+ }
+
+ FTSENT* p;
+ while ((p = fts_read(fts)) != nullptr) {
+ switch (p->fts_info) {
+ case FTS_D:
+ if (p->fts_level == 1) {
+ onVisitChildDir(p);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (p->fts_level == 1) {
+ fts_set(fts, p, FTS_SKIP);
+ }
+ }
+ fts_close(fts);
+}
+
+void CrateManager::traverseAllPackagesForUser(
+ const std::unique_ptr<std::string>& uuid, userid_t userId,
+ std::function<void(FTSENT*)>& onHandlingPackage) {
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+ auto ce_path = create_data_user_ce_path(uuid_, userId);
+ traverseChildDir(ce_path, onHandlingPackage);
+}
+
+void CrateManager::createCrate(
+ CratedFolder cratedFolder,
+ std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) {
+ const char* path = cratedFolder->fts_path;
+ if (path == nullptr || *path == '\0') {
+ return;
+ }
+
+ std::unique_ptr<CrateMetadata> crateMetadata = std::make_unique<CrateMetadata>();
+ crateMetadata->uid = cratedFolder->fts_statp->st_uid;
+ crateMetadata->packageName = mPackageName;
+ crateMetadata->id = getValidatedCratedPath(path);
+
+ onCreateCrate(cratedFolder, crateMetadata);
+}
+
+void CrateManager::traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) {
+ std::function<void(FTSENT*)> onVisitCrateDir = [&](FTSENT* cratedFolder) -> void {
+ createCrate(cratedFolder, onCreateCrate);
+ };
+ traverseChildDir(mCratedFoldersRoot, onVisitCrateDir);
+}
+
+#if CRATE_DEBUG
+void CrateManager::dump(std::unique_ptr<CrateMetadata>& CrateMetadata) {
+ LOG(DEBUG) << "CrateMetadata = {"
+ << "uid : \"" << CrateMetadata->uid
+ << "\", packageName : \"" << CrateMetadata->packageName
+ << "\", id : \"" << CrateMetadata->id
+ << "\"}";
+}
+#endif
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/CrateManager.h b/cmds/installd/CrateManager.h
new file mode 100644
index 0000000..1776622
--- /dev/null
+++ b/cmds/installd/CrateManager.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_INSTALLD_CRATE_INFO_MANAGER_H
+#define ANDROID_INSTALLD_CRATE_INFO_MANAGER_H
+
+#include <android/os/storage/CrateMetadata.h>
+#include <cutils/multiuser.h>
+#include <fts.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#ifndef CRATE_DEBUG
+#define CRATE_DEBUG 1
+#endif
+
+namespace android {
+namespace installd {
+
+using android::os::storage::CrateMetadata;
+
+/**
+ * The crated folder actually is a folder that is the first level child director. In order to
+ * distingish between the crated folder and the other FTSENT*, to define the type "CratedFolder"
+ * make the code easy to identify the difference.
+ */
+typedef FTSENT* CratedFolder;
+
+/**
+ * In order to give the users more fine-grained files controlling, the crate information can help
+ * applications' developers to show the more detail information to the users. The crate information
+ * include the Label, Expiration etc..
+ */
+class CrateManager {
+public:
+ CrateManager(const char* uuid, userid_t userId, const std::string& packageName);
+ ~CrateManager();
+
+ void traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate);
+
+ static void traverseChildDir(const std::string& targetDir,
+ std::function<void(FTSENT*)>& onVisitChildDir);
+
+ static void traverseAllPackagesForUser(
+ const std::unique_ptr<std::string>& uuid,
+ userid_t userId,
+ std::function<void(FTSENT*)>& onHandlingPackage);
+
+#if CRATE_DEBUG
+ static void dump(std::unique_ptr<CrateMetadata>& CrateMetadata);
+#endif
+private:
+ std::string mRoot;
+ std::string mCratedFoldersRoot;
+ std::string mPackageName;
+
+ void createCrate(
+ CratedFolder cratedFolder,
+ std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate);
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // ANDROID_INSTALLD_CRATE_INFO_MANAGER_H
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index f5a53f1..f8a68b4 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -31,6 +31,7 @@
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <sys/mount.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
@@ -65,6 +66,7 @@
#include "view_compiler.h"
#include "CacheTracker.h"
+#include "CrateManager.h"
#include "MatchExtensionGen.h"
#include "QuotaUtils.h"
@@ -86,6 +88,9 @@
static constexpr const char* kCpPath = "/system/bin/cp";
static constexpr const char* kXattrDefault = "user.default";
+static constexpr const char* kDataMirrorCePath = "/data_mirror/data_ce";
+static constexpr const char* kDataMirrorDePath = "/data_mirror/data_de";
+
static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
static constexpr const char* PKG_LIB_POSTFIX = "/lib";
@@ -99,6 +104,13 @@
static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
static constexpr const char* kFuseProp = "persist.sys.fuse";
+/**
+ * Property to control if app data isolation is enabled.
+ */
+static constexpr const char* kAppDataIsolationEnabledProperty = "persist.zygote.app_data_isolation";
+
+static std::atomic<bool> sAppDataIsolationEnabled(false);
+
namespace {
constexpr const char* kDump = "android.permission.DUMP";
@@ -258,6 +270,8 @@
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
ps->giveThreadPoolName();
+ sAppDataIsolationEnabled = android::base::GetBoolProperty(
+ kAppDataIsolationEnabledProperty, false);
return android::OK;
}
@@ -450,9 +464,12 @@
// And return the CE inode of the top-level data directory so we can
// clear contents while CE storage is locked
- if ((_aidl_return != nullptr)
- && get_path_inode(path, reinterpret_cast<ino_t*>(_aidl_return)) != 0) {
- return error("Failed to get_path_inode for " + path);
+ if (_aidl_return != nullptr) {
+ ino_t result;
+ if (get_path_inode(path, &result) != 0) {
+ return error("Failed to get_path_inode for " + path);
+ }
+ *_aidl_return = static_cast<uint64_t>(result);
}
}
if (flags & FLAG_STORAGE_DE) {
@@ -2029,6 +2046,82 @@
return ok();
}
+binder::Status InstalldNativeService::getAppCrates(
+ const std::unique_ptr<std::string>& uuid,
+ const std::vector<std::string>& packageNames, int32_t userId,
+ std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ for (const auto& packageName : packageNames) {
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ }
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>();
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+ std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate =
+ [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void {
+ if (cratedFolder == nullptr) {
+ return;
+ }
+ retVector->push_back(std::move(crateMetadata));
+ };
+
+ for (const auto& packageName : packageNames) {
+#if CRATE_DEBUG
+ LOG(DEBUG) << "packageName = " << packageName;
+#endif
+ auto crateManager = std::make_unique<CrateManager>(uuid_, userId, packageName);
+ crateManager->traverseAllCrates(onCreateCrate);
+ }
+
+#if CRATE_DEBUG
+ LOG(WARNING) << "retVector->size() =" << retVector->size();
+ for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) {
+ CrateManager::dump(*iter);
+ }
+#endif
+
+ *_aidl_return = std::move(retVector);
+ return ok();
+}
+
+binder::Status InstalldNativeService::getUserCrates(
+ const std::unique_ptr<std::string>& uuid, int32_t userId,
+ std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>();
+
+ std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate =
+ [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void {
+ if (cratedFolder == nullptr) {
+ return;
+ }
+ retVector->push_back(std::move(crateMetadata));
+ };
+
+ std::function<void(FTSENT*)> onHandingPackage = [&](FTSENT* packageDir) -> void {
+ auto crateManager = std::make_unique<CrateManager>(uuid_, userId, packageDir->fts_name);
+ crateManager->traverseAllCrates(onCreateCrate);
+ };
+ CrateManager::traverseAllPackagesForUser(uuid, userId, onHandingPackage);
+
+#if CRATE_DEBUG
+ LOG(DEBUG) << "retVector->size() =" << retVector->size();
+ for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) {
+ CrateManager::dump(*iter);
+ }
+#endif
+
+ *_aidl_return = std::move(retVector);
+ return ok();
+}
+
binder::Status InstalldNativeService::setAppQuota(const std::unique_ptr<std::string>& uuid,
int32_t userId, int32_t appId, int64_t cacheQuota) {
ENFORCE_UID(AID_SYSTEM);
@@ -2421,7 +2514,7 @@
#endif
binder::Status InstalldNativeService::installApkVerity(const std::string& filePath,
- const ::android::base::unique_fd& verityInputAshmem, int32_t contentSize) {
+ android::base::unique_fd verityInputAshmem, int32_t contentSize) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PATH(filePath);
std::lock_guard<std::recursive_mutex> lock(mLock);
@@ -2613,6 +2706,89 @@
return ok();
}
+// Mount volume's CE and DE storage to mirror
+binder::Status InstalldNativeService::onPrivateVolumeMounted(
+ const std::unique_ptr<std::string>& uuid) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ if (!sAppDataIsolationEnabled) {
+ return ok();
+ }
+ if (!uuid) {
+ return error("Should not happen, mounting uuid == null");
+ }
+
+ const char* uuid_ = uuid->c_str();
+ // Mount CE mirror
+ std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+ if (fs_prepare_dir(mirrorVolCePath.c_str(), 0700, AID_ROOT, AID_ROOT) != 0) {
+ return error("Failed to create CE mirror");
+ }
+ auto cePath = StringPrintf("%s/user_ce", create_data_path(uuid_).c_str());
+ if (TEMP_FAILURE_RETRY(mount(cePath.c_str(), mirrorVolCePath.c_str(), NULL,
+ MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC, nullptr)) == -1) {
+ return error("Failed to mount " + mirrorVolCePath);
+ }
+
+ // Mount DE mirror
+ std::string mirrorVolDePath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
+ if (fs_prepare_dir(mirrorVolDePath.c_str(), 0700, AID_ROOT, AID_ROOT) != 0) {
+ return error("Failed to create DE mirror");
+ }
+ auto dePath = StringPrintf("%s/user_de", create_data_path(uuid_).c_str());
+ if (TEMP_FAILURE_RETRY(mount(dePath.c_str(), mirrorVolDePath.c_str(), NULL,
+ MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC, nullptr)) == -1) {
+ return error("Failed to mount " + mirrorVolDePath);
+ }
+ return ok();
+}
+
+// Unmount volume's CE and DE storage from mirror
+binder::Status InstalldNativeService::onPrivateVolumeRemoved(
+ const std::unique_ptr<std::string>& uuid) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ if (!sAppDataIsolationEnabled) {
+ return ok();
+ }
+ if (!uuid) {
+ // It happens when private volume failed to mount.
+ LOG(INFO) << "Ignore unmount uuid=null";
+ return ok();
+ }
+ const char* uuid_ = uuid->c_str();
+
+ binder::Status res = ok();
+
+ std::string mirrorCeVolPath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
+ std::string mirrorDeVolPath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
+
+ // Unmount CE storage
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+ if (TEMP_FAILURE_RETRY(umount(mirrorCeVolPath.c_str())) != 0) {
+ if (errno != ENOENT) {
+ res = error(StringPrintf("Failed to umount %s %s", mirrorCeVolPath.c_str(),
+ strerror(errno)));
+ }
+ }
+ if (delete_dir_contents_and_dir(mirrorCeVolPath, true) != 0) {
+ res = error("Failed to delete " + mirrorCeVolPath);
+ }
+
+ // Unmount DE storage
+ if (TEMP_FAILURE_RETRY(umount(mirrorDeVolPath.c_str())) != 0) {
+ if (errno != ENOENT) {
+ res = error(StringPrintf("Failed to umount %s %s", mirrorDeVolPath.c_str(),
+ strerror(errno)));
+ }
+ }
+ if (delete_dir_contents_and_dir(mirrorDeVolPath, true) != 0) {
+ res = error("Failed to delete " + mirrorDeVolPath);
+ }
+ return res;
+}
+
std::string InstalldNativeService::findDataMediaPath(
const std::unique_ptr<std::string>& uuid, userid_t userid) {
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index ef91bf8..bf11002 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -81,6 +81,16 @@
int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
std::vector<int64_t>* _aidl_return);
+ binder::Status getAppCrates(const std::unique_ptr<std::string>& uuid,
+ const std::vector<std::string>& packageNames,
+ int32_t userId,
+ std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>*
+ _aidl_return);
+ binder::Status getUserCrates(
+ const std::unique_ptr<std::string>& uuid, int32_t userId,
+ std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>*
+ _aidl_return);
+
binder::Status setAppQuota(const std::unique_ptr<std::string>& uuid,
int32_t userId, int32_t appId, int64_t cacheQuota);
@@ -133,7 +143,7 @@
binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
const std::unique_ptr<std::string>& outputPath);
binder::Status installApkVerity(const std::string& filePath,
- const ::android::base::unique_fd& verityInput, int32_t contentSize);
+ android::base::unique_fd verityInput, int32_t contentSize);
binder::Status assertFsverityRootHashMatches(const std::string& filePath,
const std::vector<uint8_t>& expectedHash);
binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
@@ -146,6 +156,8 @@
binder::Status invalidateMounts();
binder::Status isQuotaSupported(const std::unique_ptr<std::string>& volumeUuid,
bool* _aidl_return);
+ binder::Status onPrivateVolumeMounted(const std::unique_ptr<std::string>& volumeUuid);
+ binder::Status onPrivateVolumeRemoved(const std::unique_ptr<std::string>& volumeUuid);
binder::Status prepareAppProfile(const std::string& packageName,
int32_t userId, int32_t appId, const std::string& profileName,
diff --git a/cmds/installd/QuotaUtils.cpp b/cmds/installd/QuotaUtils.cpp
index b238dd3..a71e01c 100644
--- a/cmds/installd/QuotaUtils.cpp
+++ b/cmds/installd/QuotaUtils.cpp
@@ -61,6 +61,10 @@
std::getline(in, target, ' ');
std::getline(in, ignored);
+ if (target.compare(0, 13, "/data_mirror/") == 0) {
+ continue;
+ }
+
if (source.compare(0, 11, "/dev/block/") == 0) {
struct dqblk dq;
if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), source.c_str(), 0,
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 6cc4bde..891b26d 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -40,6 +40,14 @@
long[] getUserSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds);
long[] getExternalSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds);
+ @nullable
+ android.os.storage.CrateMetadata[] getAppCrates(
+ @nullable @utf8InCpp String uuid, in @utf8InCpp String[] packageNames,
+ int userId);
+ @nullable
+ android.os.storage.CrateMetadata[] getUserCrates(
+ @nullable @utf8InCpp String uuid, int userId);
+
void setAppQuota(@nullable @utf8InCpp String uuid, int userId, int appId, long cacheQuota);
void moveCompleteApp(@nullable @utf8InCpp String fromUuid, @nullable @utf8InCpp String toUuid,
@@ -109,6 +117,8 @@
int appId, @utf8InCpp String seInfo, int user, int snapshotId, int storageflags);
void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
int userId, long ceSnapshotInode, int snapshotId, int storageFlags);
+ void onPrivateVolumeMounted(@nullable @utf8InCpp String volumeUuid);
+ void onPrivateVolumeRemoved(@nullable @utf8InCpp String volumeUuid);
void migrateLegacyObbData();
diff --git a/cmds/installd/binder/android/os/storage/CrateMetadata.aidl b/cmds/installd/binder/android/os/storage/CrateMetadata.aidl
new file mode 100644
index 0000000..bd6d12d
--- /dev/null
+++ b/cmds/installd/binder/android/os/storage/CrateMetadata.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.storage;
+
+/** {@hide} */
+parcelable CrateMetadata {
+ /**
+ * To tell which uid the crate belong to.
+ * <p>Because installd query all of crates in specified userId, the install may return the list
+ * whose elements have the same crate id but different uid and package name.
+ * It needs to tell the caller the difference between these elements.
+ */
+ int uid;
+
+ /**
+ * To tell which the package the crate belong to.
+ * <p>Because installd query all of crates in specified uid, the install may return the list
+ * whose elements have the same uid and crate id but different package name.
+ * It needs to tell the caller the difference between these elements.
+ */
+ @utf8InCpp String packageName;
+
+ /**
+ * To tell the crate id that is the child directory/folder name in crates
+ * root.
+ */
+ @utf8InCpp String id;
+}
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 4eb1df0..6012822 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -278,6 +278,15 @@
return "/data/dalvik-cache";
}
+std::string create_system_user_ce_path(userid_t userId) {
+ return StringPrintf("%s/system_ce/%u", create_data_path(nullptr).c_str(), userId);
+}
+
+std::string create_system_user_ce_package_path(userid_t userId, const char* package_name) {
+ check_package_name(package_name);
+ return StringPrintf("%s/%s", create_system_user_ce_path(userId).c_str(), package_name);
+}
+
// Keep profile paths in sync with ActivityThread and LoadedApk.
const std::string PROFILE_EXT = ".prof";
const std::string CURRENT_PROFILE_EXT = ".cur";
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 6a42026..6a39adc 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -82,6 +82,10 @@
std::string create_data_dalvik_cache_path();
+std::string create_system_user_ce_path(userid_t userId);
+
+std::string create_system_user_ce_package_path(userid_t userId, const char* package_name);
+
std::string create_primary_cur_profile_dir_path(userid_t userid);
std::string create_primary_current_profile_package_dir_path(
userid_t user, const std::string& package_name);
diff --git a/data/etc/android.hardware.reboot_escrow.xml b/data/etc/android.hardware.reboot_escrow.xml
new file mode 100644
index 0000000..5db9f4c
--- /dev/null
+++ b/data/etc/android.hardware.reboot_escrow.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!--
+ This is the feature indicating that the device has support for the
+ RebootEscrow HAL.
+-->
+
+<permissions>
+ <feature name="android.hardware.reboot_escrow" />
+</permissions>
diff --git a/include/android/configuration.h b/include/android/configuration.h
index 3310722..05f4340 100644
--- a/include/android/configuration.h
+++ b/include/android/configuration.h
@@ -645,10 +645,14 @@
*/
void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong);
+#if __ANDROID_API__ >= 30
/**
* Return the current ACONFIGURATION_SCREENROUND_* set in the configuration.
+ *
+ * Available since API level 30.
*/
-int32_t AConfiguration_getScreenRound(AConfiguration* config);
+int32_t AConfiguration_getScreenRound(AConfiguration* config) __INTRODUCED_IN(30);
+#endif
/**
* Set the current screen round in the configuration.
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index 4f0b7d3..ca9c608 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -44,16 +44,16 @@
} // namespace
static String16 _appops("appops");
-static pthread_mutex_t gTokenMutex = PTHREAD_MUTEX_INITIALIZER;
-static sp<IBinder> gToken;
+static pthread_mutex_t gClientIdMutex = PTHREAD_MUTEX_INITIALIZER;
+static sp<IBinder> gClientId;
-static const sp<IBinder>& getToken(const sp<IAppOpsService>& service) {
- pthread_mutex_lock(&gTokenMutex);
- if (gToken == nullptr || gToken->pingBinder() != NO_ERROR) {
- gToken = service->getToken(new BBinder());
+static const sp<IBinder>& getClientId() {
+ pthread_mutex_lock(&gClientIdMutex);
+ if (gClientId == nullptr) {
+ gClientId = new BBinder();
}
- pthread_mutex_unlock(&gTokenMutex);
- return gToken;
+ pthread_mutex_unlock(&gClientIdMutex);
+ return gClientId;
}
thread_local uint64_t notedAppOpsInThisBinderTransaction[2];
@@ -144,7 +144,7 @@
const String16& message) {
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
- ? service->startOperation(getToken(service), op, uid, callingPackage,
+ ? service->startOperation(getClientId(), op, uid, callingPackage,
featureId, startIfModeDefault) : APP_OPS_MANAGER_UNAVAILABLE_MODE;
if (mode == AppOpsManager::MODE_ALLOWED) {
@@ -162,7 +162,7 @@
const std::unique_ptr<String16>& callingFeatureId) {
sp<IAppOpsService> service = getService();
if (service != nullptr) {
- service->finishOperation(getToken(service), op, uid, callingPackage, callingFeatureId);
+ service->finishOperation(getClientId(), op, uid, callingPackage, callingFeatureId);
}
}
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index b85a5f2..7384466 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -106,16 +106,6 @@
remote()->transact(STOP_WATCHING_MODE_TRANSACTION, data, &reply);
}
- virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) {
- Parcel data, reply;
- data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
- data.writeStrongBinder(clientToken);
- remote()->transact(GET_TOKEN_TRANSACTION, data, &reply);
- // fail on exception
- if (reply.readExceptionCode() != 0) return nullptr;
- return reply.readStrongBinder();
- }
-
virtual int32_t permissionToOpCode(const String16& permission) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
@@ -251,14 +241,6 @@
reply->writeNoException();
return NO_ERROR;
} break;
- case GET_TOKEN_TRANSACTION: {
- CHECK_INTERFACE(IAppOpsService, data, reply);
- sp<IBinder> clientToken = data.readStrongBinder();
- sp<IBinder> token = getToken(clientToken);
- reply->writeNoException();
- reply->writeStrongBinder(token);
- return NO_ERROR;
- } break;
case PERMISSION_TO_OP_CODE_TRANSACTION: {
CHECK_INTERFACE(IAppOpsService, data, reply);
String16 permission = data.readString16();
diff --git a/libs/binder/include/binder/Enums.h b/libs/binder/include/binder/Enums.h
new file mode 100644
index 0000000..aec6f70
--- /dev/null
+++ b/libs/binder/include/binder/Enums.h
@@ -0,0 +1,41 @@
+/*
+ * 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 <iterator>
+#include <type_traits>
+
+namespace android {
+
+namespace internal {
+
+// Never instantiated. Used as a placeholder for template variables.
+template <typename T>
+struct invalid_type;
+
+// AIDL generates specializations of this for enums.
+template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>>
+constexpr invalid_type<EnumType> enum_values;
+} // namespace internal
+
+// Usage: for (const auto v : enum_range<EnumType>() ) { ... }
+template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>>
+struct enum_range {
+ constexpr auto begin() const { return std::begin(internal::enum_values<EnumType>); }
+ constexpr auto end() const { return std::end(internal::enum_values<EnumType>); }
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index 15ba005..68a917e 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -45,7 +45,6 @@
virtual void startWatchingMode(int32_t op, const String16& packageName,
const sp<IAppOpsCallback>& callback) = 0;
virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
- virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) = 0;
virtual int32_t permissionToOpCode(const String16& permission) = 0;
virtual int32_t checkAudioOperation(int32_t code, int32_t usage,int32_t uid,
const String16& packageName) = 0;
@@ -62,12 +61,11 @@
FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3,
START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4,
STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5,
- GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
- PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
- CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
- NOTE_ASYNC_OP_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+9,
- SHOULD_COLLECT_NOTES_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+10,
- SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+11,
+ 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,
};
enum {
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 79d9b79..3a401ad 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -253,7 +253,6 @@
"android.media.IDrmClient",
"android.media.IEffect",
"android.media.IEffectClient",
- "android.media.IMediaAnalyticsService",
"android.media.IMediaCodecList",
"android.media.IMediaDrmService",
"android.media.IMediaExtractor",
@@ -262,6 +261,7 @@
"android.media.IMediaHTTPService",
"android.media.IMediaLogService",
"android.media.IMediaMetadataRetriever",
+ "android.media.IMediaMetricsService",
"android.media.IMediaPlayer",
"android.media.IMediaPlayerClient",
"android.media.IMediaPlayerService",
diff --git a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
index 946ccb7..2b61cf1 100644
--- a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
@@ -34,6 +34,7 @@
#include <unistd.h>
#include <cstddef>
+#include <string>
namespace ndk {
@@ -228,6 +229,13 @@
*/
const char* getMessage() const { return AStatus_getMessage(get()); }
+ std::string getDescription() const {
+ const char* cStr = AStatus_getDescription(get());
+ std::string ret = cStr;
+ AStatus_deleteDescription(cStr);
+ return ret;
+ }
+
/**
* Convenience methods for creating scoped statuses.
*/
diff --git a/libs/binder/ndk/include_ndk/android/binder_enums.h b/libs/binder/ndk/include_ndk/android/binder_enums.h
new file mode 100644
index 0000000..ee819c0
--- /dev/null
+++ b/libs/binder/ndk/include_ndk/android/binder_enums.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+/**
+ * @addtogroup NdkBinder
+ * @{
+ */
+
+/**
+ * @file binder_enums.h
+ * @brief Helpers for AIDL enum types.
+ */
+
+#pragma once
+
+#include <iterator>
+#include <type_traits>
+
+namespace ndk {
+
+namespace internal {
+/**
+ * Never instantiated. Used as a placeholder for template variables.
+ */
+template <typename T>
+struct invalid_type;
+
+/**
+ * AIDL generates specializations of this for enums.
+ */
+template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>>
+constexpr invalid_type<EnumType> enum_values;
+} // namespace internal
+
+/**
+ * Iterable interface to enumerate all values of AIDL enum types.
+ */
+template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>>
+struct enum_range {
+ /**
+ * Return an iterator pointing to the first enum value.
+ */
+ constexpr auto begin() const { return std::begin(internal::enum_values<EnumType>); }
+ /**
+ * Return an iterator pointing to one past the last enum value.
+ */
+ constexpr auto end() const { return std::end(internal::enum_values<EnumType>); }
+};
+
+} // namespace ndk
+
+/** @} */
\ No newline at end of file
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
index 7871667..df5df13 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
@@ -421,13 +421,76 @@
}
/**
+ * Convenience API for writing a non-null parcelable.
+ */
+template <typename P>
+static inline binder_status_t AParcel_writeParcelable(AParcel* parcel, const P& p) {
+ binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
+ if (status != STATUS_OK) {
+ return status;
+ }
+ return p.writeToParcel(parcel);
+}
+
+/**
+ * Convenience API for reading a non-null parcelable.
+ */
+template <typename P>
+static inline binder_status_t AParcel_readParcelable(const AParcel* parcel, P* p) {
+ int32_t null;
+ binder_status_t status = AParcel_readInt32(parcel, &null);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ if (null == 0) {
+ return STATUS_UNEXPECTED_NULL;
+ }
+ return p->readFromParcel(parcel);
+}
+
+/**
+ * Convenience API for writing a nullable parcelable.
+ */
+template <typename P>
+static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel,
+ const std::optional<P>& p) {
+ if (p == std::nullopt) {
+ return AParcel_writeInt32(parcel, 0); // null
+ }
+ binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
+ if (status != STATUS_OK) {
+ return status;
+ }
+ return p->writeToParcel(parcel);
+}
+
+/**
+ * Convenience API for reading a nullable parcelable.
+ */
+template <typename P>
+static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel,
+ std::optional<P>* p) {
+ int32_t null;
+ binder_status_t status = AParcel_readInt32(parcel, &null);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ if (null == 0) {
+ *p = std::nullopt;
+ return STATUS_OK;
+ }
+ *p = std::optional<P>(P{});
+ return (*p)->readFromParcel(parcel);
+}
+
+/**
* Writes a parcelable object of type P inside a std::vector<P> at index 'index' to 'parcel'.
*/
template <typename P>
binder_status_t AParcel_writeStdVectorParcelableElement(AParcel* parcel, const void* vectorData,
size_t index) {
const std::vector<P>* vector = static_cast<const std::vector<P>*>(vectorData);
- return vector->at(index).writeToParcel(parcel);
+ return AParcel_writeParcelable(parcel, vector->at(index));
}
/**
@@ -437,7 +500,7 @@
binder_status_t AParcel_readStdVectorParcelableElement(const AParcel* parcel, void* vectorData,
size_t index) {
std::vector<P>* vector = static_cast<std::vector<P>*>(vectorData);
- return vector->at(index).readFromParcel(parcel);
+ return AParcel_readParcelable(parcel, &vector->at(index));
}
/**
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index 78d70f8..ab9a144 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -247,6 +247,25 @@
const char* AStatus_getMessage(const AStatus* status) __INTRODUCED_IN(29);
/**
+ * Get human-readable description for debugging.
+ *
+ * Available since API level 30.
+ *
+ * \param status the status being queried.
+ *
+ * \return a description, must be deleted with AStatus_deleteDescription.
+ */
+__attribute__((warn_unused_result)) const char* AStatus_getDescription(const AStatus* status)
+ __INTRODUCED_IN(30);
+
+/**
+ * Delete description.
+ *
+ * \param description value from AStatus_getDescription
+ */
+void AStatus_deleteDescription(const char* description) __INTRODUCED_IN(30);
+
+/**
* Deletes memory associated with the status instance.
*
* Available since API level 29.
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index d59d6e4..71d8103 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -103,6 +103,8 @@
global:
AIBinder_getExtension;
AIBinder_setExtension;
+ AStatus_getDescription;
+ AStatus_deleteDescription;
AIBinder_markSystemStability; # apex
AIBinder_markVendorStability; # llndk
diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp
index 1f75b0b..87e1341 100644
--- a/libs/binder/ndk/status.cpp
+++ b/libs/binder/ndk/status.cpp
@@ -66,6 +66,17 @@
return status->get()->exceptionMessage().c_str();
}
+const char* AStatus_getDescription(const AStatus* status) {
+ android::String8 description = status->get()->toString8();
+ char* cStr = new char[description.size() + 1];
+ memcpy(cStr, description.c_str(), description.size() + 1);
+ return cStr;
+}
+
+void AStatus_deleteDescription(const char* description) {
+ delete[] const_cast<char*>(description);
+}
+
void AStatus_delete(AStatus* status) {
delete status;
}
diff --git a/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp b/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp
index d3ccdc2..ad78e31 100644
--- a/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp
+++ b/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp
@@ -105,7 +105,8 @@
std::string outString;
ScopedAStatus status = server->RepeatString("foo", &outString);
- EXPECT_EQ(STATUS_OK, AStatus_getExceptionCode(status.get())) << serviceName;
+ EXPECT_EQ(STATUS_OK, AStatus_getExceptionCode(status.get()))
+ << serviceName << " " << status.getDescription();
EXPECT_EQ("foo", outString) << serviceName;
}
}
diff --git a/libs/gralloc/OWNERS b/libs/gralloc/OWNERS
new file mode 100644
index 0000000..67743cd
--- /dev/null
+++ b/libs/gralloc/OWNERS
@@ -0,0 +1,2 @@
+marissaw@google.com
+vhau@google.com
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index cb5305f..e1693cf 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -41,6 +41,7 @@
export_shared_lib_headers: [
"android.hardware.graphics.mapper@4.0",
+ "libhidlbase",
"vintf-graphics-common-ndk_platform",
],
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index fd91b15..4851b44 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "libgralloctypes"
+
#include <cstring>
#include <cinttypes>
#include <limits>
@@ -28,6 +30,7 @@
using aidl::android::hardware::graphics::common::BlendMode;
using aidl::android::hardware::graphics::common::ChromaSiting;
using aidl::android::hardware::graphics::common::Compression;
+using aidl::android::hardware::graphics::common::Cta861_3;
using aidl::android::hardware::graphics::common::Dataspace;
using aidl::android::hardware::graphics::common::ExtendableType;
using aidl::android::hardware::graphics::common::Interlaced;
@@ -35,7 +38,9 @@
using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
using aidl::android::hardware::graphics::common::Rect;
+using aidl::android::hardware::graphics::common::Smpte2086;
using aidl::android::hardware::graphics::common::StandardMetadataType;
+using aidl::android::hardware::graphics::common::XyColor;
using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
@@ -65,6 +70,7 @@
return BAD_VALUE;
}
mVec->resize(mNeededResize);
+ mResized = true;
return NO_ERROR;
}
@@ -72,11 +78,17 @@
if (!mVec) {
return BAD_VALUE;
}
- if (mVec->size() == 0) {
+ if (!mResized) {
if (hasAdditionOverflow(mNeededResize, size)) {
clear();
return BAD_VALUE;
}
+ /**
+ * Update mNeededResize and return NO_ERROR here because if (!mResized), the
+ * caller hasn't called resize(). No data will be written into the mVec until
+ * the caller resizes. We can't resize here for the caller because hidl_vec::resize()
+ * allocates a new backing array every time.
+ */
mNeededResize += size;
return NO_ERROR;
}
@@ -97,12 +109,14 @@
mVec->resize(0);
}
mNeededResize = 0;
+ mResized = false;
mOffset = 0;
}
private:
hidl_vec<uint8_t>* mVec;
size_t mNeededResize = 0;
+ size_t mResized = false;
size_t mOffset = 0;
};
@@ -142,6 +156,13 @@
if (!mVec) {
return false;
}
+ return mVec->size() > mOffset;
+ }
+
+ size_t getRemainingSize() {
+ if (!mVec) {
+ return 0;
+ }
return mVec->size() - mOffset;
}
@@ -170,6 +191,9 @@
template<class T>
using ErrorHandler = void(*)(T*);
+status_t encodeMetadataType(const MetadataType& input, OutputHidlVec* output);
+status_t validateMetadataType(InputHidlVec* input, const MetadataType& expectedMetadataType);
+
/**
* encode is the main encoding function. It takes in T and uses the encodeHelper function to turn T
* into the hidl_vec byte stream.
@@ -179,9 +203,16 @@
* encodes T into the hidl_vec byte stream.
*/
template <class T>
-status_t encode(const T& input, hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) {
+status_t encode(const MetadataType& metadataType, const T& input, hidl_vec<uint8_t>* output,
+ EncodeHelper<T> encodeHelper) {
OutputHidlVec outputHidlVec{output};
- status_t err = encodeHelper(input, &outputHidlVec);
+
+ status_t err = encodeMetadataType(metadataType, &outputHidlVec);
+ if (err) {
+ return err;
+ }
+
+ err = encodeHelper(input, &outputHidlVec);
if (err) {
return err;
}
@@ -191,19 +222,39 @@
return err;
}
+ err = encodeMetadataType(metadataType, &outputHidlVec);
+ if (err) {
+ return err;
+ }
+
return encodeHelper(input, &outputHidlVec);
}
+template <class T>
+status_t encodeOptional(const MetadataType& metadataType, const std::optional<T>& input,
+ hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) {
+ if (!input) {
+ return NO_ERROR;
+ }
+ return encode(metadataType, *input, output, encodeHelper);
+}
+
/**
* decode is the main decode function. It takes in a hidl_vec and uses the decodeHelper function to
* turn the hidl_vec byte stream into T. If an error occurs, the errorHandler function cleans up
* T.
*/
template <class T>
-status_t decode(const hidl_vec<uint8_t>& input, T* output, DecodeHelper<T> decodeHelper,
- ErrorHandler<T> errorHandler = nullptr) {
+status_t decode(const MetadataType& metadataType, const hidl_vec<uint8_t>& input, T* output,
+ DecodeHelper<T> decodeHelper, ErrorHandler<T> errorHandler = nullptr) {
InputHidlVec inputHidlVec{&input};
- status_t err = decodeHelper(&inputHidlVec, output);
+
+ status_t err = validateMetadataType(&inputHidlVec, metadataType);
+ if (err) {
+ return err;
+ }
+
+ err = decodeHelper(&inputHidlVec, output);
if (err) {
return err;
}
@@ -219,13 +270,32 @@
return NO_ERROR;
}
+template <class T>
+status_t decodeOptional(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ std::optional<T>* output, DecodeHelper<T> decodeHelper) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+ if (input.size() <= 0) {
+ output->reset();
+ return NO_ERROR;
+ }
+ T tmp;
+ status_t err = decode(metadataType, input, &tmp, decodeHelper);
+ if (!err) {
+ *output = tmp;
+ }
+ return err;
+}
+
/**
* Private helper functions
*/
template <class T>
status_t encodeInteger(const T& input, OutputHidlVec* output) {
static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
- std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value);
+ std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
+ std::is_same<T, float>::value || std::is_same<T, double>::value);
if (!output) {
return BAD_VALUE;
}
@@ -237,7 +307,8 @@
template <class T>
status_t decodeInteger(InputHidlVec* input, T* output) {
static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
- std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value);
+ std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
+ std::is_same<T, float>::value || std::is_same<T, double>::value);
if (!output) {
return BAD_VALUE;
}
@@ -256,7 +327,7 @@
return err;
}
- return output->encode(reinterpret_cast<const uint8_t*>(input.c_str()), input.size());
+ return output->encode(reinterpret_cast<const uint8_t*>(input.data()), input.size());
}
status_t decodeString(InputHidlVec* input, std::string* output) {
@@ -266,11 +337,46 @@
int64_t size = 0;
status_t err = decodeInteger<int64_t>(input, &size);
+ if (err) {
+ return err;
+ }
+ if (size < 0) {
+ return BAD_VALUE;
+ }
+
+ return input->decode(output, size);
+}
+
+status_t encodeByteVector(const std::vector<uint8_t>& input, OutputHidlVec* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = encodeInteger<int64_t>(input.size(), output);
+ if (err) {
+ return err;
+ }
+
+ return output->encode(input.data(), input.size());
+}
+
+status_t decodeByteVector(InputHidlVec* input, std::vector<uint8_t>* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ int64_t size = 0;
+ status_t err = decodeInteger<int64_t>(input, &size);
if (err || size < 0) {
return err;
}
- return input->decode(output, size);
+ if (size > input->getRemainingSize()) {
+ return BAD_VALUE;
+ }
+ output->resize(size);
+
+ return input->decode(output->data(), size);
}
status_t encodeExtendableType(const ExtendableType& input, OutputHidlVec* output) {
@@ -309,6 +415,79 @@
output->value = 0;
}
+status_t encodeMetadataType(const MetadataType& input, OutputHidlVec* output) {
+ status_t err = encodeString(input.name, output);
+ if (err) {
+ return err;
+ }
+
+ err = encodeInteger<int64_t>(input.value, output);
+ if (err) {
+ return err;
+ }
+
+ return NO_ERROR;
+}
+
+status_t decodeMetadataType(InputHidlVec* input, MetadataType* output) {
+ std::string name;
+ status_t err = decodeString(input, &name);
+ if (err) {
+ return err;
+ }
+ output->name = name;
+
+ err = decodeInteger<int64_t>(input, &output->value);
+ if (err) {
+ return err;
+ }
+
+ return NO_ERROR;
+}
+
+status_t validateMetadataType(InputHidlVec* input, const MetadataType& expectedMetadataType) {
+ MetadataType receivedMetadataType;
+
+ status_t err = decodeMetadataType(input, &receivedMetadataType);
+ if (err) {
+ return err;
+ }
+
+ if (expectedMetadataType.name != receivedMetadataType.name) {
+ return BAD_VALUE;
+ }
+
+ if (receivedMetadataType.value != expectedMetadataType.value) {
+ return BAD_VALUE;
+ }
+
+ return NO_ERROR;
+}
+
+status_t encodeXyColor(const XyColor& input, OutputHidlVec* output) {
+ status_t err = encodeInteger<float>(input.x, output);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<float>(input.y, output);
+}
+
+status_t decodeXyColor(InputHidlVec* input, XyColor* output) {
+ status_t err = decodeInteger<float>(input, &output->x);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<float>(input, &output->y);
+}
+
+void clearXyColor(XyColor* output) {
+ if (!output) {
+ return;
+ }
+ output->x = 0;
+ output->y = 0;
+}
+
status_t encodeRect(const Rect& input, OutputHidlVec* output) {
status_t err = encodeInteger<int32_t>(static_cast<int32_t>(input.left), output);
if (err) {
@@ -400,13 +579,17 @@
int64_t size = 0;
status_t err = decodeInteger<int64_t>(input, &size);
- if (err || size < 0) {
+ if (err) {
return err;
}
+ if (size < 0 || size > 10000) {
+ return BAD_VALUE;
+ }
- for (int i = 0; i < size; i++) {
- output->emplace_back();
- err = decodePlaneLayoutComponent(input, &output->back());
+ output->resize(size);
+
+ for (auto& planeLayoutComponent : *output) {
+ err = decodePlaneLayoutComponent(input, &planeLayoutComponent);
if (err) {
return err;
}
@@ -525,9 +708,12 @@
status_t decodePlaneLayoutsHelper(InputHidlVec* inputHidlVec, std::vector<PlaneLayout>* outPlaneLayouts) {
int64_t size = 0;
status_t err = decodeInteger<int64_t>(inputHidlVec, &size);
- if (err || size < 0) {
+ if (err) {
return err;
}
+ if (size < 0) {
+ return BAD_VALUE;
+ }
for (size_t i = 0; i < size; i++) {
outPlaneLayouts->emplace_back();
@@ -546,150 +732,327 @@
output->clear();
}
+status_t encodeSmpte2086Helper(const Smpte2086& smpte2086, OutputHidlVec* outOutputHidlVec) {
+ status_t err = encodeXyColor(smpte2086.primaryRed, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ err = encodeXyColor(smpte2086.primaryGreen, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ err = encodeXyColor(smpte2086.primaryBlue, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ err = encodeXyColor(smpte2086.whitePoint, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<float>(smpte2086.maxLuminance, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<float>(smpte2086.minLuminance, outOutputHidlVec);
+}
+
+status_t decodeSmpte2086Helper(InputHidlVec* inputHidlVec, Smpte2086* outSmpte2086) {
+ status_t err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryRed);
+ if (err) {
+ return err;
+ }
+ err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryGreen);
+ if (err) {
+ return err;
+ }
+ err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryBlue);
+ if (err) {
+ return err;
+ }
+ err = decodeXyColor(inputHidlVec, &outSmpte2086->whitePoint);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<float>(inputHidlVec, &outSmpte2086->maxLuminance);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<float>(inputHidlVec, &outSmpte2086->minLuminance);
+}
+
+status_t encodeCta861_3Helper(const Cta861_3& cta861_3, OutputHidlVec* outOutputHidlVec) {
+ status_t err = encodeInteger<float>(cta861_3.maxContentLightLevel, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<float>(cta861_3.maxFrameAverageLightLevel, outOutputHidlVec);
+}
+
+status_t decodeCta861_3Helper(InputHidlVec* inputHidlVec, Cta861_3* outCta861_3) {
+ status_t err = decodeInteger<float>(inputHidlVec, &outCta861_3->maxContentLightLevel);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<float>(inputHidlVec, &outCta861_3->maxFrameAverageLightLevel);
+}
+
/**
* Public API functions
*/
-PlaneLayoutComponentType getStandardPlaneLayoutComponentTypeValue(
- const ExtendableType& planeLayoutComponentType) {
- return static_cast<PlaneLayoutComponentType>(planeLayoutComponentType.value);
-}
status_t encodeBufferId(uint64_t bufferId, hidl_vec<uint8_t>* outBufferId) {
- return encode(bufferId, outBufferId, encodeInteger);
+ return encode(MetadataType_BufferId, bufferId, outBufferId, encodeInteger);
}
status_t decodeBufferId(const hidl_vec<uint8_t>& bufferId, uint64_t* outBufferId) {
- return decode(bufferId, outBufferId, decodeInteger);
+ return decode(MetadataType_BufferId, bufferId, outBufferId, decodeInteger);
}
status_t encodeName(const std::string& name, hidl_vec<uint8_t>* outName) {
- return encode(name, outName, encodeString);
+ return encode(MetadataType_Name, name, outName, encodeString);
}
status_t decodeName(const hidl_vec<uint8_t>& name, std::string* outName) {
- return decode(name, outName, decodeString);
+ return decode(MetadataType_Name, name, outName, decodeString);
}
status_t encodeWidth(uint64_t width, hidl_vec<uint8_t>* outWidth) {
- return encode(width, outWidth, encodeInteger);
+ return encode(MetadataType_Width, width, outWidth, encodeInteger);
}
status_t decodeWidth(const hidl_vec<uint8_t>& width, uint64_t* outWidth) {
- return decode(width, outWidth, decodeInteger);
+ return decode(MetadataType_Width, width, outWidth, decodeInteger);
}
status_t encodeHeight(uint64_t height, hidl_vec<uint8_t>* outHeight) {
- return encode(height, outHeight, encodeInteger);
+ return encode(MetadataType_Height, height, outHeight, encodeInteger);
}
status_t decodeHeight(const hidl_vec<uint8_t>& height, uint64_t* outHeight) {
- return decode(height, outHeight, decodeInteger);
+ return decode(MetadataType_Height, height, outHeight, decodeInteger);
}
status_t encodeLayerCount(uint64_t layerCount, hidl_vec<uint8_t>* outLayerCount) {
- return encode(layerCount, outLayerCount, encodeInteger);
+ return encode(MetadataType_LayerCount, layerCount, outLayerCount, encodeInteger);
}
status_t decodeLayerCount(const hidl_vec<uint8_t>& layerCount, uint64_t* outLayerCount) {
- return decode(layerCount, outLayerCount, decodeInteger);
+ return decode(MetadataType_LayerCount, layerCount, outLayerCount, decodeInteger);
}
status_t encodePixelFormatRequested(const hardware::graphics::common::V1_2::PixelFormat& pixelFormatRequested,
hidl_vec<uint8_t>* outPixelFormatRequested) {
- return encode(static_cast<int32_t>(pixelFormatRequested), outPixelFormatRequested, encodeInteger);
+ return encode(MetadataType_PixelFormatRequested, static_cast<int32_t>(pixelFormatRequested),
+ outPixelFormatRequested, encodeInteger);
}
status_t decodePixelFormatRequested(const hidl_vec<uint8_t>& pixelFormatRequested,
hardware::graphics::common::V1_2::PixelFormat* outPixelFormatRequested) {
- return decode(pixelFormatRequested, reinterpret_cast<int32_t*>(outPixelFormatRequested), decodeInteger);
+ return decode(MetadataType_PixelFormatRequested, pixelFormatRequested,
+ reinterpret_cast<int32_t*>(outPixelFormatRequested), decodeInteger);
}
status_t encodePixelFormatFourCC(uint32_t pixelFormatFourCC, hidl_vec<uint8_t>* outPixelFormatFourCC) {
- return encode(pixelFormatFourCC, outPixelFormatFourCC, encodeInteger);
+ return encode(MetadataType_PixelFormatFourCC, pixelFormatFourCC, outPixelFormatFourCC,
+ encodeInteger);
}
status_t decodePixelFormatFourCC(const hidl_vec<uint8_t>& pixelFormatFourCC, uint32_t* outPixelFormatFourCC) {
- return decode(pixelFormatFourCC, outPixelFormatFourCC, decodeInteger);
+ return decode(MetadataType_PixelFormatFourCC, pixelFormatFourCC, outPixelFormatFourCC,
+ decodeInteger);
}
status_t encodePixelFormatModifier(uint64_t pixelFormatModifier, hidl_vec<uint8_t>* outPixelFormatModifier) {
- return encode(pixelFormatModifier, outPixelFormatModifier, encodeInteger);
+ return encode(MetadataType_PixelFormatModifier, pixelFormatModifier, outPixelFormatModifier,
+ encodeInteger);
}
status_t decodePixelFormatModifier(const hidl_vec<uint8_t>& pixelFormatModifier, uint64_t* outPixelFormatModifier) {
- return decode(pixelFormatModifier, outPixelFormatModifier, decodeInteger);
+ return decode(MetadataType_PixelFormatModifier, pixelFormatModifier, outPixelFormatModifier,
+ decodeInteger);
}
status_t encodeUsage(uint64_t usage, hidl_vec<uint8_t>* outUsage) {
- return encode(usage, outUsage, encodeInteger);
+ return encode(MetadataType_Usage, usage, outUsage, encodeInteger);
}
status_t decodeUsage(const hidl_vec<uint8_t>& usage, uint64_t* outUsage) {
- return decode(usage, outUsage, decodeInteger);
+ return decode(MetadataType_Usage, usage, outUsage, decodeInteger);
}
status_t encodeAllocationSize(uint64_t allocationSize, hidl_vec<uint8_t>* outAllocationSize) {
- return encode(allocationSize, outAllocationSize, encodeInteger);
+ return encode(MetadataType_AllocationSize, allocationSize, outAllocationSize, encodeInteger);
}
status_t decodeAllocationSize(const hidl_vec<uint8_t>& allocationSize, uint64_t* outAllocationSize) {
- return decode(allocationSize, outAllocationSize, decodeInteger);
+ return decode(MetadataType_AllocationSize, allocationSize, outAllocationSize, decodeInteger);
}
status_t encodeProtectedContent(uint64_t protectedContent, hidl_vec<uint8_t>* outProtectedContent) {
- return encode(protectedContent, outProtectedContent, encodeInteger);
+ return encode(MetadataType_ProtectedContent, protectedContent, outProtectedContent,
+ encodeInteger);
}
status_t decodeProtectedContent(const hidl_vec<uint8_t>& protectedContent, uint64_t* outProtectedContent) {
- return decode(protectedContent, outProtectedContent, decodeInteger);
+ return decode(MetadataType_ProtectedContent, protectedContent, outProtectedContent,
+ decodeInteger);
}
status_t encodeCompression(const ExtendableType& compression, hidl_vec<uint8_t>* outCompression) {
- return encode(compression, outCompression, encodeExtendableType);
+ return encode(MetadataType_Compression, compression, outCompression, encodeExtendableType);
}
status_t decodeCompression(const hidl_vec<uint8_t>& compression, ExtendableType* outCompression) {
- return decode(compression, outCompression, decodeExtendableType, clearExtendableType);
+ return decode(MetadataType_Compression, compression, outCompression, decodeExtendableType,
+ clearExtendableType);
}
status_t encodeInterlaced(const ExtendableType& interlaced, hidl_vec<uint8_t>* outInterlaced) {
- return encode(interlaced, outInterlaced, encodeExtendableType);
+ return encode(MetadataType_Interlaced, interlaced, outInterlaced, encodeExtendableType);
}
status_t decodeInterlaced(const hidl_vec<uint8_t>& interlaced, ExtendableType* outInterlaced) {
- return decode(interlaced, outInterlaced, decodeExtendableType, clearExtendableType);
+ return decode(MetadataType_Interlaced, interlaced, outInterlaced, decodeExtendableType,
+ clearExtendableType);
}
status_t encodeChromaSiting(const ExtendableType& chromaSiting, hidl_vec<uint8_t>* outChromaSiting) {
- return encode(chromaSiting, outChromaSiting, encodeExtendableType);
+ return encode(MetadataType_ChromaSiting, chromaSiting, outChromaSiting, encodeExtendableType);
}
status_t decodeChromaSiting(const hidl_vec<uint8_t>& chromaSiting, ExtendableType* outChromaSiting) {
- return decode(chromaSiting, outChromaSiting, decodeExtendableType, clearExtendableType);
+ return decode(MetadataType_ChromaSiting, chromaSiting, outChromaSiting, decodeExtendableType,
+ clearExtendableType);
}
status_t encodePlaneLayouts(const std::vector<PlaneLayout>& planeLayouts, hidl_vec<uint8_t>* outPlaneLayouts) {
- return encode(planeLayouts, outPlaneLayouts, encodePlaneLayoutsHelper);
+ return encode(MetadataType_PlaneLayouts, planeLayouts, outPlaneLayouts,
+ encodePlaneLayoutsHelper);
}
status_t decodePlaneLayouts(const hidl_vec<uint8_t>& planeLayouts, std::vector<PlaneLayout>* outPlaneLayouts) {
- return decode(planeLayouts, outPlaneLayouts, decodePlaneLayoutsHelper, clearPlaneLayouts);
+ return decode(MetadataType_PlaneLayouts, planeLayouts, outPlaneLayouts,
+ decodePlaneLayoutsHelper, clearPlaneLayouts);
}
status_t encodeDataspace(const Dataspace& dataspace, hidl_vec<uint8_t>* outDataspace) {
- return encode(static_cast<int32_t>(dataspace), outDataspace, encodeInteger);
+ return encode(MetadataType_Dataspace, static_cast<int32_t>(dataspace), outDataspace,
+ encodeInteger);
}
status_t decodeDataspace(const hidl_vec<uint8_t>& dataspace, Dataspace* outDataspace) {
- return decode(dataspace, reinterpret_cast<int32_t*>(outDataspace), decodeInteger);
+ return decode(MetadataType_Dataspace, dataspace, reinterpret_cast<int32_t*>(outDataspace),
+ decodeInteger);
}
status_t encodeBlendMode(const BlendMode& blendMode, hidl_vec<uint8_t>* outBlendMode) {
- return encode(static_cast<int32_t>(blendMode), outBlendMode, encodeInteger);
+ return encode(MetadataType_BlendMode, static_cast<int32_t>(blendMode), outBlendMode,
+ encodeInteger);
}
status_t decodeBlendMode(const hidl_vec<uint8_t>& blendMode, BlendMode* outBlendMode) {
- return decode(blendMode, reinterpret_cast<int32_t*>(outBlendMode), decodeInteger);
+ return decode(MetadataType_BlendMode, blendMode, reinterpret_cast<int32_t*>(outBlendMode),
+ decodeInteger);
+}
+
+status_t encodeSmpte2086(const std::optional<Smpte2086>& smpte2086,
+ hidl_vec<uint8_t>* outSmpte2086) {
+ return encodeOptional(MetadataType_Smpte2086, smpte2086, outSmpte2086, encodeSmpte2086Helper);
+}
+
+status_t decodeSmpte2086(const hidl_vec<uint8_t>& smpte2086,
+ std::optional<Smpte2086>* outSmpte2086) {
+ return decodeOptional(MetadataType_Smpte2086, smpte2086, outSmpte2086, decodeSmpte2086Helper);
+}
+
+status_t encodeCta861_3(const std::optional<Cta861_3>& cta861_3, hidl_vec<uint8_t>* outCta861_3) {
+ return encodeOptional(MetadataType_Cta861_3, cta861_3, outCta861_3, encodeCta861_3Helper);
+}
+
+status_t decodeCta861_3(const hidl_vec<uint8_t>& cta861_3, std::optional<Cta861_3>* outCta861_3) {
+ return decodeOptional(MetadataType_Cta861_3, cta861_3, outCta861_3, decodeCta861_3Helper);
+}
+
+status_t encodeSmpte2094_40(const std::optional<std::vector<uint8_t>>& smpte2094_40,
+ hidl_vec<uint8_t>* outSmpte2094_40) {
+ return encodeOptional(MetadataType_Smpte2094_40, smpte2094_40, outSmpte2094_40,
+ encodeByteVector);
+}
+
+status_t decodeSmpte2094_40(const hidl_vec<uint8_t>& smpte2094_40,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_40) {
+ return decodeOptional(MetadataType_Smpte2094_40, smpte2094_40, outSmpte2094_40,
+ decodeByteVector);
+}
+
+status_t encodeUint32(const MetadataType& metadataType, uint32_t input,
+ hidl_vec<uint8_t>* output) {
+ return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeUint32(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ uint32_t* output) {
+ return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeInt32(const MetadataType& metadataType, int32_t input,
+ hidl_vec<uint8_t>* output) {
+ return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeInt32(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ int32_t* output) {
+ return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeUint64(const MetadataType& metadataType, uint64_t input,
+ hidl_vec<uint8_t>* output) {
+ return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeUint64(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ uint64_t* output) {
+ return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeInt64(const MetadataType& metadataType, int64_t input,
+ hidl_vec<uint8_t>* output) {
+ return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeInt64(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ int64_t* output) {
+ return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeFloat(const MetadataType& metadataType, float input,
+ hidl_vec<uint8_t>* output) {
+ return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeFloat(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ float* output) {
+ return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeDouble(const MetadataType& metadataType, double input,
+ hidl_vec<uint8_t>* output) {
+ return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeDouble(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ double* output) {
+ return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeString(const MetadataType& metadataType, const std::string& input,
+ hidl_vec<uint8_t>* output) {
+ return encode(metadataType, input, output, encodeString);
+}
+
+status_t decodeString(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ std::string* output) {
+ return decode(metadataType, input, output, decodeString);
}
bool isStandardMetadataType(const MetadataType& metadataType) {
@@ -733,6 +1096,11 @@
return static_cast<ChromaSiting>(chromaSiting.value);
}
+PlaneLayoutComponentType getStandardPlaneLayoutComponentTypeValue(
+ const ExtendableType& planeLayoutComponentType) {
+ return static_cast<PlaneLayoutComponentType>(planeLayoutComponentType.value);
+}
+
std::string getCompressionName(const ExtendableType& compression) {
if (!isStandardCompression(compression)) {
std::ostringstream stream;
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.cpp b/libs/gralloc/types/fuzzer/gralloctypes.cpp
index 23c90b8..b18f407 100644
--- a/libs/gralloc/types/fuzzer/gralloctypes.cpp
+++ b/libs/gralloc/types/fuzzer/gralloctypes.cpp
@@ -30,9 +30,10 @@
#include "util.h"
using ::android::status_t;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
#define GRALLOCTYPES_DECODE(T, FUNC) \
- [] (const ::android::hardware::hidl_vec<uint8_t>& vec, uint8_t /*data*/) {\
+ [] (const ::android::hardware::hidl_vec<uint8_t>& vec) {\
FUZZ_LOG() << "about to read " #T " using " #FUNC;\
T t;\
status_t err = FUNC(vec, &t);\
@@ -40,6 +41,15 @@
FUZZ_LOG() << #T " done " /* << "err: " << err*/;\
}
+#define GRALLOCTYPES_DECODE_VENDOR_HELPER(T, FUNC) \
+ [] (const MetadataType& metadataType, const ::android::hardware::hidl_vec<uint8_t>& vec) {\
+ FUZZ_LOG() << "about to read " #T " using " #FUNC;\
+ T t;\
+ status_t err = FUNC(metadataType, vec, &t);\
+ (void) err;\
+ FUZZ_LOG() << #T " done " /* << "err: " << err*/;\
+ }
+
// clang-format off
std::vector<GrallocTypesDecode> GRALLOCTYPES_DECODE_FUNCTIONS {
@@ -60,5 +70,18 @@
GRALLOCTYPES_DECODE(std::vector<aidl::android::hardware::graphics::common::PlaneLayout>, ::android::gralloc4::decodePlaneLayouts),
GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::Dataspace, ::android::gralloc4::decodeDataspace),
GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::BlendMode, ::android::gralloc4::decodeBlendMode),
+ GRALLOCTYPES_DECODE(std::optional<aidl::android::hardware::graphics::common::Smpte2086>, ::android::gralloc4::decodeSmpte2086),
+ GRALLOCTYPES_DECODE(std::optional<aidl::android::hardware::graphics::common::Cta861_3>, ::android::gralloc4::decodeCta861_3),
+ GRALLOCTYPES_DECODE(std::optional<std::vector<uint8_t>>, ::android::gralloc4::decodeSmpte2094_40),
+};
+
+std::vector<GrallocTypesVendorHelperDecode> GRALLOCTYPES_DECODE_VENDOR_HELPER_FUNCTIONS {
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(uint32_t, ::android::gralloc4::decodeUint32),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(int32_t, ::android::gralloc4::decodeInt32),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(uint64_t, ::android::gralloc4::decodeUint64),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(int64_t, ::android::gralloc4::decodeInt64),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(float, ::android::gralloc4::decodeFloat),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(double, ::android::gralloc4::decodeDouble),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(std::string, ::android::gralloc4::decodeString),
};
// clang-format on
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.h b/libs/gralloc/types/fuzzer/gralloctypes.h
index b995fce..a3fe2d9 100644
--- a/libs/gralloc/types/fuzzer/gralloctypes.h
+++ b/libs/gralloc/types/fuzzer/gralloctypes.h
@@ -20,6 +20,8 @@
#include <vector>
-using GrallocTypesDecode = std::function<void(const ::android::hardware::hidl_vec<uint8_t>& vec, uint8_t data)>;
+using GrallocTypesDecode = std::function<void(const ::android::hardware::hidl_vec<uint8_t>& vec)>;
+using GrallocTypesVendorHelperDecode = std::function<void(const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType&, const ::android::hardware::hidl_vec<uint8_t>& vec)>;
extern std::vector<GrallocTypesDecode> GRALLOCTYPES_DECODE_FUNCTIONS;
+extern std::vector<GrallocTypesVendorHelperDecode> GRALLOCTYPES_DECODE_VENDOR_HELPER_FUNCTIONS;
diff --git a/libs/gralloc/types/fuzzer/main.cpp b/libs/gralloc/types/fuzzer/main.cpp
index 2807878..8779fa2 100644
--- a/libs/gralloc/types/fuzzer/main.cpp
+++ b/libs/gralloc/types/fuzzer/main.cpp
@@ -24,52 +24,99 @@
#include <cstdlib>
#include <ctime>
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
void doFuzz(
- const std::vector<GrallocTypesDecode>& decodes,
- const std::vector<uint8_t>& input,
- const std::vector<uint8_t>& instructions) {
+ const std::vector<GrallocTypesDecode>& decodes, uint8_t instruction,
+ const std::vector<uint8_t>& input) {
::android::hardware::hidl_vec<uint8_t> vec;
vec.setToExternal(const_cast<uint8_t*>(input.data()), input.size(), false /*shouldOwn*/);
// since we are only using a byte to index
CHECK(decodes.size() <= 255) << decodes.size();
+ uint8_t decodeIdx = instruction % decodes.size();
- for (size_t i = 0; i < instructions.size() - 1; i += 2) {
- uint8_t a = instructions[i];
- uint8_t decodeIdx = a % decodes.size();
+ FUZZ_LOG() << "Instruction: " << instruction << " idx: " << static_cast<size_t>(decodeIdx)
+ << " size: " << vec.size();
- uint8_t b = instructions[i + 1];
+ decodes[decodeIdx](vec);
+}
- FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2
- << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(decodeIdx)
- << ") arg: " << static_cast<size_t>(b) << " size: " << vec.size();
+size_t fillInMetadataType(const std::vector<uint8_t>& input, MetadataType* outMetadataType) {
+ if (input.size() < sizeof(outMetadataType->value) + 1) {
+ return 0;
+ }
+ size_t size = 0;
- decodes[decodeIdx](vec, b);
+ outMetadataType->value = *(reinterpret_cast<const int64_t*>(input.data()));
+ size += sizeof(outMetadataType->value);
+
+ uint8_t nameLen = *(input.data() + size);
+ size += 1;
+
+ if (input.size() < size + nameLen) {
+ return 0;
+ }
+ std::string name(reinterpret_cast<const char*>(input.data()) + size, nameLen);
+ outMetadataType->name = name;
+ return size + nameLen;
+}
+
+void doFuzzVendorHelper(
+ const std::vector<GrallocTypesVendorHelperDecode>& decodes, uint8_t instruction,
+ const std::vector<uint8_t>& input) {
+
+ MetadataType metadataType;
+ size_t sizeUsed = fillInMetadataType(input, &metadataType);
+ if (sizeUsed <= 0) {
+ return;
+ }
+
+ ::android::hardware::hidl_vec<uint8_t> vec;
+ vec.setToExternal(const_cast<uint8_t*>(input.data() + sizeUsed), input.size() - sizeUsed,
+ false /*shouldOwn*/);
+
+ // since we are only using a byte to index
+ CHECK(decodes.size() <= 255) << decodes.size();
+ uint8_t decodeIdx = instruction % decodes.size();
+
+ FUZZ_LOG() << "Vendor Helper instruction: " << instruction << " idx: "
+ << static_cast<size_t>(decodeIdx) << " size: " << vec.size();
+
+ decodes[decodeIdx](metadataType, vec);
+}
+
+void fuzz(uint8_t options, uint8_t instruction, const std::vector<uint8_t>& input) {
+ uint8_t option = options & 0x1;
+
+ switch (option) {
+ case 0x0:
+ doFuzz(GRALLOCTYPES_DECODE_FUNCTIONS, instruction, input);
+ break;
+ case 0x1:
+ doFuzzVendorHelper(GRALLOCTYPES_DECODE_VENDOR_HELPER_FUNCTIONS, instruction, input);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("unknown gralloc types %d", static_cast<int>(option));
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size <= 1) return 0; // no use
- // data to fill out parcel
- size_t inputLen = size / 2;
- std::vector<uint8_t> input(data, data + inputLen);
- data += inputLen;
- size -= inputLen;
+ uint8_t options = *data;
+ data++;
+ size--;
- // data to use to determine what to do
- size_t instructionLen = size;
- std::vector<uint8_t> instructions(data, data + instructionLen);
- data += instructionLen;
- size -= instructionLen;
+ uint8_t instruction = *data;
+ data++;
+ size--;
- CHECK(size == 0) << "size: " << size;
+ std::vector<uint8_t> input(data, data + size);
- FUZZ_LOG() << "inputLen: " << inputLen << " instructionLen: " << instructionLen;
FUZZ_LOG() << "input: " << hexString(input);
- FUZZ_LOG() << "instructions: " << hexString(instructions);
- doFuzz(GRALLOCTYPES_DECODE_FUNCTIONS, input, instructions);
+ fuzz(options, instruction, input);
return 0;
}
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index a5d3bb2..ca0d4b5 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -17,18 +17,199 @@
#pragma once
#include <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
+#include <aidl/android/hardware/graphics/common/Compression.h>
+#include <aidl/android/hardware/graphics/common/Cta861_3.h>
#include <aidl/android/hardware/graphics/common/Dataspace.h>
#include <aidl/android/hardware/graphics/common/ExtendableType.h>
#include <aidl/android/hardware/graphics/common/Interlaced.h>
#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
-#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
-#include <aidl/android/hardware/graphics/common/Compression.h>
-#include <aidl/android/hardware/graphics/common/Interlaced.h>
-#include <aidl/android/hardware/graphics/common/StandardMetadataType.h>
#include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
-
+#include <aidl/android/hardware/graphics/common/Smpte2086.h>
+#include <aidl/android/hardware/graphics/common/StandardMetadataType.h>
+#include <aidl/android/hardware/graphics/common/XyColor.h>
#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+/**
+ * Define equality operators for Stable AIDL types.
+ */
+inline bool operator==(const aidl::android::hardware::graphics::common::ExtendableType& lhs,
+ const aidl::android::hardware::graphics::common::ExtendableType& rhs) {
+ return !std::strcmp(lhs.name.c_str(), rhs.name.c_str()) && lhs.value == rhs.value;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::ExtendableType& lhs,
+ const aidl::android::hardware::graphics::common::ExtendableType& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::PlaneLayoutComponent& lhs,
+ const aidl::android::hardware::graphics::common::PlaneLayoutComponent& rhs) {
+ if (lhs.type.name != rhs.type.name) {
+ return false;
+ }
+ if (lhs.type.value != rhs.type.value) {
+ return false;
+ }
+ if (lhs.sizeInBits != rhs.sizeInBits) {
+ return false;
+ }
+ if (lhs.offsetInBits != rhs.offsetInBits) {
+ return false;
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::PlaneLayoutComponent& lhs,
+ const aidl::android::hardware::graphics::common::PlaneLayoutComponent& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::Rect& lhs,
+ const aidl::android::hardware::graphics::common::Rect& rhs) {
+ if (lhs.left != rhs.left) {
+ return false;
+ }
+ if (lhs.top != rhs.top) {
+ return false;
+ }
+ if (lhs.right != rhs.right) {
+ return false;
+ }
+ if (lhs.bottom != rhs.bottom) {
+ return false;
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::Rect& lhs,
+ const aidl::android::hardware::graphics::common::Rect& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::PlaneLayout& lhs,
+ const aidl::android::hardware::graphics::common::PlaneLayout& rhs) {
+ if (lhs.offsetInBytes != rhs.offsetInBytes) {
+ return false;
+ }
+ if (lhs.sampleIncrementInBits != rhs.sampleIncrementInBits) {
+ return false;
+ }
+ if (lhs.strideInBytes != rhs.strideInBytes) {
+ return false;
+ }
+ if (lhs.widthInSamples != rhs.widthInSamples) {
+ return false;
+ }
+ if (lhs.heightInSamples != rhs.heightInSamples) {
+ return false;
+ }
+ if (lhs.totalSizeInBytes != rhs.totalSizeInBytes) {
+ return false;
+ }
+ if (lhs.horizontalSubsampling != rhs.horizontalSubsampling) {
+ return false;
+ }
+ if (lhs.verticalSubsampling != rhs.verticalSubsampling) {
+ return false;
+ }
+ if (lhs.crop != rhs.crop) {
+ return false;
+ }
+ if (lhs.components.size() != rhs.components.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < lhs.components.size(); i++) {
+ if (lhs.components[i] != rhs.components[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::PlaneLayout& lhs,
+ const aidl::android::hardware::graphics::common::PlaneLayout& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& lhs,
+ const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& rhs) {
+ if (lhs.size() != rhs.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < lhs.size(); i++) {
+ if (lhs[i] != rhs[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline bool operator!=(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& lhs,
+ const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::XyColor& lhs,
+ const aidl::android::hardware::graphics::common::XyColor& rhs) {
+ if (lhs.x != rhs.x) {
+ return false;
+ }
+ if (lhs.y != rhs.y) {
+ return false;
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::XyColor& lhs,
+ const aidl::android::hardware::graphics::common::XyColor& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::Smpte2086& lhs,
+ const aidl::android::hardware::graphics::common::Smpte2086& rhs) {
+ if (lhs.primaryRed != rhs.primaryRed) {
+ return false;
+ }
+ if (lhs.primaryGreen != rhs.primaryGreen) {
+ return false;
+ }
+ if (lhs.primaryBlue != rhs.primaryBlue) {
+ return false;
+ }
+ if (lhs.whitePoint != rhs.whitePoint) {
+ return false;
+ }
+ if (lhs.maxLuminance != rhs.maxLuminance) {
+ return false;
+ }
+ if (lhs.minLuminance != rhs.minLuminance) {
+ return false;
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::Smpte2086& lhs,
+ const aidl::android::hardware::graphics::common::Smpte2086& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::Cta861_3& lhs,
+ const aidl::android::hardware::graphics::common::Cta861_3& rhs) {
+ if (lhs.maxContentLightLevel != rhs.maxContentLightLevel) {
+ return false;
+ }
+ if (lhs.maxFrameAverageLightLevel != rhs.maxFrameAverageLightLevel) {
+ return false;
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::Cta861_3& lhs,
+ const aidl::android::hardware::graphics::common::Cta861_3& rhs) {
+ return !(lhs == rhs);
+}
+
namespace android {
namespace gralloc4 {
@@ -113,6 +294,22 @@
GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::BLEND_MODE)
};
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+ MetadataType_Smpte2086 = {GRALLOC4_STANDARD_METADATA_TYPE,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::
+ StandardMetadataType::SMPTE2086)};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+ MetadataType_Cta861_3 = {GRALLOC4_STANDARD_METADATA_TYPE,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::
+ StandardMetadataType::CTA861_3)};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+ MetadataType_Smpte2094_40 = {GRALLOC4_STANDARD_METADATA_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::
+ StandardMetadataType::SMPTE2094_40)};
+
/*---------------------------------------------------------------------------------------------*/
/**
@@ -272,6 +469,77 @@
status_t encodeBlendMode(const aidl::android::hardware::graphics::common::BlendMode& blendMode, android::hardware::hidl_vec<uint8_t>* outBlendMode);
status_t decodeBlendMode(const android::hardware::hidl_vec<uint8_t>& blendMode, aidl::android::hardware::graphics::common::BlendMode* outBlendMode);
+status_t encodeSmpte2086(
+ const std::optional<aidl::android::hardware::graphics::common::Smpte2086>& smpte2086,
+ android::hardware::hidl_vec<uint8_t>* outSmpte2086);
+status_t decodeSmpte2086(
+ const android::hardware::hidl_vec<uint8_t>& smpte2086,
+ std::optional<aidl::android::hardware::graphics::common::Smpte2086>* outSmpte2086);
+
+status_t encodeCta861_3(
+ const std::optional<aidl::android::hardware::graphics::common::Cta861_3>& cta861_3,
+ android::hardware::hidl_vec<uint8_t>* outCta861_3);
+status_t decodeCta861_3(
+ const android::hardware::hidl_vec<uint8_t>& cta861_3,
+ std::optional<aidl::android::hardware::graphics::common::Cta861_3>* outCta861_3);
+
+status_t encodeSmpte2094_40(const std::optional<std::vector<uint8_t>>& smpte2094_40,
+ android::hardware::hidl_vec<uint8_t>* outSmpte2094_40);
+status_t decodeSmpte2094_40(const android::hardware::hidl_vec<uint8_t>& smpte2094_40,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_40);
+
+/**
+ * The functions below can be used to encode and decode vendor metadata types.
+ */
+status_t encodeUint32(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ uint32_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeUint32(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, uint32_t* output);
+
+status_t encodeInt32(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ int32_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeInt32(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, int32_t* output);
+
+status_t encodeUint64(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ uint64_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeUint64(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, uint64_t* output);
+
+status_t encodeInt64(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ int64_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeInt64(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, int64_t* output);
+
+status_t encodeFloat(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ float input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeFloat(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, float* output);
+
+status_t encodeDouble(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ double input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeDouble(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, double* output);
+
+status_t encodeString(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const std::string& input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeString(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, std::string* output);
+
/**
* The functions below can be used to parse extendable types.
*/
diff --git a/libs/gralloc/types/tests/Android.bp b/libs/gralloc/types/tests/Android.bp
new file mode 100644
index 0000000..b939c1d
--- /dev/null
+++ b/libs/gralloc/types/tests/Android.bp
@@ -0,0 +1,25 @@
+//
+// 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.
+//
+
+cc_test {
+ name: "GrallocTypes_test",
+ shared_libs: [
+ "libgralloctypes",
+ "libhidlbase",
+ ],
+ srcs: ["Gralloc4_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+}
diff --git a/libs/gralloc/types/tests/Gralloc4_test.cpp b/libs/gralloc/types/tests/Gralloc4_test.cpp
new file mode 100644
index 0000000..3542ed9
--- /dev/null
+++ b/libs/gralloc/types/tests/Gralloc4_test.cpp
@@ -0,0 +1,552 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Gralloc4Test"
+
+#include <limits>
+
+#include <gralloctypes/Gralloc4.h>
+
+#include <gtest/gtest.h>
+
+using android::hardware::hidl_vec;
+
+using android::hardware::graphics::common::V1_2::PixelFormat;
+
+using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::ChromaSiting;
+using aidl::android::hardware::graphics::common::Compression;
+using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::Dataspace;
+using aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::Interlaced;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using aidl::android::hardware::graphics::common::Rect;
+using aidl::android::hardware::graphics::common::Smpte2086;
+using aidl::android::hardware::graphics::common::StandardMetadataType;
+using aidl::android::hardware::graphics::common::XyColor;
+
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
+namespace android {
+
+template<class T>
+using EncodeFunction = status_t(*)(T, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeConstFunction = status_t(*)(const T&, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeMetadataTypeFunction = status_t(*)(const MetadataType&, T, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeMetadataTypeConstFunction = status_t(*)(const MetadataType&, const T&, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeOptionalFunction = status_t(*)(const std::optional<T>&, hidl_vec<uint8_t>*);
+
+template<class T>
+using DecodeFunction = status_t(*)(const hidl_vec<uint8_t>&, T*);
+
+template<class T>
+using DecodeMetadataTypeFunction = status_t(*)(const MetadataType&, const hidl_vec<uint8_t>&, T*);
+
+template<class T>
+using DecodeOptionalFunction = status_t(*)(const hidl_vec<uint8_t>&, std::optional<T>*);
+
+template<class T>
+void testHelper(const T& input, EncodeFunction<T> encode, DecodeFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ T output;
+ ASSERT_EQ(NO_ERROR, encode(input, &vec));
+ ASSERT_EQ(NO_ERROR, decode(vec, &output));
+ ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperConst(const T& input, EncodeConstFunction<T> encode, DecodeFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ T output;
+ ASSERT_EQ(NO_ERROR, encode(input, &vec));
+ ASSERT_EQ(NO_ERROR, decode(vec, &output));
+ ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperMetadataType(const T& input, EncodeMetadataTypeFunction<T> encode, DecodeMetadataTypeFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ MetadataType metadataType{"vendor.mycompanyname.graphics.common.MetadataType", 0};
+ T output;
+ ASSERT_EQ(NO_ERROR, encode(metadataType, input, &vec));
+ ASSERT_EQ(NO_ERROR, decode(metadataType, vec, &output));
+ ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperMetadataTypeConst(const T& input, EncodeMetadataTypeConstFunction<T> encode, DecodeMetadataTypeFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ MetadataType metadataType{"vendor.mycompanyname.graphics.common.MetadataType", 0};
+ T output;
+ ASSERT_EQ(NO_ERROR, encode(metadataType, input, &vec));
+ ASSERT_EQ(NO_ERROR, decode(metadataType, vec, &output));
+ ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperStableAidlType(const T& input, EncodeConstFunction<T> encode, DecodeFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ T output;
+ ASSERT_EQ(NO_ERROR, encode(input, &vec));
+ ASSERT_EQ(NO_ERROR, decode(vec, &output));
+ ASSERT_TRUE(input == output);
+}
+
+template<class T>
+void testHelperStableAidlTypeOptional(const std::optional<T>& input, EncodeOptionalFunction<T> encode,
+ DecodeOptionalFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ std::optional<T> tmp = input;
+ std::optional<T> output;
+ ASSERT_EQ(NO_ERROR, encode(tmp, &vec));
+ ASSERT_EQ(NO_ERROR, decode(vec, &output));
+ ASSERT_EQ(tmp.has_value(), output.has_value());
+ if (!tmp.has_value()) {
+ return;
+ }
+ ASSERT_TRUE(*tmp == *output);
+}
+
+class Gralloc4TestUint32 : public testing::TestWithParam<uint32_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestUint32Params, Gralloc4TestUint32,
+ ::testing::Values(0, -1, 1, 5, 100, 0xFF, std::numeric_limits<uint32_t>::min(),
+ std::numeric_limits<uint32_t>::max()));
+
+TEST_P(Gralloc4TestUint32, Uint32) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeUint32, gralloc4::decodeUint32));
+}
+
+TEST_P(Gralloc4TestUint32, PixelFormatFourCC) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodePixelFormatFourCC, gralloc4::decodePixelFormatFourCC));
+}
+
+class Gralloc4TestInt32 : public testing::TestWithParam<int32_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestInt32Params, Gralloc4TestInt32,
+ ::testing::Values(0, 1, 5, 100, 0xFF, std::numeric_limits<int32_t>::min(),
+ std::numeric_limits<int32_t>::max()));
+
+TEST_P(Gralloc4TestInt32, Int32) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeInt32, gralloc4::decodeInt32));
+}
+
+class Gralloc4TestUint64 : public testing::TestWithParam<uint64_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestUint64Params, Gralloc4TestUint64,
+ ::testing::Values(0, -1, 1, 5, 100, 0xFF, std::numeric_limits<uint64_t>::min(),
+ std::numeric_limits<uint64_t>::max()));
+
+TEST_P(Gralloc4TestUint64, Uint64) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeUint64, gralloc4::decodeUint64));
+}
+
+TEST_P(Gralloc4TestUint64, BufferId) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeBufferId, gralloc4::decodeBufferId));
+}
+
+TEST_P(Gralloc4TestUint64, Width) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeWidth, gralloc4::decodeWidth));
+}
+
+TEST_P(Gralloc4TestUint64, Height) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeHeight, gralloc4::decodeHeight));
+}
+
+TEST_P(Gralloc4TestUint64, LayerCount) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeLayerCount, gralloc4::decodeLayerCount));
+}
+
+TEST_P(Gralloc4TestUint64, PixelFormatModifier) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodePixelFormatModifier, gralloc4::decodePixelFormatModifier));
+}
+
+TEST_P(Gralloc4TestUint64, Usage) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeUsage, gralloc4::decodeUsage));
+}
+
+TEST_P(Gralloc4TestUint64, AllocationSize) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeAllocationSize, gralloc4::decodeAllocationSize));
+}
+
+TEST_P(Gralloc4TestUint64, ProtectedContent) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeProtectedContent, gralloc4::decodeProtectedContent));
+}
+
+class Gralloc4TestInt64 : public testing::TestWithParam<int64_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestInt64Params, Gralloc4TestInt64,
+ ::testing::Values(0, 1, 5, 100, 0xFF, std::numeric_limits<int64_t>::min(),
+ std::numeric_limits<int64_t>::max()));
+
+TEST_P(Gralloc4TestInt64, Int64) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeInt64, gralloc4::decodeInt64));
+}
+
+class Gralloc4TestFloat : public testing::TestWithParam<float> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestFloatParams, Gralloc4TestFloat,
+ ::testing::Values(0.0, 1.999999, 5.5, 100.1, 1234.5678, std::numeric_limits<float>::min(),
+ std::numeric_limits<float>::max()));
+
+TEST_P(Gralloc4TestFloat, Float) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeFloat, gralloc4::decodeFloat));
+}
+
+class Gralloc4TestDouble : public testing::TestWithParam<double> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestDoubleParams, Gralloc4TestDouble,
+ ::testing::Values(0.0, 1.999999, 5.5, 100.1, 1234.5678, std::numeric_limits<double>::min(),
+ std::numeric_limits<double>::max()));
+
+TEST_P(Gralloc4TestDouble, Double) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeDouble, gralloc4::decodeDouble));
+}
+
+class Gralloc4TestString : public testing::TestWithParam<std::string> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestStringParams, Gralloc4TestString,
+ ::testing::Values("name", "aaaaa", "", "abcdefghijklmnopqrstuvwxyz", "0xFF"));
+
+TEST_P(Gralloc4TestString, String) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataTypeConst(GetParam(), gralloc4::encodeString, gralloc4::decodeString));
+}
+
+TEST_P(Gralloc4TestString, Name) {
+ ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeName, gralloc4::decodeName));
+}
+
+class Gralloc4TestPixelFormat : public testing::TestWithParam<PixelFormat> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestPixelFormatParams, Gralloc4TestPixelFormat,
+ ::testing::Values(PixelFormat::RGBA_8888, PixelFormat::BLOB,
+ PixelFormat::IMPLEMENTATION_DEFINED, PixelFormat::YCBCR_420_888,
+ PixelFormat::YV12));
+
+TEST_P(Gralloc4TestPixelFormat, PixelFormatRequested) {
+ ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodePixelFormatRequested, gralloc4::decodePixelFormatRequested));
+}
+
+class Gralloc4TestCompression : public testing::TestWithParam<ExtendableType> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestCompressionParams, Gralloc4TestCompression,
+ ::testing::Values(gralloc4::Compression_None, gralloc4::Compression_DisplayStreamCompression,
+ ExtendableType{"", 0},
+ ExtendableType{"vendor.mycompanyname.graphics.common.Compression", 0xFF},
+ ExtendableType{"vendor.mycompanyname.graphics.common.Compression", std::numeric_limits<int64_t>::max()}));
+
+TEST_P(Gralloc4TestCompression, Compression) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(GetParam(), gralloc4::encodeCompression, gralloc4::decodeCompression));
+}
+
+class Gralloc4TestInterlaced : public testing::TestWithParam<ExtendableType> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestInterlacedParams, Gralloc4TestInterlaced,
+ ::testing::Values(gralloc4::Interlaced_None, gralloc4::Interlaced_TopBottom,
+ gralloc4::Interlaced_RightLeft,
+ ExtendableType{"", 0},
+ ExtendableType{"vendor.mycompanyname.graphics.common.Interlaced", 0xFF},
+ ExtendableType{"vendor.mycompanyname.graphics.common.Interlaced", std::numeric_limits<int64_t>::max()}));
+
+TEST_P(Gralloc4TestInterlaced, Interlaced) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(GetParam(), gralloc4::encodeInterlaced, gralloc4::decodeInterlaced));
+}
+
+class Gralloc4TestChromaSiting : public testing::TestWithParam<ExtendableType> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestChromaSitingParams, Gralloc4TestChromaSiting,
+ ::testing::Values(gralloc4::ChromaSiting_None, gralloc4::ChromaSiting_Unknown,
+ gralloc4::ChromaSiting_SitedInterstitial, gralloc4::ChromaSiting_CositedHorizontal,
+ ExtendableType{"", 0},
+ ExtendableType{"vendor.mycompanyname.graphics.common.ChromaSiting", 0xFF},
+ ExtendableType{"vendor.mycompanyname.graphics.common.ChromaSiting", std::numeric_limits<int64_t>::max()}));
+
+TEST_P(Gralloc4TestChromaSiting, ChromaSiting) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(GetParam(), gralloc4::encodeChromaSiting, gralloc4::decodeChromaSiting));
+}
+
+class Gralloc4TestPlaneLayouts : public testing::Test { };
+
+TEST_F(Gralloc4TestPlaneLayouts, PlaneLayouts) {
+ uint32_t width = 64;
+ uint32_t height = 64;
+
+ std::vector<PlaneLayout> planeLayouts;
+ PlaneLayout planeLayoutA;
+ PlaneLayout planeLayoutRGB;
+ PlaneLayoutComponent component;
+
+ planeLayoutA.offsetInBytes = 0;
+ planeLayoutA.sampleIncrementInBits = 8;
+ planeLayoutA.strideInBytes = width + 20;
+ planeLayoutA.widthInSamples = width;
+ planeLayoutA.heightInSamples = height;
+ planeLayoutA.totalSizeInBytes = planeLayoutA.strideInBytes * height;
+ planeLayoutA.horizontalSubsampling = 1;
+ planeLayoutA.verticalSubsampling = 1;
+ planeLayoutA.crop.left = 0;
+ planeLayoutA.crop.top = 0;
+ planeLayoutA.crop.right = width;
+ planeLayoutA.crop.bottom = height;
+
+ component.type = gralloc4::PlaneLayoutComponentType_A;
+ component.offsetInBits = 0;
+ component.sizeInBits = 8;
+ planeLayoutA.components.push_back(component);
+
+ planeLayouts.push_back(planeLayoutA);
+
+ planeLayoutRGB.offsetInBytes = 0;
+ planeLayoutRGB.sampleIncrementInBits = 32;
+ planeLayoutRGB.strideInBytes = width + 20;
+ planeLayoutRGB.widthInSamples = width;
+ planeLayoutRGB.heightInSamples = height;
+ planeLayoutRGB.totalSizeInBytes = planeLayoutRGB.strideInBytes * height;
+ planeLayoutRGB.horizontalSubsampling = 1;
+ planeLayoutRGB.verticalSubsampling = 1;
+ planeLayoutRGB.crop.left = 0;
+ planeLayoutRGB.crop.top = 0;
+ planeLayoutRGB.crop.right = width;
+ planeLayoutRGB.crop.bottom = height;
+
+ component.type = gralloc4::PlaneLayoutComponentType_R;
+ planeLayoutRGB.components.push_back(component);
+ component.type = gralloc4::PlaneLayoutComponentType_G;
+ planeLayoutRGB.components.push_back(component);
+ component.type = gralloc4::PlaneLayoutComponentType_B;
+ planeLayoutRGB.components.push_back(component);
+
+ planeLayouts.push_back(planeLayoutRGB);
+
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(planeLayouts, gralloc4::encodePlaneLayouts, gralloc4::decodePlaneLayouts));
+}
+
+class Gralloc4TestDataspace : public testing::TestWithParam<Dataspace> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestDataspaceParams, Gralloc4TestDataspace,
+ ::testing::Values(Dataspace::UNKNOWN, Dataspace::ARBITRARY, Dataspace::DISPLAY_P3,
+ Dataspace::ADOBE_RGB));
+
+TEST_P(Gralloc4TestDataspace, DataspaceRequested) {
+ ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeDataspace, gralloc4::decodeDataspace));
+}
+
+class Gralloc4TestBlendMode : public testing::TestWithParam<BlendMode> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestBlendModeParams, Gralloc4TestBlendMode,
+ ::testing::Values(BlendMode::INVALID, BlendMode::NONE, BlendMode::PREMULTIPLIED,
+ BlendMode::COVERAGE));
+
+TEST_P(Gralloc4TestBlendMode, BlendMode) {
+ ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeBlendMode, gralloc4::decodeBlendMode));
+}
+
+class Gralloc4TestSmpte2086 : public testing::TestWithParam<std::optional<Smpte2086>> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestSmpte2086Params, Gralloc4TestSmpte2086,
+ ::testing::Values(std::optional<Smpte2086>(Smpte2086{XyColor{0.680, 0.320},
+ XyColor{0.265, 0.690},
+ XyColor{0.150, 0.060},
+ XyColor{0.3127, 0.3290},
+ 100.0, 0.1}),
+ std::optional<Smpte2086>(Smpte2086{XyColor{-1.0, 100.0},
+ XyColor{0xFF, -0xFF},
+ XyColor{999.9, 0.0},
+ XyColor{0.0, -1.0},
+ -0.1, -100.0}),
+ std::nullopt));
+
+TEST_P(Gralloc4TestSmpte2086, Smpte2086) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeSmpte2086, gralloc4::decodeSmpte2086));
+}
+
+class Gralloc4TestCta861_3 : public testing::TestWithParam<std::optional<Cta861_3>> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestCta861_3Params, Gralloc4TestCta861_3,
+ ::testing::Values(std::optional<Cta861_3>(Cta861_3{78.0, 62.0}),
+ std::optional<Cta861_3>(Cta861_3{10.0, 10.0}),
+ std::optional<Cta861_3>(Cta861_3{0.0, 0.0}),
+ std::optional<Cta861_3>(Cta861_3{std::numeric_limits<float>::min(), std::numeric_limits<float>::min()}),
+ std::optional<Cta861_3>(Cta861_3{std::numeric_limits<float>::max(), std::numeric_limits<float>::max()}),
+ std::nullopt));
+
+TEST_P(Gralloc4TestCta861_3, Cta861_3) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeCta861_3, gralloc4::decodeCta861_3));
+}
+
+class Gralloc4TestSmpte2094_40 : public testing::TestWithParam<std::optional<std::vector<uint8_t>>> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestSmpte2094_40Params, Gralloc4TestSmpte2094_40,
+ ::testing::Values(std::optional<std::vector<uint8_t>>({}),
+ std::optional<std::vector<uint8_t>>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}),
+ std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::min(),
+ std::numeric_limits<uint8_t>::min() + 1,
+ std::numeric_limits<uint8_t>::min() + 2,
+ std::numeric_limits<uint8_t>::min() + 3,
+ std::numeric_limits<uint8_t>::min() + 4}),
+ std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::max(),
+ std::numeric_limits<uint8_t>::max() - 1,
+ std::numeric_limits<uint8_t>::max() - 2,
+ std::numeric_limits<uint8_t>::max() - 3,
+ std::numeric_limits<uint8_t>::max() - 4}),
+ std::nullopt));
+
+TEST_P(Gralloc4TestSmpte2094_40, Smpte2094_40) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeSmpte2094_40, gralloc4::decodeSmpte2094_40));
+}
+
+class Gralloc4TestErrors : public testing::Test { };
+
+TEST_F(Gralloc4TestErrors, Gralloc4TestEncodeNull) {
+ ASSERT_NE(NO_ERROR, gralloc4::encodeBufferId(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeName("", nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeWidth(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeHeight(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeLayerCount(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodePixelFormatRequested(PixelFormat::RGBA_8888, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodePixelFormatFourCC(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodePixelFormatModifier(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeUsage(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeAllocationSize(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeProtectedContent(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeCompression(gralloc4::Compression_None, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeInterlaced(gralloc4::Interlaced_None, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeChromaSiting(gralloc4::ChromaSiting_None, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodePlaneLayouts({}, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeDataspace(Dataspace::UNKNOWN, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeBlendMode(BlendMode::NONE, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2086({{}}, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeCta861_3({{}}, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2094_40({{}}, nullptr));
+}
+
+TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeNull) {
+ hidl_vec<uint8_t> vec;
+
+ ASSERT_NE(NO_ERROR, gralloc4::decodeBufferId(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeName(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeWidth(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeHeight(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeLayerCount(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatRequested(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatFourCC(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatModifier(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeUsage(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeAllocationSize(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeProtectedContent(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeCompression(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeInterlaced(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeChromaSiting(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePlaneLayouts(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeDataspace(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeBlendMode(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2086(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeCta861_3(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_40(vec, nullptr));
+}
+
+TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeBadVec) {
+ hidl_vec<uint8_t> vec = { 0 };
+
+ uint64_t bufferId, width, height, layerCount, pixelFormatModifier, usage, allocationSize,
+ protectedContent;
+ std::string name;
+ PixelFormat pixelFormatRequested;
+ uint32_t pixelFormatFourCC;
+ ExtendableType compression, interlaced, chromaSiting;
+ std::vector<PlaneLayout> planeLayouts;
+ Dataspace dataspace;
+ BlendMode blendMode;
+ std::optional<Smpte2086> smpte2086;
+ std::optional<Cta861_3> cta861_3;
+ std::optional<std::vector<uint8_t>> smpte2094_40;
+
+ ASSERT_NE(NO_ERROR, gralloc4::decodeBufferId(vec, &bufferId));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeName(vec, &name));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeWidth(vec, &width));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeHeight(vec, &height));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeLayerCount(vec, &layerCount));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatRequested(vec, &pixelFormatRequested));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatFourCC(vec, &pixelFormatFourCC));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatModifier(vec, &pixelFormatModifier));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeUsage(vec, &usage));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeAllocationSize(vec, &allocationSize));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeProtectedContent(vec, &protectedContent));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeCompression(vec, &compression));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeInterlaced(vec, &interlaced));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeChromaSiting(vec, &chromaSiting));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePlaneLayouts(vec, &planeLayouts));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeDataspace(vec, &dataspace));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeBlendMode(vec, &blendMode));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2086(vec, &smpte2086));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeCta861_3(vec, &cta861_3));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_40(vec, &smpte2094_40));
+}
+
+class Gralloc4TestHelpers : public testing::Test { };
+
+TEST_F(Gralloc4TestHelpers, Gralloc4TestIsStandard) {
+ ASSERT_TRUE(gralloc4::isStandardMetadataType(gralloc4::MetadataType_BufferId));
+ ASSERT_TRUE(gralloc4::isStandardCompression(gralloc4::Compression_None));
+ ASSERT_TRUE(gralloc4::isStandardInterlaced(gralloc4::Interlaced_None));
+ ASSERT_TRUE(gralloc4::isStandardChromaSiting(gralloc4::ChromaSiting_None));
+ ASSERT_TRUE(gralloc4::isStandardPlaneLayoutComponentType(gralloc4::PlaneLayoutComponentType_Y));
+}
+
+TEST_F(Gralloc4TestHelpers, Gralloc4TestIsNotStandard) {
+ ASSERT_FALSE(gralloc4::isStandardMetadataType({"vendor.mycompanyname.graphics.common.MetadataType", 0}));
+ ASSERT_FALSE(gralloc4::isStandardCompression({"vendor.mycompanyname.graphics.common.Compression", 0}));
+ ASSERT_FALSE(gralloc4::isStandardInterlaced({"vendor.mycompanyname.graphics.common.Interlaced", 0}));
+ ASSERT_FALSE(gralloc4::isStandardChromaSiting({"vendor.mycompanyname.graphics.common.ChromaSiting", 0}));
+ ASSERT_FALSE(gralloc4::isStandardPlaneLayoutComponentType({"vendor.mycompanyname.graphics.common.PlaneLayoutComponentType", 0}));
+}
+
+TEST_F(Gralloc4TestHelpers, Gralloc4TestGetStandardValue) {
+ ASSERT_EQ(StandardMetadataType::BUFFER_ID, gralloc4::getStandardMetadataTypeValue(gralloc4::MetadataType_BufferId));
+ ASSERT_EQ(Compression::NONE, gralloc4::getStandardCompressionValue(gralloc4::Compression_None));
+ ASSERT_EQ(Interlaced::NONE, gralloc4::getStandardInterlacedValue(gralloc4::Interlaced_None));
+ ASSERT_EQ(ChromaSiting::NONE, gralloc4::getStandardChromaSitingValue(gralloc4::ChromaSiting_None));
+ ASSERT_EQ(PlaneLayoutComponentType::Y, gralloc4::getStandardPlaneLayoutComponentTypeValue(gralloc4::PlaneLayoutComponentType_Y));
+}
+
+} // namespace android
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index b9cf9e1..a5e5693 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -17,10 +17,14 @@
#undef LOG_TAG
#define LOG_TAG "BLASTBufferQueue"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include <gui/BLASTBufferQueue.h>
#include <gui/BufferItemConsumer.h>
#include <gui/GLConsumer.h>
+#include <utils/Trace.h>
+
#include <chrono>
using namespace std::chrono_literals;
@@ -29,23 +33,29 @@
BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height)
: mSurfaceControl(surface),
- mPendingCallbacks(0),
mWidth(width),
mHeight(height),
mNextTransaction(nullptr) {
BufferQueue::createBufferQueue(&mProducer, &mConsumer);
mConsumer->setMaxBufferCount(MAX_BUFFERS);
mProducer->setMaxDequeuedBufferCount(MAX_BUFFERS - 1);
+ mConsumer->setMaxAcquiredBufferCount(MAX_ACQUIRED_BUFFERS);
mBufferItemConsumer =
new BufferItemConsumer(mConsumer, AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, 1, true);
- mBufferItemConsumer->setName(String8("BLAST Consumer"));
+ static int32_t id = 0;
+ auto name = std::string("BLAST Consumer") + std::to_string(id);
+ id++;
+ mBufferItemConsumer->setName(String8(name.c_str()));
mBufferItemConsumer->setFrameAvailableListener(this);
mBufferItemConsumer->setBufferFreedListener(this);
mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888);
mBufferItemConsumer->setTransformHint(mSurfaceControl->getTransformHint());
- mAcquired = false;
+ mNumAcquired = 0;
+ mNumFrameAvailable = 0;
+ mPendingReleaseItem.item = BufferItem();
+ mPendingReleaseItem.releaseFence = nullptr;
}
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) {
@@ -70,34 +80,46 @@
void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
const std::vector<SurfaceControlStats>& stats) {
std::unique_lock _lock{mMutex};
+ ATRACE_CALL();
- if (stats.size() > 0 && !mShadowQueue.empty()) {
- mBufferItemConsumer->releaseBuffer(mNextCallbackBufferItem,
- stats[0].previousReleaseFence
- ? stats[0].previousReleaseFence
+ if (mPendingReleaseItem.item.mGraphicBuffer != nullptr) {
+ if (stats.size() > 0) {
+ mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
+ mTransformHint = stats[0].transformHint;
+ } else {
+ ALOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
+ mPendingReleaseItem.releaseFence = nullptr;
+ }
+ mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item,
+ mPendingReleaseItem.releaseFence
+ ? mPendingReleaseItem.releaseFence
: Fence::NO_FENCE);
- mAcquired = false;
- mNextCallbackBufferItem = BufferItem();
- mBufferItemConsumer->setTransformHint(stats[0].transformHint);
+ mNumAcquired--;
+ mPendingReleaseItem.item = BufferItem();
+ mPendingReleaseItem.releaseFence = nullptr;
}
- mPendingCallbacks--;
+
+ if (mSubmitted.empty()) {
+ ALOGE("ERROR: callback with no corresponding submitted buffer item");
+ }
+ mPendingReleaseItem.item = std::move(mSubmitted.front());
+ mSubmitted.pop();
processNextBufferLocked();
mCallbackCV.notify_all();
decStrong((void*)transactionCallbackThunk);
}
void BLASTBufferQueue::processNextBufferLocked() {
- if (mShadowQueue.empty()) {
+ ATRACE_CALL();
+ if (mNumFrameAvailable == 0) {
return;
}
- if (mAcquired) {
+ if (mSurfaceControl == nullptr) {
+ ALOGE("ERROR : surface control is null");
return;
}
- BufferItem item = std::move(mShadowQueue.front());
- mShadowQueue.pop();
-
SurfaceComposerClient::Transaction localTransaction;
bool applyTransaction = true;
SurfaceComposerClient::Transaction* t = &localTransaction;
@@ -107,33 +129,34 @@
applyTransaction = false;
}
- mNextCallbackBufferItem = mLastSubmittedBufferItem;
- mLastSubmittedBufferItem = BufferItem();
+ BufferItem bufferItem;
- status_t status = mBufferItemConsumer->acquireBuffer(&mLastSubmittedBufferItem, -1, false);
- mAcquired = true;
+ status_t status = mBufferItemConsumer->acquireBuffer(&bufferItem, -1, false);
if (status != OK) {
- ALOGE("Failed to acquire?");
- }
-
- auto buffer = mLastSubmittedBufferItem.mGraphicBuffer;
-
- if (buffer == nullptr) {
- ALOGE("Null buffer");
return;
}
+ auto buffer = bufferItem.mGraphicBuffer;
+ mNumFrameAvailable--;
+
+ if (buffer == nullptr) {
+ mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
+ return;
+ }
+
+ mNumAcquired++;
+ mSubmitted.push(bufferItem);
// Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
incStrong((void*)transactionCallbackThunk);
t->setBuffer(mSurfaceControl, buffer);
t->setAcquireFence(mSurfaceControl,
- item.mFence ? new Fence(item.mFence->dup()) : Fence::NO_FENCE);
+ bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
t->setFrame(mSurfaceControl, {0, 0, (int32_t)buffer->getWidth(), (int32_t)buffer->getHeight()});
- t->setCrop(mSurfaceControl, computeCrop(mLastSubmittedBufferItem));
- t->setTransform(mSurfaceControl, mLastSubmittedBufferItem.mTransform);
+ t->setCrop(mSurfaceControl, computeCrop(bufferItem));
+ t->setTransform(mSurfaceControl, bufferItem.mTransform);
if (applyTransaction) {
t->apply();
@@ -147,13 +170,13 @@
return item.mCrop;
}
-void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
+void BLASTBufferQueue::onFrameAvailable(const BufferItem& /*item*/) {
+ ATRACE_CALL();
std::lock_guard _lock{mMutex};
// add to shadow queue
- mShadowQueue.push(item);
+ mNumFrameAvailable++;
processNextBufferLocked();
- mPendingCallbacks++;
}
void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index a307d04..a159f85 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -998,6 +998,17 @@
item.mGraphicBuffer.clear();
}
+ // Update and get FrameEventHistory.
+ nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ NewFrameEventsEntry newFrameEventsEntry = {
+ currentFrameNumber,
+ postedTime,
+ requestedPresentTimestamp,
+ std::move(acquireFenceTime)
+ };
+ addAndGetFrameTimestamps(&newFrameEventsEntry,
+ getFrameTimestamps ? &output->frameTimestamps : nullptr);
+
// Call back without the main BufferQueue lock held, but with the callback
// lock held so we can ensure that callbacks occur in order
@@ -1027,17 +1038,6 @@
mCallbackCondition.notify_all();
}
- // Update and get FrameEventHistory.
- nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
- NewFrameEventsEntry newFrameEventsEntry = {
- currentFrameNumber,
- postedTime,
- requestedPresentTimestamp,
- std::move(acquireFenceTime)
- };
- addAndGetFrameTimestamps(&newFrameEventsEntry,
- getFrameTimestamps ? &output->frameTimestamps : nullptr);
-
// Wait without lock held
if (connectedApi == NATIVE_WINDOW_API_EGL) {
// Waiting here allows for two full buffers to be queued but not a
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index dd0b470..0a0a03c 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -68,23 +68,25 @@
std::mutex mMutex;
std::condition_variable mCallbackCV;
- uint64_t mPendingCallbacks GUARDED_BY(mMutex);
static const int MAX_BUFFERS = 3;
- struct BufferInfo {
- sp<GraphicBuffer> buffer;
- int fence;
+ static const int MAX_ACQUIRED_BUFFERS = 2;
+
+ int32_t mNumFrameAvailable GUARDED_BY(mMutex);
+ int32_t mNumAcquired GUARDED_BY(mMutex);
+
+ struct PendingReleaseItem {
+ BufferItem item;
+ sp<Fence> releaseFence;
};
- std::queue<const BufferItem> mShadowQueue GUARDED_BY(mMutex);
- bool mAcquired GUARDED_BY(mMutex);
+ std::queue<const BufferItem> mSubmitted GUARDED_BY(mMutex);
+ PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex);
int mWidth GUARDED_BY(mMutex);
int mHeight GUARDED_BY(mMutex);
- BufferItem mLastSubmittedBufferItem GUARDED_BY(mMutex);
- BufferItem mNextCallbackBufferItem GUARDED_BY(mMutex);
- sp<Fence> mLastFence GUARDED_BY(mMutex);
+ uint32_t mTransformHint GUARDED_BY(mMutex);
sp<IGraphicBufferConsumer> mConsumer;
sp<IGraphicBufferProducer> mProducer;
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 48a5cb6..0f618f1 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -69,7 +69,7 @@
void waitForCallbacks() {
std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
- while (mBlastBufferQueueAdapter->mPendingCallbacks > 0) {
+ while (mBlastBufferQueueAdapter->mSubmitted.size() > 0) {
mBlastBufferQueueAdapter->mCallbackCV.wait(lock);
}
}
@@ -302,7 +302,7 @@
igbProducer->cancelBuffer(allocated[i].first, allocated[i].second);
}
- for (int i = 0; i < 10; i++) {
+ for (int i = 0; i < 100; i++) {
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 200e1f3..d53a557 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -301,8 +301,8 @@
if (nWrite < 0) {
int error = errno;
#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.c_str(),
- msg->header.type, error);
+ ALOGD("channel '%s' ~ error sending message of type %d, %s", mName.c_str(),
+ msg->header.type, strerror(error));
#endif
if (error == EAGAIN || error == EWOULDBLOCK) {
return WOULD_BLOCK;
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index 4ce891e..33ab7c4 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -62,8 +62,26 @@
int warningTimeout = 3000;
int err = sync_wait(mFenceFd, warningTimeout);
if (err < 0 && errno == ETIME) {
- ALOGE("%s: fence %d didn't signal in %u ms", logname, mFenceFd.get(),
- warningTimeout);
+ ALOGE("waitForever: %s: fence %d didn't signal in %u ms", logname, mFenceFd.get(),
+ warningTimeout);
+
+ struct sync_file_info* finfo = sync_file_info(mFenceFd);
+ if (finfo) {
+ // status: active(0) signaled(1) error(<0)
+ ALOGI("waitForever: fence(%s) status(%d)", finfo->name, finfo->status);
+
+ struct sync_fence_info* pinfo = sync_get_fence_info(finfo);
+ for (uint32_t i = 0; i < finfo->num_fences; i++) {
+ uint64_t ts_sec = pinfo[i].timestamp_ns / 1000000000LL;
+ uint64_t ts_usec = (pinfo[i].timestamp_ns % 1000000000LL) / 1000LL;
+
+ ALOGI("waitForever: sync point: timeline(%s) drv(%s) status(%d) timestamp(%" PRIu64
+ ".%06" PRIu64 ")",
+ pinfo[i].obj_name, pinfo[i].driver_name, pinfo[i].status, ts_sec, ts_usec);
+ }
+ sync_file_info_free(finfo);
+ }
+
err = sync_wait(mFenceFd, TIMEOUT_NEVER);
}
return err < 0 ? -errno : status_t(NO_ERROR);
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 2c897cf..30c48c8 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -614,6 +614,24 @@
outBlendMode);
}
+status_t Gralloc4Mapper::getSmpte2086(buffer_handle_t bufferHandle,
+ std::optional<ui::Smpte2086>* outSmpte2086) const {
+ return get(bufferHandle, gralloc4::MetadataType_Smpte2086, gralloc4::decodeSmpte2086,
+ outSmpte2086);
+}
+
+status_t Gralloc4Mapper::getCta861_3(buffer_handle_t bufferHandle,
+ std::optional<ui::Cta861_3>* outCta861_3) const {
+ return get(bufferHandle, gralloc4::MetadataType_Cta861_3, gralloc4::decodeCta861_3,
+ outCta861_3);
+}
+
+status_t Gralloc4Mapper::getSmpte2094_40(
+ buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_40) const {
+ return get(bufferHandle, gralloc4::MetadataType_Smpte2094_40, gralloc4::decodeSmpte2094_40,
+ outSmpte2094_40);
+}
+
template <class T>
status_t Gralloc4Mapper::getDefault(uint32_t width, uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage,
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 8540fd3..d20bd7a 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -286,6 +286,21 @@
return mMapper->getBlendMode(bufferHandle, outBlendMode);
}
+status_t GraphicBufferMapper::getSmpte2086(buffer_handle_t bufferHandle,
+ std::optional<ui::Smpte2086>* outSmpte2086) {
+ return mMapper->getSmpte2086(bufferHandle, outSmpte2086);
+}
+
+status_t GraphicBufferMapper::getCta861_3(buffer_handle_t bufferHandle,
+ std::optional<ui::Cta861_3>* outCta861_3) {
+ return mMapper->getCta861_3(bufferHandle, outCta861_3);
+}
+
+status_t GraphicBufferMapper::getSmpte2094_40(
+ buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_40) {
+ return mMapper->getSmpte2094_40(bufferHandle, outSmpte2094_40);
+}
+
status_t GraphicBufferMapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
PixelFormat format, uint32_t layerCount,
uint64_t usage,
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index fcd959c..e199648 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -165,6 +165,19 @@
ui::BlendMode* /*outBlendMode*/) const {
return INVALID_OPERATION;
}
+ virtual status_t getSmpte2086(buffer_handle_t /*bufferHandle*/,
+ std::optional<ui::Smpte2086>* /*outSmpte2086*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getCta861_3(buffer_handle_t /*bufferHandle*/,
+ std::optional<ui::Cta861_3>* /*outCta861_3*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getSmpte2094_40(
+ buffer_handle_t /*bufferHandle*/,
+ std::optional<std::vector<uint8_t>>* /*outSmpte2094_40*/) const {
+ return INVALID_OPERATION;
+ }
virtual status_t getDefaultPixelFormatFourCC(uint32_t /*width*/, uint32_t /*height*/,
PixelFormat /*format*/, uint32_t /*layerCount*/,
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index af7c076..4729cba 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -102,6 +102,12 @@
std::vector<ui::PlaneLayout>* outPlaneLayouts) const override;
status_t getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace) const override;
status_t getBlendMode(buffer_handle_t bufferHandle, ui::BlendMode* outBlendMode) const override;
+ status_t getSmpte2086(buffer_handle_t bufferHandle,
+ std::optional<ui::Smpte2086>* outSmpte2086) const override;
+ status_t getCta861_3(buffer_handle_t bufferHandle,
+ std::optional<ui::Cta861_3>* outCta861_3) const override;
+ status_t getSmpte2094_40(buffer_handle_t bufferHandle,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_40) const override;
status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage,
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 77c00ae..837e3d8 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -122,6 +122,10 @@
std::vector<ui::PlaneLayout>* outPlaneLayouts);
status_t getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace);
status_t getBlendMode(buffer_handle_t bufferHandle, ui::BlendMode* outBlendMode);
+ status_t getSmpte2086(buffer_handle_t bufferHandle, std::optional<ui::Smpte2086>* outSmpte2086);
+ status_t getCta861_3(buffer_handle_t bufferHandle, std::optional<ui::Cta861_3>* outCta861_3);
+ status_t getSmpte2094_40(buffer_handle_t bufferHandle,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_40);
/**
* Gets the default metadata for a gralloc buffer allocated with the given parameters.
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index ad5ee80..4bdacb0 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -19,8 +19,10 @@
#include <aidl/android/hardware/graphics/common/BlendMode.h>
#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
#include <aidl/android/hardware/graphics/common/Compression.h>
+#include <aidl/android/hardware/graphics/common/Cta861_3.h>
#include <aidl/android/hardware/graphics/common/Interlaced.h>
#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <aidl/android/hardware/graphics/common/Smpte2086.h>
#include <android/hardware/graphics/common/1.1/types.h>
#include <android/hardware/graphics/common/1.2/types.h>
#include <system/graphics.h>
@@ -47,7 +49,9 @@
* Stable AIDL types
*/
using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::Cta861_3;
using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::Smpte2086;
/**
* The following stable AIDL types below have standard platform definitions
diff --git a/libs/vr/OWNERS b/libs/vr/OWNERS
index ec2d712..098c80e 100644
--- a/libs/vr/OWNERS
+++ b/libs/vr/OWNERS
@@ -1,4 +1,4 @@
hendrikw@google.com
jwcai@google.com
steventhomas@google.com
-
+patplunkett@google.com
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
index 1a9d727..23a4224 100644
--- a/libs/vr/libpdx/Android.bp
+++ b/libs/vr/libpdx/Android.bp
@@ -53,6 +53,8 @@
"libpdx",
"liblog",
"libutils",
+ ],
+ shared_libs: [
"libvndksupport",
],
}
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
new file mode 100644
index 0000000..385b981
--- /dev/null
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -0,0 +1,22 @@
+cc_benchmark {
+ name: "inputflinger_benchmarks",
+ srcs: [
+ "InputDispatcher_benchmarks.cpp",
+ ],
+ defaults: ["inputflinger_defaults"],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "libinput",
+ "libinputflinger_base",
+ "libinputreporter",
+ "liblog",
+ "libstatslog",
+ "libui",
+ "libutils",
+ ],
+ static_libs: [
+ "libinputdispatcher",
+ ],
+}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
new file mode 100644
index 0000000..9686cea
--- /dev/null
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -0,0 +1,314 @@
+/*
+ * 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.
+ */
+
+#include <benchmark/benchmark.h>
+
+#include <binder/Binder.h>
+#include "../dispatcher/InputDispatcher.h"
+
+namespace android::inputdispatcher {
+
+// An arbitrary device id.
+static const int32_t DEVICE_ID = 1;
+
+// An arbitrary injector pid / uid pair that has permission to inject events.
+static const int32_t INJECTOR_PID = 999;
+static const int32_t INJECTOR_UID = 1001;
+
+static const int32_t INJECT_EVENT_TIMEOUT = 5000;
+static const int32_t DISPATCHING_TIMEOUT = 100000;
+
+static nsecs_t now() {
+ return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+// --- FakeInputDispatcherPolicy ---
+
+class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
+public:
+ FakeInputDispatcherPolicy() {}
+
+protected:
+ virtual ~FakeInputDispatcherPolicy() {}
+
+private:
+ virtual void notifyConfigurationChanged(nsecs_t) override {}
+
+ virtual nsecs_t notifyANR(const sp<InputApplicationHandle>&, const sp<IBinder>&,
+ const std::string& name) override {
+ ALOGE("The window is not responding : %s", name.c_str());
+ return 0;
+ }
+
+ virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
+
+ virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+
+ virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
+ *outConfig = mConfig;
+ }
+
+ virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+ return true;
+ }
+
+ virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
+
+ virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+
+ virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*,
+ uint32_t) override {
+ return 0;
+ }
+
+ virtual bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t,
+ KeyEvent*) override {
+ return false;
+ }
+
+ virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
+
+ virtual void pokeUserActivity(nsecs_t, int32_t) override {}
+
+ virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override {
+ return false;
+ }
+
+ virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
+
+ InputDispatcherConfiguration mConfig;
+};
+
+class FakeApplicationHandle : public InputApplicationHandle {
+public:
+ FakeApplicationHandle() {}
+ virtual ~FakeApplicationHandle() {}
+
+ virtual bool updateInfo() {
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+ return true;
+ }
+};
+
+class FakeInputReceiver {
+public:
+ void consumeEvent() {
+ uint32_t consumeSeq;
+ InputEvent* event;
+
+ status_t result = WOULD_BLOCK;
+ while (result == WOULD_BLOCK) {
+ result = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq,
+ &event);
+ }
+ if (result != OK) {
+ ALOGE("Received result = %d from consume()", result);
+ }
+ result = mConsumer->sendFinishedSignal(consumeSeq, true);
+ if (result != OK) {
+ ALOGE("Received result = %d from sendFinishedSignal", result);
+ }
+ }
+
+protected:
+ explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, const std::string name)
+ : mDispatcher(dispatcher) {
+ InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
+ mConsumer = std::make_unique<InputConsumer>(mClientChannel);
+ }
+
+ virtual ~FakeInputReceiver() {}
+
+ sp<InputDispatcher> mDispatcher;
+ sp<InputChannel> mServerChannel, mClientChannel;
+ std::unique_ptr<InputConsumer> mConsumer;
+ PreallocatedInputEventFactory mEventFactory;
+};
+
+class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver {
+public:
+ static const int32_t WIDTH = 200;
+ static const int32_t HEIGHT = 200;
+
+ FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<InputDispatcher>& dispatcher, const std::string name)
+ : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
+ mDispatcher->registerInputChannel(mServerChannel);
+
+ inputApplicationHandle->updateInfo();
+ mInfo.applicationInfo = *inputApplicationHandle->getInfo();
+ }
+
+ virtual bool updateInfo() override {
+ mInfo.token = mServerChannel->getConnectionToken();
+ mInfo.name = "FakeWindowHandle";
+ mInfo.layoutParamsFlags = 0;
+ 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 = true;
+ mInfo.hasWallpaper = false;
+ mInfo.paused = false;
+ mInfo.layer = 0;
+ mInfo.ownerPid = INJECTOR_PID;
+ mInfo.ownerUid = INJECTOR_UID;
+ mInfo.inputFeatures = 0;
+ mInfo.displayId = ADISPLAY_ID_DEFAULT;
+
+ return true;
+ }
+
+protected:
+ Rect mFrame;
+};
+
+static MotionEvent generateMotionEvent() {
+ PointerProperties pointerProperties[1];
+ PointerCoords pointerCoords[1];
+
+ pointerProperties[0].clear();
+ pointerProperties[0].id = 0;
+ pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ pointerCoords[0].clear();
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 100);
+
+ const nsecs_t currentTime = now();
+
+ MotionEvent event;
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0,
+ /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+ /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
+ /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime,
+ /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ return event;
+}
+
+static NotifyMotionArgs generateMotionArgs() {
+ PointerProperties pointerProperties[1];
+ PointerCoords pointerCoords[1];
+
+ pointerProperties[0].clear();
+ pointerProperties[0].id = 0;
+ pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ pointerCoords[0].clear();
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 100);
+
+ const nsecs_t currentTime = now();
+ // Define a valid motion event.
+ NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, POLICY_FLAG_PASS_TO_USER, AMOTION_EVENT_ACTION_DOWN,
+ /* actionButton */ 0, /* flags */ 0, AMETA_NONE, /* buttonState */ 0,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ pointerProperties, pointerCoords,
+ /* xPrecision */ 0, /* yPrecision */ 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {});
+
+ return args;
+}
+
+static void benchmarkNotifyMotion(benchmark::State& state) {
+ // Create dispatcher
+ sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy();
+ sp<InputDispatcher> dispatcher = new InputDispatcher(fakePolicy);
+ dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+ dispatcher->start();
+
+ // Create a window that will receive motion events
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
+
+ dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ NotifyMotionArgs motionArgs = generateMotionArgs();
+
+ for (auto _ : state) {
+ // Send ACTION_DOWN
+ motionArgs.action = AMOTION_EVENT_ACTION_DOWN;
+ motionArgs.sequenceNum = 0;
+ motionArgs.downTime = now();
+ motionArgs.eventTime = motionArgs.downTime;
+ dispatcher->notifyMotion(&motionArgs);
+
+ // Send ACTION_UP
+ motionArgs.action = AMOTION_EVENT_ACTION_UP;
+ motionArgs.sequenceNum = 1;
+ motionArgs.eventTime = now();
+ dispatcher->notifyMotion(&motionArgs);
+
+ window->consumeEvent();
+ window->consumeEvent();
+ }
+
+ dispatcher->stop();
+}
+
+static void benchmarkInjectMotion(benchmark::State& state) {
+ // Create dispatcher
+ sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy();
+ sp<InputDispatcher> dispatcher = new InputDispatcher(fakePolicy);
+ dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+ dispatcher->start();
+
+ // Create a window that will receive motion events
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
+
+ dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ MotionEvent event = generateMotionEvent();
+
+ for (auto _ : state) {
+ // 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);
+
+ // Send ACTION_UP
+ event.setAction(AMOTION_EVENT_ACTION_UP);
+ dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT,
+ POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+
+ window->consumeEvent();
+ window->consumeEvent();
+ }
+
+ dispatcher->stop();
+}
+
+BENCHMARK(benchmarkNotifyMotion);
+BENCHMARK(benchmarkInjectMotion);
+
+} // namespace android::inputdispatcher
+
+BENCHMARK_MAIN();
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index ada2266..09ecb13 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -4,6 +4,7 @@
name: "inputflinger_tests",
srcs: [
"BlockingQueue_test.cpp",
+ "EventHub_test.cpp",
"TestInputListener.cpp",
"InputClassifier_test.cpp",
"InputClassifierConverter_test.cpp",
@@ -36,4 +37,5 @@
header_libs: [
"libinputreader_headers",
],
+ require_root: true,
}
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
new file mode 100644
index 0000000..6504738
--- /dev/null
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -0,0 +1,238 @@
+/*
+ * 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.
+ */
+
+#include "EventHub.h"
+
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <linux/uinput.h>
+#include <log/log.h>
+#include <chrono>
+
+#define TAG "EventHub_test"
+
+using android::EventHub;
+using android::EventHubInterface;
+using android::InputDeviceIdentifier;
+using android::RawEvent;
+using android::sp;
+using android::base::StringPrintf;
+using std::chrono_literals::operator""ms;
+
+static constexpr bool DEBUG = false;
+static const char* DEVICE_NAME = "EventHub Test Device";
+
+static void dumpEvents(const std::vector<RawEvent>& events) {
+ for (const RawEvent& event : events) {
+ if (event.type >= EventHubInterface::FIRST_SYNTHETIC_EVENT) {
+ switch (event.type) {
+ case EventHubInterface::DEVICE_ADDED:
+ ALOGI("Device added: %i", event.deviceId);
+ break;
+ case EventHubInterface::DEVICE_REMOVED:
+ ALOGI("Device removed: %i", event.deviceId);
+ break;
+ case EventHubInterface::FINISHED_DEVICE_SCAN:
+ ALOGI("Finished device scan.");
+ break;
+ }
+ } else {
+ ALOGI("Device %" PRId32 " : time = %" PRId64 ", type %i, code %i, value %i",
+ event.deviceId, event.when, event.type, event.code, event.value);
+ }
+ }
+}
+
+// --- EventHubTest ---
+class EventHubTest : public testing::Test {
+protected:
+ std::unique_ptr<EventHubInterface> mEventHub;
+ // We are only going to emulate a single input device currently.
+ android::base::unique_fd mDeviceFd;
+ int32_t mDeviceId;
+ virtual void SetUp() override {
+ mEventHub = std::make_unique<EventHub>();
+ consumeInitialDeviceAddedEvents();
+ createDevice();
+ mDeviceId = waitForDeviceCreation();
+ }
+ virtual void TearDown() override {
+ mDeviceFd.reset();
+ waitForDeviceClose(mDeviceId);
+ }
+
+ void createDevice();
+ /**
+ * Return the device id of the created device.
+ */
+ int32_t waitForDeviceCreation();
+ void waitForDeviceClose(int32_t deviceId);
+ void consumeInitialDeviceAddedEvents();
+ void sendEvent(uint16_t type, uint16_t code, int32_t value);
+ std::vector<RawEvent> getEvents(std::chrono::milliseconds timeout = 5ms);
+};
+
+std::vector<RawEvent> EventHubTest::getEvents(std::chrono::milliseconds timeout) {
+ static constexpr size_t EVENT_BUFFER_SIZE = 256;
+ std::array<RawEvent, EVENT_BUFFER_SIZE> eventBuffer;
+ std::vector<RawEvent> events;
+
+ while (true) {
+ size_t count =
+ mEventHub->getEvents(timeout.count(), eventBuffer.data(), eventBuffer.size());
+ if (count == 0) {
+ break;
+ }
+ events.insert(events.end(), eventBuffer.begin(), eventBuffer.begin() + count);
+ }
+ if (DEBUG) {
+ dumpEvents(events);
+ }
+ return events;
+}
+
+void EventHubTest::createDevice() {
+ mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
+ if (mDeviceFd < 0) {
+ FAIL() << "Can't open /dev/uinput :" << strerror(errno);
+ }
+
+ /**
+ * Signal which type of events this input device supports.
+ * We will emulate a keyboard here.
+ */
+ // enable key press/release event
+ if (ioctl(mDeviceFd, UI_SET_EVBIT, EV_KEY)) {
+ ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_KEY: " << strerror(errno);
+ }
+
+ // enable set of KEY events
+ if (ioctl(mDeviceFd, UI_SET_KEYBIT, KEY_HOME)) {
+ ADD_FAILURE() << "Error in ioctl : UI_SET_KEYBIT : KEY_HOME: " << strerror(errno);
+ }
+
+ // enable synchronization event
+ if (ioctl(mDeviceFd, UI_SET_EVBIT, EV_SYN)) {
+ ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_SYN: " << strerror(errno);
+ }
+
+ struct uinput_user_dev keyboard = {};
+ strlcpy(keyboard.name, DEVICE_NAME, UINPUT_MAX_NAME_SIZE);
+ keyboard.id.bustype = BUS_USB;
+ keyboard.id.vendor = 0x01;
+ keyboard.id.product = 0x01;
+ keyboard.id.version = 1;
+
+ if (write(mDeviceFd, &keyboard, sizeof(keyboard)) < 0) {
+ FAIL() << "Could not write uinput_user_dev struct into uinput file descriptor: "
+ << strerror(errno);
+ }
+
+ if (ioctl(mDeviceFd, UI_DEV_CREATE)) {
+ FAIL() << "Error in ioctl : UI_DEV_CREATE: " << strerror(errno);
+ }
+}
+
+/**
+ * Since the test runs on a real platform, there will be existing devices
+ * in addition to the test devices being added. Therefore, when EventHub is first created,
+ * it will return a lot of "device added" type of events.
+ */
+void EventHubTest::consumeInitialDeviceAddedEvents() {
+ std::vector<RawEvent> events = getEvents(0ms);
+ std::set<int32_t /*deviceId*/> existingDevices;
+ // All of the events should be DEVICE_ADDED type, except the last one.
+ for (size_t i = 0; i < events.size() - 1; i++) {
+ const RawEvent& event = events[i];
+ EXPECT_EQ(EventHubInterface::DEVICE_ADDED, event.type);
+ existingDevices.insert(event.deviceId);
+ }
+ // None of the existing system devices should be changing while this test is run.
+ // Check that the returned device ids are unique for all of the existing devices.
+ EXPECT_EQ(existingDevices.size(), events.size() - 1);
+ // The last event should be "finished device scan"
+ EXPECT_EQ(EventHubInterface::FINISHED_DEVICE_SCAN, events[events.size() - 1].type);
+}
+
+int32_t EventHubTest::waitForDeviceCreation() {
+ // Wait a little longer than usual, to ensure input device has time to be created
+ std::vector<RawEvent> events = getEvents(20ms);
+ EXPECT_EQ(2U, events.size()); // Using "expect" because the function is non-void.
+ const RawEvent& deviceAddedEvent = events[0];
+ EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_ADDED), deviceAddedEvent.type);
+ InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceAddedEvent.deviceId);
+ const int32_t deviceId = deviceAddedEvent.deviceId;
+ EXPECT_EQ(identifier.name, DEVICE_NAME);
+ const RawEvent& finishedDeviceScanEvent = events[1];
+ EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
+ finishedDeviceScanEvent.type);
+ return deviceId;
+}
+
+void EventHubTest::waitForDeviceClose(int32_t deviceId) {
+ std::vector<RawEvent> events = getEvents(20ms);
+ ASSERT_EQ(2U, events.size());
+ const RawEvent& deviceRemovedEvent = events[0];
+ EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_REMOVED), deviceRemovedEvent.type);
+ EXPECT_EQ(deviceId, deviceRemovedEvent.deviceId);
+ const RawEvent& finishedDeviceScanEvent = events[1];
+ EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
+ finishedDeviceScanEvent.type);
+}
+
+void EventHubTest::sendEvent(uint16_t type, uint16_t code, int32_t value) {
+ struct input_event event = {};
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ event.time = {}; // uinput ignores the timestamp
+
+ if (write(mDeviceFd, &event, sizeof(input_event)) < 0) {
+ std::string msg = StringPrintf("Could not write event %" PRIu16 " %" PRIu16
+ " with value %" PRId32 " : %s",
+ type, code, value, strerror(errno));
+ ALOGE("%s", msg.c_str());
+ ADD_FAILURE() << msg.c_str();
+ }
+}
+
+/**
+ * Ensure that input_events are generated with monotonic clock.
+ * That means input_event should receive a timestamp that is in the future of the time
+ * before the event was sent.
+ * Input system uses CLOCK_MONOTONIC everywhere in the code base.
+ */
+TEST_F(EventHubTest, InputEvent_TimestampIsMonotonic) {
+ nsecs_t lastEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ // key press
+ sendEvent(EV_KEY, KEY_HOME, 1);
+ sendEvent(EV_SYN, SYN_REPORT, 0);
+
+ // key release
+ sendEvent(EV_KEY, KEY_HOME, 0);
+ sendEvent(EV_SYN, SYN_REPORT, 0);
+
+ std::vector<RawEvent> events = getEvents();
+ ASSERT_EQ(4U, events.size()) << "Expected to receive 2 keys and 2 syncs, total of 4 events";
+ for (const RawEvent& event : events) {
+ // Cannot use strict comparison because the events may happen too quickly
+ ASSERT_LE(lastEventTime, event.when) << "Event must have occurred after the key was sent";
+ ASSERT_LT(std::chrono::nanoseconds(event.when - lastEventTime), 100ms)
+ << "Event times are too far apart";
+ lastEventTime = event.when; // Ensure all returned events are monotonic
+ }
+}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 0701f3b..b7f7ac5 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -418,7 +418,7 @@
std::chrono::time_point start = std::chrono::steady_clock::now();
status_t status = WOULD_BLOCK;
while (status == WOULD_BLOCK) {
- status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1, &consumeSeq,
+ status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq,
&event);
std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
if (elapsed > 100ms) {
@@ -505,7 +505,7 @@
const std::string name, int32_t displayId) :
mDispatcher(dispatcher), mName(name), mDisplayId(displayId) {
InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
- mConsumer = new InputConsumer(mClientChannel);
+ mConsumer = std::make_unique<InputConsumer>(mClientChannel);
}
virtual ~FakeInputReceiver() {
@@ -518,7 +518,7 @@
sp<InputDispatcher> mDispatcher;
sp<InputChannel> mServerChannel, mClientChannel;
- InputConsumer *mConsumer;
+ std::unique_ptr<InputConsumer> mConsumer;
PreallocatedInputEventFactory mEventFactory;
std::string mName;
@@ -932,6 +932,32 @@
monitor->consumeMotionUp(ADISPLAY_ID_DEFAULT);
}
+TEST_F(InputDispatcherTest, TestMoveEvent) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->notifyMotion(&motionArgs);
+ // Window should receive motion down event.
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ motionArgs.action = AMOTION_EVENT_ACTION_MOVE;
+ motionArgs.sequenceNum += 1;
+ motionArgs.eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ motionArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
+ motionArgs.pointerCoords[0].getX() - 10);
+
+ mDispatcher->notifyMotion(&motionArgs);
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE, ADISPLAY_ID_DEFAULT,
+ 0 /*expectedFlags*/);
+}
+
/* Test InputDispatcher for MultiDisplay */
class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
public:
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 16855d2..5f5532e 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -190,7 +190,7 @@
static bool getOpacityForFormat(uint32_t format);
- // from GLES
+ // from graphics API
const uint32_t mTextureName;
bool mRefreshPending{false};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
index 0e67acf..6559ed8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
@@ -45,19 +45,19 @@
// prepareFrame is called after the composition configuration is known but
// before composition takes place. The DisplaySurface can use the
// composition type to decide how to manage the flow of buffers between
- // GLES and HWC for this frame.
+ // GPU and HWC for this frame.
enum CompositionType {
COMPOSITION_UNKNOWN = 0,
- COMPOSITION_GLES = 1,
+ COMPOSITION_GPU = 1,
COMPOSITION_HWC = 2,
- COMPOSITION_MIXED = COMPOSITION_GLES | COMPOSITION_HWC
+ COMPOSITION_MIXED = COMPOSITION_GPU | COMPOSITION_HWC
};
virtual status_t prepareFrame(CompositionType compositionType) = 0;
- // Inform the surface that GLES composition is complete for this frame, and
+ // Inform the surface that GPU composition is complete for this frame, and
// the surface should make sure that HWComposer has the correct buffer for
// this frame. Some implementations may only push a new buffer to
- // HWComposer if GLES composition took place, others need to push a new
+ // HWComposer if GPU composition took place, others need to push a new
// buffer on every frame.
//
// advanceFrame must be followed by a call to onFrameCommitted before
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index ce0222c..82d2422 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -231,7 +231,7 @@
* (NOTE: the matrices are multiplied in reverse order)
*/
const ui::Transform& layerTransform = layerState.geomLayerTransform;
- const ui::Transform displayTransform{outputState.orientation};
+ const ui::Transform displayTransform{outputState.transform};
const ui::Transform bufferTransform{layerState.geomBufferTransform};
ui::Transform transform(displayTransform * layerTransform * bufferTransform);
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 5ed21fc..949feb4 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -117,12 +117,12 @@
if (usesClientComposition && usesDeviceComposition) {
compositionType = DisplaySurface::COMPOSITION_MIXED;
} else if (usesClientComposition) {
- compositionType = DisplaySurface::COMPOSITION_GLES;
+ compositionType = DisplaySurface::COMPOSITION_GPU;
} else if (usesDeviceComposition) {
compositionType = DisplaySurface::COMPOSITION_HWC;
} else {
// Nothing to do -- when turning the screen off we get a frame like
- // this. Call it a HWC frame since we won't be doing any GLES work but
+ // this. Call it a HWC frame since we won't be doing any GPU work but
// will do a prepare/set cycle.
compositionType = DisplaySurface::COMPOSITION_HWC;
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 2e030a1..0e579fa 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -364,6 +364,7 @@
mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
mLayerFEState.geomBufferTransform = entry.buffer;
mOutputState.orientation = entry.display;
+ mOutputState.transform = ui::Transform{entry.display};
auto actual = mOutputLayer.calculateOutputRelativeBufferTransform();
EXPECT_EQ(entry.expected, actual) << "entry " << i;
@@ -422,6 +423,7 @@
mLayerFEState.geomLayerTransform = ui::Transform{entry.layer};
mLayerFEState.geomBufferTransform = entry.buffer;
mOutputState.orientation = entry.display;
+ mOutputState.transform = ui::Transform{entry.display};
auto actual = mOutputLayer.calculateOutputRelativeBufferTransform();
EXPECT_EQ(entry.expected, actual) << "entry " << i;
@@ -764,6 +766,21 @@
mOutputLayer.writeStateToHWC(true);
}
+TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) {
+ mLayerFEState.geomBufferUsesDisplayInverseTransform = false;
+ mLayerFEState.geomLayerTransform = ui::Transform{TR_IDENT};
+ // This test simulates a scenario where displayInstallOrientation is set to
+ // ROT_90. This only has an effect on the transform; orientation stays 0 (see
+ // DisplayDevice::setProjection).
+ mOutputState.orientation = TR_IDENT;
+ mOutputState.transform = ui::Transform{TR_ROT_90};
+ // Buffers are pre-rotated based on the transform hint (ROT_90); their
+ // geomBufferTransform is set to the inverse transform.
+ mLayerFEState.geomBufferTransform = TR_ROT_270;
+
+ EXPECT_EQ(TR_IDENT, mOutputLayer.calculateOutputRelativeBufferTransform());
+}
+
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSolidColor) {
mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index da3f4fb..fd47e45 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -192,8 +192,8 @@
mSurface.prepareFrame(true, true);
}
-TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGlesComposition) {
- EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GLES))
+TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGpuComposition) {
+ EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GPU))
.WillOnce(Return(NO_ERROR));
mSurface.prepareFrame(true, false);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 34254e0..4f96ad3 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -62,8 +62,11 @@
class ComposerCallbackBridge : public Hwc2::IComposerCallback {
public:
- ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId)
- : mCallback(callback), mSequenceId(sequenceId) {}
+ ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId,
+ bool vsyncSwitchingSupported)
+ : mCallback(callback),
+ mSequenceId(sequenceId),
+ mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
Return<void> onHotplug(Hwc2::Display display,
IComposerCallback::Connection conn) override
@@ -81,15 +84,23 @@
Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
{
- mCallback->onVsyncReceived(mSequenceId, display, timestamp, std::nullopt);
+ 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 {
- // TODO(b/140201379): use vsyncPeriodNanos in the new DispSync
- mCallback->onVsyncReceived(mSequenceId, display, timestamp,
- std::make_optional(vsyncPeriodNanos));
+ 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();
}
@@ -107,6 +118,7 @@
private:
ComposerCallback* mCallback;
int32_t mSequenceId;
+ const bool mVsyncSwitchingSupported;
};
} // namespace anonymous
@@ -126,7 +138,8 @@
}
mRegisteredCallback = true;
sp<ComposerCallbackBridge> callbackBridge(
- new ComposerCallbackBridge(callback, sequenceId));
+ new ComposerCallbackBridge(callback, sequenceId,
+ mComposer->isVsyncPeriodSwitchSupported()));
mComposer->registerCallback(callbackBridge);
}
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 4e0e4df..56b0ea6 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -42,13 +42,14 @@
switch (type) {
case compositionengine::DisplaySurface::COMPOSITION_UNKNOWN:
return "UNKNOWN";
- case compositionengine::DisplaySurface::COMPOSITION_GLES:
- return "GLES";
+ case compositionengine::DisplaySurface::COMPOSITION_GPU:
+ return "GPU";
case compositionengine::DisplaySurface::COMPOSITION_HWC:
return "HWC";
case compositionengine::DisplaySurface::COMPOSITION_MIXED:
return "MIXED";
- default: return "<INVALID>";
+ default:
+ return "<INVALID>";
}
}
@@ -92,7 +93,7 @@
mSinkBufferHeight = sinkHeight;
// Pick the buffer format to request from the sink when not rendering to it
- // with GLES. If the consumer needs CPU access, use the default format
+ // with GPU. If the consumer needs CPU access, use the default format
// set by the consumer. Otherwise allow gralloc to decide the format based
// on usage bits.
int sinkUsage;
@@ -143,10 +144,10 @@
mDbgState = DBG_STATE_PREPARED;
mCompositionType = compositionType;
- if (mForceHwcCopy && mCompositionType == COMPOSITION_GLES) {
+ if (mForceHwcCopy && mCompositionType == COMPOSITION_GPU) {
// Some hardware can do RGB->YUV conversion more efficiently in hardware
// controlled by HWC than in hardware controlled by the video encoder.
- // Forcing GLES-composed frames to go through an extra copy by the HWC
+ // Forcing GPU-composed frames to go through an extra copy by the HWC
// allows the format conversion to happen there, rather than passing RGB
// directly to the consumer.
//
@@ -161,18 +162,17 @@
mDbgLastCompositionType = mCompositionType;
}
- if (mCompositionType != COMPOSITION_GLES &&
- (mOutputFormat != mDefaultOutputFormat ||
- mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) {
- // We must have just switched from GLES-only to MIXED or HWC
- // composition. Stop using the format and usage requested by the GLES
+ if (mCompositionType != COMPOSITION_GPU &&
+ (mOutputFormat != mDefaultOutputFormat || mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) {
+ // We must have just switched from GPU-only to MIXED or HWC
+ // composition. Stop using the format and usage requested by the GPU
// driver; they may be suboptimal when HWC is writing to the output
// buffer. For example, if the output is going to a video encoder, and
// HWC can write directly to YUV, some hardware can skip a
// memory-to-memory RGB-to-YUV conversion step.
//
- // If we just switched *to* GLES-only mode, we'll change the
- // format/usage and get a new buffer when the GLES driver calls
+ // If we just switched *to* GPU-only mode, we'll change the
+ // format/usage and get a new buffer when the GPU driver calls
// dequeueBuffer().
mOutputFormat = mDefaultOutputFormat;
mOutputUsage = GRALLOC_USAGE_HW_COMPOSER;
@@ -192,17 +192,16 @@
"Unexpected advanceFrame() in %s state on HWC frame",
dbgStateStr());
} else {
- VDS_LOGW_IF(mDbgState != DBG_STATE_GLES_DONE,
- "Unexpected advanceFrame() in %s state on GLES/MIXED frame",
- dbgStateStr());
+ VDS_LOGW_IF(mDbgState != DBG_STATE_GPU_DONE,
+ "Unexpected advanceFrame() in %s state on GPU/MIXED frame", dbgStateStr());
}
mDbgState = DBG_STATE_HWC;
if (mOutputProducerSlot < 0 ||
(mCompositionType != COMPOSITION_HWC && mFbProducerSlot < 0)) {
// Last chance bailout if something bad happened earlier. For example,
- // in a GLES configuration, if the sink disappears then dequeueBuffer
- // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
+ // in a graphics API configuration, if the sink disappears then dequeueBuffer
+ // will fail, the GPU driver won't queue a buffer, but SurfaceFlinger
// will soldier on. So we end up here without a buffer. There should
// be lots of scary messages in the log just before this.
VDS_LOGE("advanceFrame: no buffer, bailing out");
@@ -302,9 +301,8 @@
return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
- "Unexpected requestBuffer pslot=%d in %s state",
- pslot, dbgStateStr());
+ VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected requestBuffer pslot=%d in %s state", pslot,
+ dbgStateStr());
*outBuf = mProducerBuffers[pslot];
return NO_ERROR;
@@ -374,7 +372,7 @@
VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
"Unexpected dequeueBuffer() in %s state", dbgStateStr());
- mDbgState = DBG_STATE_GLES;
+ mDbgState = DBG_STATE_GPU;
VDS_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#" PRIx64, w, h, format, usage);
@@ -385,18 +383,18 @@
if (mOutputProducerSlot < 0) {
// Last chance bailout if something bad happened earlier. For example,
- // in a GLES configuration, if the sink disappears then dequeueBuffer
- // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
+ // in a graphics API configuration, if the sink disappears then dequeueBuffer
+ // will fail, the GPU driver won't queue a buffer, but SurfaceFlinger
// will soldier on. So we end up here without a buffer. There should
// be lots of scary messages in the log just before this.
VDS_LOGE("dequeueBuffer: no buffer, bailing out");
return NO_MEMORY;
}
- // We already dequeued the output buffer. If the GLES driver wants
+ // We already dequeued the output buffer. If the GPU driver wants
// something incompatible, we have to cancel and get a new one. This
// will mean that HWC will see a different output buffer between
- // prepare and set, but since we're in GLES-only mode already it
+ // prepare and set, but since we're in GPU-only mode already it
// shouldn't matter.
usage |= GRALLOC_USAGE_HW_COMPOSER;
@@ -458,10 +456,9 @@
return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
- "Unexpected queueBuffer(pslot=%d) in %s state", pslot,
- dbgStateStr());
- mDbgState = DBG_STATE_GLES_DONE;
+ VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected queueBuffer(pslot=%d) in %s state", pslot,
+ dbgStateStr());
+ mDbgState = DBG_STATE_GPU_DONE;
VDS_LOGV("queueBuffer pslot=%d", pslot);
@@ -488,11 +485,11 @@
mFbFence = mSlots[item.mSlot].mFence;
} else {
- LOG_FATAL_IF(mCompositionType != COMPOSITION_GLES,
- "Unexpected queueBuffer in state %s for compositionType %s",
- dbgStateStr(), dbgCompositionTypeStr(mCompositionType));
+ LOG_FATAL_IF(mCompositionType != COMPOSITION_GPU,
+ "Unexpected queueBuffer in state %s for compositionType %s", dbgStateStr(),
+ dbgCompositionTypeStr(mCompositionType));
- // Extract the GLES release fence for HWC to acquire
+ // Extract the GPU release fence for HWC to acquire
int64_t timestamp;
bool isAutoTimestamp;
android_dataspace dataSpace;
@@ -517,9 +514,8 @@
return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
- "Unexpected cancelBuffer(pslot=%d) in %s state", pslot,
- dbgStateStr());
+ VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected cancelBuffer(pslot=%d) in %s state", pslot,
+ dbgStateStr());
VDS_LOGV("cancelBuffer pslot=%d", pslot);
Source source = fbSourceForCompositionType(mCompositionType);
return mSource[source]->cancelBuffer(
@@ -641,8 +637,8 @@
return result;
mOutputProducerSlot = mapSource2ProducerSlot(SOURCE_SINK, sslot);
- // On GLES-only frames, we don't have the right output buffer acquire fence
- // until after GLES calls queueBuffer(). So here we just set the buffer
+ // On GPU-only frames, we don't have the right output buffer acquire fence
+ // until after GPU calls queueBuffer(). So here we just set the buffer
// (for use in HWC prepare) but not the fence; we'll call this again with
// the proper fence once we have it.
result = mHwc.setOutputBuffer(*mDisplayId, Fence::NO_FENCE,
@@ -672,12 +668,18 @@
const char* VirtualDisplaySurface::dbgStateStr() const {
switch (mDbgState) {
- case DBG_STATE_IDLE: return "IDLE";
- case DBG_STATE_PREPARED: return "PREPARED";
- case DBG_STATE_GLES: return "GLES";
- case DBG_STATE_GLES_DONE: return "GLES_DONE";
- case DBG_STATE_HWC: return "HWC";
- default: return "INVALID";
+ case DBG_STATE_IDLE:
+ return "IDLE";
+ case DBG_STATE_PREPARED:
+ return "PREPARED";
+ case DBG_STATE_GPU:
+ return "GPU";
+ case DBG_STATE_GPU_DONE:
+ return "GPU_DONE";
+ case DBG_STATE_HWC:
+ return "HWC";
+ default:
+ return "INVALID";
}
}
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index d6543d1..3cbad8f 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -34,23 +34,23 @@
class HWComposer;
class IProducerListener;
-/* This DisplaySurface implementation supports virtual displays, where GLES
+/* This DisplaySurface implementation supports virtual displays, where GPU
* and/or HWC compose into a buffer that is then passed to an arbitrary
* consumer (the sink) running in another process.
*
* The simplest case is when the virtual display will never use the h/w
* composer -- either the h/w composer doesn't support writing to buffers, or
* there are more virtual displays than it supports simultaneously. In this
- * case, the GLES driver works directly with the output buffer queue, and
+ * case, the GPU driver works directly with the output buffer queue, and
* calls to the VirtualDisplay from SurfaceFlinger and DisplayHardware do
* nothing.
*
* If h/w composer might be used, then each frame will fall into one of three
- * configurations: GLES-only, HWC-only, and MIXED composition. In all of these,
+ * configurations: GPU-only, HWC-only, and MIXED composition. In all of these,
* we must provide a FB target buffer and output buffer for the HWC set() call.
*
- * In GLES-only composition, the GLES driver is given a buffer from the sink to
- * render into. When the GLES driver queues the buffer to the
+ * In GPU-only composition, the GPU driver is given a buffer from the sink to
+ * render into. When the GPU driver queues the buffer to the
* VirtualDisplaySurface, the VirtualDisplaySurface holds onto it instead of
* immediately queueing it to the sink. The buffer is used as both the FB
* target and output buffer for HWC, though on these frames the HWC doesn't
@@ -65,7 +65,7 @@
*
* On MIXED frames, things become more complicated, since some h/w composer
* implementations can't read from and write to the same buffer. This class has
- * an internal BufferQueue that it uses as a scratch buffer pool. The GLES
+ * an internal BufferQueue that it uses as a scratch buffer pool. The GPU
* driver is given a scratch buffer to render into. When it finishes rendering,
* the buffer is queued and then immediately acquired by the
* VirtualDisplaySurface. The scratch buffer is then used as the FB target
@@ -99,7 +99,7 @@
virtual ~VirtualDisplaySurface();
//
- // IGraphicBufferProducer interface, used by the GLES driver.
+ // IGraphicBufferProducer interface, used by the GPU driver.
//
virtual status_t requestBuffer(int pslot, sp<GraphicBuffer>* outBuf);
virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
@@ -144,7 +144,7 @@
// Both the sink and scratch buffer pools have their own set of slots
// ("source slots", or "sslot"). We have to merge these into the single
- // set of slots used by the GLES producer ("producer slots" or "pslot") and
+ // set of slots used by the graphics producer ("producer slots" or "pslot") and
// internally in the VirtualDisplaySurface. To minimize the number of times
// a producer slot switches which source it comes from, we map source slot
// numbers to producer slot numbers differently for each source.
@@ -166,12 +166,12 @@
// To avoid buffer reallocations, we track the buffer usage and format
// we used on the previous frame and use it again on the new frame. If
- // the composition type changes or the GLES driver starts requesting
+ // the composition type changes or the GPU driver starts requesting
// different usage/format, we'll get a new buffer.
uint32_t mOutputFormat;
uint64_t mOutputUsage;
- // Since we present a single producer interface to the GLES driver, but
+ // Since we present a single producer interface to the GPU driver, but
// are internally muxing between the sink and scratch producers, we have
// to keep track of which source last returned each producer slot from
// dequeueBuffer. Each bit in mProducerSlotSource corresponds to a producer
@@ -181,7 +181,7 @@
sp<GraphicBuffer> mProducerBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
// The QueueBufferOutput with the latest info from the sink, and with the
- // transform hint cleared. Since we defer queueBuffer from the GLES driver
+ // transform hint cleared. Since we defer queueBuffer from the GPU driver
// to the sink, we have to return the previous version.
// Moves instead of copies are performed to avoid duplicate
// FrameEventHistoryDeltas.
@@ -195,7 +195,7 @@
// Intra-frame state
//
- // Composition type and GLES buffer source for the current frame.
+ // Composition type and graphics buffer source for the current frame.
// Valid after prepareFrame(), cleared in onFrameCommitted.
CompositionType mCompositionType;
@@ -221,13 +221,13 @@
// +-----------+-------------------+-------------+
// | IDLE | beginFrame || BEGUN |
// | BEGUN | prepareFrame || PREPARED |
- // | PREPARED | dequeueBuffer [1] || GLES |
+ // | PREPARED | dequeueBuffer [1] || GPU |
// | PREPARED | advanceFrame [2] || HWC |
- // | GLES | queueBuffer || GLES_DONE |
- // | GLES_DONE | advanceFrame || HWC |
+ // | GPU | queueBuffer || GPU_DONE |
+ // | GPU_DONE | advanceFrame || HWC |
// | HWC | onFrameCommitted || IDLE |
// +-----------+-------------------++------------+
- // [1] COMPOSITION_GLES and COMPOSITION_MIXED frames.
+ // [1] COMPOSITION_GPU and COMPOSITION_MIXED frames.
// [2] COMPOSITION_HWC frames.
//
enum DbgState {
@@ -236,12 +236,12 @@
// output buffer dequeued, framebuffer source not yet known
DBG_STATE_BEGUN,
// output buffer dequeued, framebuffer source known but not provided
- // to GLES yet.
+ // to GPU yet.
DBG_STATE_PREPARED,
- // GLES driver has a buffer dequeued
- DBG_STATE_GLES,
- // GLES driver has queued the buffer, we haven't sent it to HWC yet
- DBG_STATE_GLES_DONE,
+ // GPU driver has a buffer dequeued
+ DBG_STATE_GPU,
+ // GPU driver has queued the buffer, we haven't sent it to HWC yet
+ DBG_STATE_GPU_DONE,
// HWC has the buffer for this frame
DBG_STATE_HWC,
};
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 38a80a7..6598bd8 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -18,26 +18,139 @@
#include "Client.h"
#include "Layer.h"
+#include <gui/IProducerListener.h>
+
+#undef LOG_TAG
+#define LOG_TAG "RefreshRateOverlay"
+
namespace android {
+void RefreshRateOverlay::SevenSegmentDrawer::drawRect(const Rect& r, const half4& color,
+ const sp<GraphicBuffer>& buffer,
+ uint8_t* pixels) {
+ for (int32_t j = r.top; j < r.bottom; j++) {
+ if (j >= buffer->getHeight()) {
+ break;
+ }
+
+ for (int32_t i = r.left; i < r.right; i++) {
+ if (i >= buffer->getWidth()) {
+ break;
+ }
+
+ uint8_t* iter = pixels + 4 * (i + (buffer->getStride() * j));
+ iter[0] = uint8_t(color.r * 255);
+ iter[1] = uint8_t(color.g * 255);
+ iter[2] = uint8_t(color.b * 255);
+ iter[3] = uint8_t(color.a * 255);
+ }
+ }
+}
+
+void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left,
+ const half4& color,
+ const sp<GraphicBuffer>& buffer,
+ uint8_t* pixels) {
+ const Rect rect = [&]() {
+ switch (segment) {
+ case Segment::Upper:
+ return Rect(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE);
+ case Segment::UpperLeft:
+ return Rect(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2);
+ case Segment::UpperRight:
+ return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH,
+ DIGIT_HEIGHT / 2);
+ case Segment::Middle:
+ return Rect(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2, left + DIGIT_WIDTH,
+ DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2);
+ case Segment::LowerLeft:
+ return Rect(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT);
+ case Segment::LowerRight:
+ return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, DIGIT_HEIGHT / 2, left + DIGIT_WIDTH,
+ DIGIT_HEIGHT);
+ case Segment::Buttom:
+ return Rect(left, DIGIT_HEIGHT - DIGIT_SPACE, left + DIGIT_WIDTH, DIGIT_HEIGHT);
+ }
+ }();
+
+ drawRect(rect, color, buffer, pixels);
+}
+
+void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, const half4& color,
+ const sp<GraphicBuffer>& buffer,
+ uint8_t* pixels) {
+ if (digit < 0 || digit > 9) return;
+
+ if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 ||
+ digit == 8 || digit == 9)
+ drawSegment(Segment::Upper, left, color, buffer, pixels);
+ if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9)
+ drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
+ if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 ||
+ digit == 8 || digit == 9)
+ drawSegment(Segment::UpperRight, left, color, buffer, pixels);
+ if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 ||
+ digit == 9)
+ drawSegment(Segment::Middle, left, color, buffer, pixels);
+ if (digit == 0 || digit == 2 || digit == 6 || digit == 8)
+ drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
+ if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 ||
+ digit == 7 || digit == 8 || digit == 9)
+ drawSegment(Segment::LowerRight, left, color, buffer, pixels);
+ if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 ||
+ digit == 9)
+ drawSegment(Segment::Buttom, left, color, buffer, pixels);
+}
+
+sp<GraphicBuffer> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number,
+ const half4& color) {
+ if (number < 0 || number > 1000) return nullptr;
+
+ const auto hundreds = number / 100;
+ const auto tens = (number / 10) % 10;
+ const auto ones = number % 10;
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_WRITE_RARELY, "RefreshRateOverlayBuffer");
+ uint8_t* pixels;
+ buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
+ int left = 0;
+ if (hundreds != 0) {
+ drawDigit(hundreds, left, color, buffer, pixels);
+ left += DIGIT_WIDTH + DIGIT_SPACE;
+ }
+
+ if (tens != 0) {
+ drawDigit(tens, left, color, buffer, pixels);
+ left += DIGIT_WIDTH + DIGIT_SPACE;
+ }
+
+ drawDigit(ones, left, color, buffer, pixels);
+ buffer->unlock();
+ return buffer;
+}
+
RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger)
: mFlinger(flinger), mClient(new Client(&mFlinger)) {
createLayer();
+ primeCache();
}
bool RefreshRateOverlay::createLayer() {
const status_t ret =
- mFlinger.createLayer(String8("RefreshRateOverlay"), mClient, 0, 0,
- PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor,
- LayerMetadata(), &mIBinder, &mGbp, nullptr);
+ mFlinger.createLayer(String8("RefreshRateOverlay"), mClient,
+ SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(),
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(),
+ &mIBinder, &mGbp, nullptr);
if (ret) {
- ALOGE("failed to create color layer");
+ ALOGE("failed to create buffer state layer");
return false;
}
Mutex::Autolock _l(mFlinger.mStateLock);
mLayer = mClient->getLayerUser(mIBinder);
- mLayer->setCrop_legacy(Rect(50, 70, 200, 100));
// setting Layer's Z requires resorting layersSortedByZ
ssize_t idx = mFlinger.mCurrentState.layersSortedByZ.indexOf(mLayer);
@@ -49,9 +162,41 @@
return true;
}
+void RefreshRateOverlay::primeCache() {
+ auto allRefreshRates = mFlinger.mRefreshRateConfigs->getAllRefreshRates();
+ if (allRefreshRates.size() == 1) {
+ auto fps = allRefreshRates.begin()->second.fps;
+ half4 color = {LOW_FPS_COLOR, ALPHA};
+ mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
+ return;
+ }
+
+ std::vector<uint32_t> supportedFps;
+ supportedFps.reserve(allRefreshRates.size());
+ for (auto [ignored, refreshRate] : allRefreshRates) {
+ supportedFps.push_back(refreshRate.fps);
+ }
+
+ std::sort(supportedFps.begin(), supportedFps.end());
+ const auto mLowFps = supportedFps[0];
+ const auto mHighFps = supportedFps[supportedFps.size() - 1];
+ for (auto fps : supportedFps) {
+ const auto fpsScale = float(fps - mLowFps) / (mHighFps - mLowFps);
+ half4 color;
+ color.r = HIGH_FPS_COLOR.r * fpsScale + LOW_FPS_COLOR.r * (1 - fpsScale);
+ color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale);
+ color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale);
+ color.a = ALPHA;
+ mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
+ }
+}
+
void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
- const half3& color = (refreshRate.fps > 65.0f) ? GREEN : RED;
- mLayer->setColor(color);
+ auto buffer = mBufferCache[refreshRate.fps];
+ mLayer->setBuffer(buffer, 0, 0, {});
+ mLayer->setFrame(Rect(20, 120, 20 + SevenSegmentDrawer::getWidth(),
+ 120 + SevenSegmentDrawer::getHeight()));
+
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 414bc47..6d34df2 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -28,7 +28,32 @@
void changeRefreshRate(const RefreshRate& refreshRate);
private:
+ class SevenSegmentDrawer {
+ public:
+ static sp<GraphicBuffer> drawNumber(int number, const half4& color);
+ static uint32_t getHeight() { return BUFFER_HEIGHT; }
+ static uint32_t getWidth() { return BUFFER_WIDTH; }
+
+ private:
+ enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Buttom };
+
+ static void drawRect(const Rect& r, const half4& color, const sp<GraphicBuffer>& buffer,
+ uint8_t* pixels);
+ static void drawSegment(Segment segment, int left, const half4& color,
+ const sp<GraphicBuffer>& buffer, uint8_t* pixels);
+ static void drawDigit(int digit, int left, const half4& color,
+ const sp<GraphicBuffer>& buffer, uint8_t* pixels);
+
+ static constexpr uint32_t DIGIT_HEIGHT = 100;
+ static constexpr uint32_t DIGIT_WIDTH = 64;
+ static constexpr uint32_t DIGIT_SPACE = 16;
+ static constexpr uint32_t BUFFER_HEIGHT = DIGIT_HEIGHT;
+ static constexpr uint32_t BUFFER_WIDTH =
+ 3 * DIGIT_WIDTH + 2 * DIGIT_SPACE; // Digit|Space|Digit|Space|Digit
+ };
+
bool createLayer();
+ void primeCache();
SurfaceFlinger& mFlinger;
sp<Client> mClient;
@@ -36,8 +61,11 @@
sp<IBinder> mIBinder;
sp<IGraphicBufferProducer> mGbp;
- const half3 RED = half3(1.0f, 0.0f, 0.0f);
- const half3 GREEN = half3(0.0f, 1.0f, 0.0f);
+ std::unordered_map<int, sp<GraphicBuffer>> mBufferCache;
+
+ static constexpr float ALPHA = 0.8f;
+ const half3 LOW_FPS_COLOR = half3(1.0f, 0.0f, 0.0f);
+ const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f);
};
}; // namespace android
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index e001080..56b3252 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -26,7 +26,7 @@
class TimeKeeper;
class VSyncTracker;
-enum class ScheduleResult { Scheduled, ReScheduled, CannotSchedule, Error };
+enum class ScheduleResult { Scheduled, CannotSchedule, Error };
enum class CancelResult { Cancelled, TooLate, Error };
/*
@@ -83,7 +83,6 @@
* \param [in] earliestVsync The targeted display time. This will be snapped to the closest
* predicted vsync time after earliestVsync.
* \return A ScheduleResult::Scheduled if callback was scheduled.
- * A ScheduleResult::ReScheduled if callback was rescheduled.
* A ScheduleResult::CannotSchedule
* if (workDuration - earliestVsync) is in the past, or
* if a callback was dispatched for the predictedVsync already.
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index a79fe98..48f2abb 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -29,8 +29,13 @@
TimeKeeper::~TimeKeeper() = default;
VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string const& name,
- std::function<void(nsecs_t)> const& cb)
- : mName(name), mCallback(cb), mWorkDuration(0), mEarliestVsync(0) {}
+ std::function<void(nsecs_t)> const& cb,
+ nsecs_t minVsyncDistance)
+ : mName(name),
+ mCallback(cb),
+ mWorkDuration(0),
+ mEarliestVsync(0),
+ mMinVsyncDistance(minVsyncDistance) {}
std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
return mLastDispatchTime;
@@ -49,18 +54,28 @@
ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
VSyncTracker& tracker, nsecs_t now) {
- auto const nextVsyncTime =
+ auto nextVsyncTime =
tracker.nextAnticipatedVSyncTimeFrom(std::max(earliestVsync, now + workDuration));
- if (mLastDispatchTime >= nextVsyncTime) { // already dispatched a callback for this vsync
- return ScheduleResult::CannotSchedule;
+
+ bool const wouldSkipAVsyncTarget =
+ mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
+ if (wouldSkipAVsyncTarget) {
+ return ScheduleResult::Scheduled;
+ }
+
+ bool const alreadyDispatchedForVsync = mLastDispatchTime &&
+ ((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime &&
+ (*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime);
+ if (alreadyDispatchedForVsync) {
+ nextVsyncTime =
+ tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
}
auto const nextWakeupTime = nextVsyncTime - workDuration;
- auto result = mArmedInfo ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled;
mWorkDuration = workDuration;
mEarliestVsync = earliestVsync;
mArmedInfo = {nextWakeupTime, nextVsyncTime};
- return result;
+ return ScheduleResult::Scheduled;
}
void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
@@ -101,8 +116,12 @@
}
VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
- VSyncTracker& tracker, nsecs_t timerSlack)
- : mTimeKeeper(std::move(tk)), mTracker(tracker), mTimerSlack(timerSlack) {}
+ VSyncTracker& tracker, nsecs_t timerSlack,
+ nsecs_t minVsyncDistance)
+ : mTimeKeeper(std::move(tk)),
+ mTracker(tracker),
+ mTimerSlack(timerSlack),
+ mMinVsyncDistance(minVsyncDistance) {}
VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
std::lock_guard<decltype(mMutex)> lk(mMutex);
@@ -187,7 +206,8 @@
mCallbacks
.emplace(++mCallbackToken,
std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName,
- callbackFn))
+ callbackFn,
+ mMinVsyncDistance))
.first->first};
}
@@ -277,12 +297,16 @@
}
ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) {
- if (!mValidToken) return ScheduleResult::Error;
+ if (!mValidToken) {
+ return ScheduleResult::Error;
+ }
return mDispatch.get().schedule(mToken, workDuration, earliestVsync);
}
CancelResult VSyncCallbackRegistration::cancel() {
- if (!mValidToken) return CancelResult::Error;
+ if (!mValidToken) {
+ return CancelResult::Error;
+ }
return mDispatch.get().cancel(mToken);
}
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index fc78da3..0c9b4fe 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -36,7 +36,8 @@
// Valid transition: disarmed -> armed ( when scheduled )
// Valid transition: armed -> running -> disarmed ( when timer is called)
// Valid transition: armed -> disarmed ( when cancelled )
- VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& fn);
+ VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& fn,
+ nsecs_t minVsyncDistance);
std::string_view name() const;
// Start: functions that are not threadsafe.
@@ -72,6 +73,7 @@
nsecs_t mWorkDuration;
nsecs_t mEarliestVsync;
+ nsecs_t const mMinVsyncDistance;
struct ArmingInfo {
nsecs_t mActualWakeupTime;
@@ -91,8 +93,15 @@
*/
class VSyncDispatchTimerQueue : public VSyncDispatch {
public:
+ // Constructs a VSyncDispatchTimerQueue.
+ // \param[in] tk A timekeeper.
+ // \param[in] tracker A tracker.
+ // \param[in] timerSlack The threshold at which different similarly timed callbacks
+ // should be grouped into one wakeup.
+ // \param[in] minVsyncDistance The minimum distance between two vsync estimates before the
+ // vsyncs are considered the same vsync event.
explicit VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
- nsecs_t timerSlack);
+ nsecs_t timerSlack, nsecs_t minVsyncDistance);
~VSyncDispatchTimerQueue();
CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
@@ -119,6 +128,7 @@
std::unique_ptr<TimeKeeper> const mTimeKeeper;
VSyncTracker& mTracker;
nsecs_t const mTimerSlack;
+ nsecs_t const mMinVsyncDistance;
std::mutex mutable mMutex;
size_t mCallbackToken GUARDED_BY(mMutex) = 0;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index f2a7791..47e3f4f 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -14,7 +14,11 @@
* limitations under the License.
*/
+#undef LOG_TAG
+#define LOG_TAG "VSyncReactor"
+//#define LOG_NDEBUG 0
#include "VSyncReactor.h"
+#include <log/log.h>
#include "TimeKeeper.h"
#include "VSyncDispatch.h"
#include "VSyncTracker.h"
@@ -30,6 +34,87 @@
mTracker(std::move(tracker)),
mPendingLimit(pendingFenceLimit) {}
+VSyncReactor::~VSyncReactor() = default;
+
+// The DispSync interface has a 'repeat this callback at rate' semantic. This object adapts
+// VSyncDispatch's individually-scheduled callbacks so as to meet DispSync's existing semantic
+// for now.
+class CallbackRepeater {
+public:
+ CallbackRepeater(VSyncDispatch& dispatch, DispSync::Callback* cb, const char* name,
+ nsecs_t period, nsecs_t offset, nsecs_t notBefore)
+ : mCallback(cb),
+ mRegistration(dispatch,
+ std::bind(&CallbackRepeater::callback, this, std::placeholders::_1),
+ std::string(name)),
+ mPeriod(period),
+ mOffset(offset),
+ mLastCallTime(notBefore) {}
+
+ ~CallbackRepeater() {
+ std::lock_guard<std::mutex> lk(mMutex);
+ mRegistration.cancel();
+ }
+
+ void start(nsecs_t offset) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ mStopped = false;
+ mOffset = offset;
+
+ auto const schedule_result = mRegistration.schedule(calculateWorkload(), mLastCallTime);
+ LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
+ "Error scheduling callback: rc %X", schedule_result);
+ }
+
+ void setPeriod(nsecs_t period) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ if (period == mPeriod) {
+ return;
+ }
+ mPeriod = period;
+ }
+
+ void stop() {
+ std::lock_guard<std::mutex> lk(mMutex);
+ LOG_ALWAYS_FATAL_IF(mStopped, "DispSyncInterface misuse: callback already stopped");
+ mStopped = true;
+ mRegistration.cancel();
+ }
+
+private:
+ void callback(nsecs_t vsynctime) {
+ nsecs_t period = 0;
+ {
+ std::lock_guard<std::mutex> lk(mMutex);
+ period = mPeriod;
+ mLastCallTime = vsynctime;
+ }
+
+ mCallback->onDispSyncEvent(vsynctime - period);
+
+ {
+ std::lock_guard<std::mutex> lk(mMutex);
+ auto const schedule_result = mRegistration.schedule(calculateWorkload(), vsynctime);
+ LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
+ "Error rescheduling callback: rc %X", schedule_result);
+ }
+ }
+
+ // DispSync offsets are defined as time after the vsync before presentation.
+ // VSyncReactor workloads are defined as time before the intended presentation vsync.
+ // Note change in sign between the two defnitions.
+ nsecs_t calculateWorkload() REQUIRES(mMutex) { return mPeriod - mOffset; }
+
+ DispSync::Callback* const mCallback;
+
+ std::mutex mutable mMutex;
+ VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
+ bool mStopped GUARDED_BY(mMutex) = false;
+ nsecs_t mPeriod GUARDED_BY(mMutex);
+ nsecs_t mOffset GUARDED_BY(mMutex);
+ nsecs_t mLastCallTime GUARDED_BY(mMutex);
+};
+
bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) {
if (!fence) {
return false;
@@ -92,6 +177,9 @@
{
std::lock_guard<std::mutex> lk(mMutex);
mPeriodChangeInProgress = true;
+ for (auto& entry : mCallbacks) {
+ entry.second->setPeriod(period);
+ }
}
}
@@ -114,4 +202,47 @@
return false;
}
+status_t VSyncReactor::addEventListener(const char* name, nsecs_t phase,
+ DispSync::Callback* callback,
+ nsecs_t /* lastCallbackTime */) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ auto it = mCallbacks.find(callback);
+ if (it == mCallbacks.end()) {
+ // TODO (b/146557561): resolve lastCallbackTime semantics in DispSync i/f.
+ static auto constexpr maxListeners = 3;
+ if (mCallbacks.size() >= maxListeners) {
+ ALOGE("callback %s not added, exceeded callback limit of %i (currently %zu)", name,
+ maxListeners, mCallbacks.size());
+ return NO_MEMORY;
+ }
+
+ auto const period = mTracker->currentPeriod();
+ auto repeater = std::make_unique<CallbackRepeater>(*mDispatch, callback, name, period,
+ phase, mClock->now());
+ it = mCallbacks.emplace(std::pair(callback, std::move(repeater))).first;
+ }
+
+ it->second->start(phase);
+ return NO_ERROR;
+}
+
+status_t VSyncReactor::removeEventListener(DispSync::Callback* callback,
+ nsecs_t* /* outLastCallback */) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ auto const it = mCallbacks.find(callback);
+ LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback %p not registered", callback);
+
+ it->second->stop();
+ return NO_ERROR;
+}
+
+status_t VSyncReactor::changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ auto const it = mCallbacks.find(callback);
+ LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback was %p not registered", callback);
+
+ it->second->start(phase);
+ return NO_ERROR;
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 786ee98..837eb75 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -20,19 +20,23 @@
#include <ui/FenceTime.h>
#include <memory>
#include <mutex>
+#include <unordered_map>
#include <vector>
+#include "DispSync.h"
namespace android::scheduler {
class Clock;
class VSyncDispatch;
class VSyncTracker;
+class CallbackRepeater;
// TODO (b/145217110): consider renaming.
class VSyncReactor /* TODO (b/140201379): : 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);
@@ -48,6 +52,11 @@
bool addResyncSample(nsecs_t timestamp, bool* periodFlushed);
void endResync();
+ 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);
+
private:
std::unique_ptr<Clock> const mClock;
std::unique_ptr<VSyncDispatch> const mDispatch;
@@ -58,6 +67,8 @@
bool mIgnorePresentFences GUARDED_BY(mMutex) = false;
std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
bool mPeriodChangeInProgress GUARDED_BY(mMutex) = false;
+ std::unordered_map<DispSync::Callback*, std::unique_ptr<CallbackRepeater>> mCallbacks
+ GUARDED_BY(mMutex);
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 857cb70..fed2395 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4857,15 +4857,15 @@
return NO_ERROR;
}
case 1034: {
- // TODO(b/129297325): expose this via developer menu option
n = data.readInt32();
- if (n && !mRefreshRateOverlay &&
- mRefreshRateConfigs->refreshRateSwitchingSupported()) {
+ if (n == 1 && !mRefreshRateOverlay) {
mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
auto current = mRefreshRateConfigs->getCurrentRefreshRate();
mRefreshRateOverlay->changeRefreshRate(current);
- } else if (!n) {
+ } else if (n == 0) {
mRefreshRateOverlay.reset();
+ } else {
+ reply->writeBool(mRefreshRateOverlay != nullptr);
}
return NO_ERROR;
}
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 8db03db..4c1baaf 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -311,8 +311,16 @@
interface_cast<ITransactionCompletedListener>(listenerStats.listener)
->onTransactionCompleted(listenerStats);
listener->unlinkToDeath(mDeathRecipient);
+ if (transactionStatsDeque.empty()) {
+ completedTransactionsItr =
+ mCompletedTransactions.erase(completedTransactionsItr);
+ } else {
+ completedTransactionsItr++;
+ }
+ } else {
+ completedTransactionsItr =
+ mCompletedTransactions.erase(completedTransactionsItr);
}
- completedTransactionsItr = mCompletedTransactions.erase(completedTransactionsItr);
} else {
completedTransactionsItr++;
}
diff --git a/services/surfaceflinger/tests/CommonTypes_test.cpp b/services/surfaceflinger/tests/CommonTypes_test.cpp
index a3e16f9..ab4af09 100644
--- a/services/surfaceflinger/tests/CommonTypes_test.cpp
+++ b/services/surfaceflinger/tests/CommonTypes_test.cpp
@@ -100,21 +100,21 @@
static_cast<uint32_t>(HidlDataspace::RANGE_LIMITED));
static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_EXTENDED) ==
static_cast<uint32_t>(HidlDataspace::RANGE_EXTENDED));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_SRGB_LINEAR) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::SRGB_LINEAR) ==
static_cast<uint32_t>(HidlDataspace::V0_SRGB_LINEAR));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_SCRGB_LINEAR) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::SCRGB_LINEAR) ==
static_cast<uint32_t>(HidlDataspace::V0_SCRGB_LINEAR));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_SRGB) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::SRGB) ==
static_cast<uint32_t>(HidlDataspace::V0_SRGB));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_SCRGB) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::SCRGB) ==
static_cast<uint32_t>(HidlDataspace::V0_SCRGB));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_JFIF) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::JFIF) ==
static_cast<uint32_t>(HidlDataspace::V0_JFIF));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_BT601_625) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::BT601_625) ==
static_cast<uint32_t>(HidlDataspace::V0_BT601_625));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_BT601_525) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::BT601_525) ==
static_cast<uint32_t>(HidlDataspace::V0_BT601_525));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_BT709) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::BT709) ==
static_cast<uint32_t>(HidlDataspace::V0_BT709));
static_assert(static_cast<uint32_t>(AidlDataspace::DCI_P3_LINEAR) ==
static_cast<uint32_t>(HidlDataspace::DCI_P3_LINEAR));
@@ -152,19 +152,3 @@
static_cast<uint32_t>(HidlDataspace::JPEG_APP_SEGMENTS));
static_assert(static_cast<uint32_t>(AidlDataspace::HEIF) ==
static_cast<uint32_t>(HidlDataspace::HEIF));
-
-// Below are the dataspaces that have been deprecated for sometime. They are required to behave
-// the same as their V0_* counterparts. We redefined them in AIDL to be the same as the
-// their V0_* counterparts.
-static_assert(static_cast<uint32_t>(AidlDataspace::SRGB_LINEAR) ==
- static_cast<uint32_t>(AidlDataspace::V0_SRGB_LINEAR));
-static_assert(static_cast<uint32_t>(AidlDataspace::SRGB) ==
- static_cast<uint32_t>(AidlDataspace::V0_SRGB));
-static_assert(static_cast<uint32_t>(AidlDataspace::JFIF) ==
- static_cast<uint32_t>(AidlDataspace::V0_JFIF));
-static_assert(static_cast<uint32_t>(AidlDataspace::BT601_625) ==
- static_cast<uint32_t>(AidlDataspace::V0_BT601_625));
-static_assert(static_cast<uint32_t>(AidlDataspace::BT601_525) ==
- static_cast<uint32_t>(AidlDataspace::V0_BT601_525));
-static_assert(static_cast<uint32_t>(AidlDataspace::BT709) ==
- static_cast<uint32_t>(AidlDataspace::V0_BT709));
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 999e82d..627de7a 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -72,8 +72,8 @@
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
- // GLES requires only 4 bits of subpixel precision during rasterization
- // XXX GLES composition does not match HWC composition due to precision
+ // GPU composition requires only 4 bits of subpixel precision during rasterization
+ // XXX GPU composition does not match HWC composition due to precision
// loss (b/69315223)
const float epsilon = 1.0f / 16.0f;
Transaction().setPosition(layer, 0.5f - epsilon, 0.5f - epsilon).apply();
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 04991f9..76dea62 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -361,7 +361,7 @@
static void setupRECompositionCallExpectations(CompositionTest* test) {
EXPECT_CALL(*test->mDisplaySurface,
- prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GLES))
+ prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GPU))
.Times(1);
EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
.WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 484947d..5846c77 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -92,6 +92,7 @@
struct VSyncDispatchRealtimeTest : testing::Test {
static nsecs_t constexpr mDispatchGroupThreshold = toNs(100us);
+ static nsecs_t constexpr mVsyncMoveThreshold = toNs(500us);
static size_t constexpr mIterations = 20;
};
@@ -148,7 +149,8 @@
TEST_F(VSyncDispatchRealtimeTest, triple_alarm) {
FixedRateIdealStubTracker tracker;
- VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold);
+ VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+ mVsyncMoveThreshold);
static size_t constexpr num_clients = 3;
std::array<RepeatingCallbackReceiver, num_clients>
@@ -176,7 +178,8 @@
TEST_F(VSyncDispatchRealtimeTest, vascillating_vrr) {
auto next_vsync_interval = toNs(3ms);
VRRStubTracker tracker(next_vsync_interval);
- VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold);
+ VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+ mVsyncMoveThreshold);
RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
@@ -193,7 +196,8 @@
// starts at 333hz, jumps to 200hz at frame 10
TEST_F(VSyncDispatchRealtimeTest, fixed_jump) {
VRRStubTracker tracker(toNs(3ms));
- VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold);
+ VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+ mVsyncMoveThreshold);
RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index d668a41..5aff429 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -196,16 +196,18 @@
NiceMock<ControllableClock> mMockClock;
static nsecs_t constexpr mDispatchGroupThreshold = 5;
nsecs_t const mPeriod = 1000;
+ nsecs_t const mVsyncMoveThreshold = 300;
NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
- VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold};
+ VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
+ mVsyncMoveThreshold};
};
TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) {
EXPECT_CALL(mMockClock, alarmIn(_, 900));
EXPECT_CALL(mMockClock, alarmCancel());
{
- VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker,
- mDispatchGroupThreshold};
+ VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
+ mVsyncMoveThreshold};
CountingCallback cb(mDispatch);
EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::Scheduled);
}
@@ -468,14 +470,24 @@
TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) {
VSyncDispatch::CallbackToken tmp;
+ std::optional<nsecs_t> lastTarget;
tmp = mDispatch.registerCallback(
- [&](auto) {
- EXPECT_EQ(mDispatch.schedule(tmp, 400, 1000), ScheduleResult::CannotSchedule);
+ [&](auto timestamp) {
+ EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp - mVsyncMoveThreshold),
+ ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp + mVsyncMoveThreshold),
+ ScheduleResult::Scheduled);
+ lastTarget = timestamp;
},
"oo");
mDispatch.schedule(tmp, 999, 1000);
advanceToNextCallback();
+ EXPECT_THAT(lastTarget, Eq(1000));
+
+ advanceToNextCallback();
+ EXPECT_THAT(lastTarget, Eq(2000));
}
TEST_F(VSyncDispatchTimerQueueTest, modificationsAroundVsyncTime) {
@@ -547,10 +559,33 @@
EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
}
-TEST_F(VSyncDispatchTimerQueueTest, distinguishesScheduleAndReschedule) {
+TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
CountingCallback cb0(mDispatch);
EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::ReScheduled);
+ EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::Scheduled);
+}
+
+// b/1450138150
+TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
+ EXPECT_CALL(mMockClock, alarmIn(_, 500));
+ CountingCallback cb(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
+ mMockClock.advanceBy(400);
+
+ EXPECT_EQ(mDispatch.schedule(cb, 800, 1000), ScheduleResult::Scheduled);
+ advanceToNextCallback();
+ ASSERT_THAT(cb.mCalls.size(), Eq(1));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedule) {
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+ .Times(2)
+ .WillOnce(Return(1000))
+ .WillOnce(Return(1002));
+ CountingCallback cb(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
+ mMockClock.advanceBy(400);
+ EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
}
TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
@@ -570,13 +605,15 @@
EXPECT_EQ(mDispatch.schedule(cb0, 1900, 2000), ScheduleResult::Scheduled);
}
-TEST_F(VSyncDispatchTimerQueueTest, cannotScheduleDoesNotAffectSchedulingState) {
+TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
EXPECT_CALL(mMockClock, alarmIn(_, 600));
CountingCallback cb(mDispatch);
EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+
+ EXPECT_EQ(mDispatch.schedule(cb, 1400, 1000), ScheduleResult::Scheduled);
+
advanceToNextCallback();
- EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::CannotSchedule);
}
TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
@@ -612,19 +649,22 @@
class VSyncDispatchTimerQueueEntryTest : public testing::Test {
protected:
nsecs_t const mPeriod = 1000;
+ nsecs_t const mVsyncMoveThreshold = 200;
NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
};
TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) {
std::string name("basicname");
- VSyncDispatchTimerQueueEntry entry(name, [](auto) {});
+ VSyncDispatchTimerQueueEntry entry(
+ name, [](auto) {}, mVsyncMoveThreshold);
EXPECT_THAT(entry.name(), Eq(name));
EXPECT_FALSE(entry.lastExecutedVsyncTarget());
EXPECT_FALSE(entry.wakeupTime());
}
TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) {
- VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+ VSyncDispatchTimerQueueEntry entry(
+ "test", [](auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
@@ -643,7 +683,8 @@
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + duration))
.Times(1)
.WillOnce(Return(10000));
- VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+ VSyncDispatchTimerQueueEntry entry(
+ "test", [](auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
EXPECT_THAT(entry.schedule(500, 994, mStubTracker, now), Eq(ScheduleResult::Scheduled));
@@ -655,10 +696,13 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) {
auto callCount = 0;
auto calledTime = 0;
- VSyncDispatchTimerQueueEntry entry("test", [&](auto time) {
- callCount++;
- calledTime = time;
- });
+ VSyncDispatchTimerQueueEntry entry(
+ "test",
+ [&](auto time) {
+ callCount++;
+ calledTime = time;
+ },
+ mVsyncMoveThreshold);
EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
auto const wakeup = entry.wakeupTime();
@@ -681,7 +725,8 @@
.WillOnce(Return(1000))
.WillOnce(Return(1020));
- VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+ VSyncDispatchTimerQueueEntry entry(
+ "test", [](auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
entry.update(mStubTracker, 0);
@@ -699,7 +744,8 @@
}
TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
- VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+ VSyncDispatchTimerQueueEntry entry(
+ "test", [](auto) {}, mVsyncMoveThreshold);
EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
entry.update(mStubTracker, 0);
@@ -708,19 +754,52 @@
EXPECT_THAT(*wakeup, Eq(wakeup));
}
-TEST_F(VSyncDispatchTimerQueueEntryTest, reportsCannotScheduleIfMissedOpportunity) {
- VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
+ VSyncDispatchTimerQueueEntry entry(
+ "test", [](auto) {}, mVsyncMoveThreshold);
EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
- entry.executing();
- EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::CannotSchedule));
- EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::CannotSchedule));
+ entry.executing(); // 1000 is executing
+ // had 1000 not been executing, this could have been scheduled for time 800.
+ EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
+
+ EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
+
EXPECT_THAT(entry.schedule(200, 1001, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
}
-TEST_F(VSyncDispatchTimerQueueEntryTest, reportsReScheduleIfStillTime) {
- VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+TEST_F(VSyncDispatchTimerQueueEntryTest,
+ willRequestNextEstimateWhenSnappingToNextTargettableVSync) {
+ VSyncDispatchTimerQueueEntry entry(
+ "test", [](auto) {}, mVsyncMoveThreshold);
+
+ Sequence seq;
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
+ .InSequence(seq)
+ .WillOnce(Return(1000));
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
+ .InSequence(seq)
+ .WillOnce(Return(1000));
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold))
+ .InSequence(seq)
+ .WillOnce(Return(2000));
+
EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
- EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::ReScheduled));
+
+ entry.executing(); // 1000 is executing
+
+ EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
+ VSyncDispatchTimerQueueEntry entry(
+ "test", [](auto) {}, mVsyncMoveThreshold);
+ EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule(1200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 84df019..537cc80 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -122,21 +122,53 @@
return ft;
}
+class StubCallback : public DispSync::Callback {
+public:
+ void onDispSyncEvent(nsecs_t when) final {
+ std::lock_guard<std::mutex> lk(mMutex);
+ mLastCallTime = when;
+ }
+ std::optional<nsecs_t> lastCallTime() const {
+ std::lock_guard<std::mutex> lk(mMutex);
+ return mLastCallTime;
+ }
+
+private:
+ std::mutex mutable mMutex;
+ std::optional<nsecs_t> mLastCallTime GUARDED_BY(mMutex);
+};
+
class VSyncReactorTest : public testing::Test {
protected:
VSyncReactorTest()
- : mMockDispatch(std::make_shared<MockVSyncDispatch>()),
+ : mMockDispatch(std::make_shared<NiceMock<MockVSyncDispatch>>()),
mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
mMockClock(std::make_shared<NiceMock<MockClock>>()),
mReactor(std::make_unique<ClockWrapper>(mMockClock),
std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
- std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit) {}
+ std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit) {
+ ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow));
+ ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period));
+ }
std::shared_ptr<MockVSyncDispatch> mMockDispatch;
std::shared_ptr<MockVSyncTracker> mMockTracker;
std::shared_ptr<MockClock> mMockClock;
static constexpr size_t kPendingLimit = 3;
- static constexpr nsecs_t dummyTime = 47;
+ static constexpr nsecs_t mDummyTime = 47;
+ 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";
+ VSyncDispatch::CallbackToken const mFakeToken{2398};
+
+ nsecs_t lastCallbackTime = 0;
+ StubCallback outerCb;
+ std::function<void(nsecs_t)> innerCb;
+
VSyncReactor mReactor;
};
@@ -149,8 +181,8 @@
}
TEST_F(VSyncReactorTest, addingSignalledFenceAddsToTracker) {
- EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
- EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime));
+ EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
}
TEST_F(VSyncReactorTest, addingPendingFenceAddsSignalled) {
@@ -161,9 +193,9 @@
EXPECT_FALSE(mReactor.addPresentFence(pendingFence));
Mock::VerifyAndClearExpectations(mMockTracker.get());
- signalFenceWithTime(pendingFence, dummyTime);
+ signalFenceWithTime(pendingFence, mDummyTime);
- EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime));
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(anotherDummyTime));
EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(anotherDummyTime)));
}
@@ -193,15 +225,15 @@
TEST_F(VSyncReactorTest, ignoresPresentFencesWhenToldTo) {
static constexpr size_t aFewTimes = 8;
- EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime)).Times(1);
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime)).Times(1);
mReactor.setIgnorePresentFences(true);
for (auto i = 0; i < aFewTimes; i++) {
- mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime));
+ mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime));
}
mReactor.setIgnorePresentFences(false);
- EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
+ EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
}
TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
@@ -227,11 +259,11 @@
TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshFuture) {
nsecs_t const fakeTimestamp = 4839;
nsecs_t const fakePeriod = 1010;
- nsecs_t const fakeNow = 2214;
+ nsecs_t const mFakeNow = 2214;
int const numPeriodsOut = 3;
- EXPECT_CALL(*mMockClock, now()).WillOnce(Return(fakeNow));
+ EXPECT_CALL(*mMockClock, now()).WillOnce(Return(mFakeNow));
EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
- EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(fakeNow + numPeriodsOut * fakePeriod))
+ EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(mFakeNow + numPeriodsOut * fakePeriod))
.WillOnce(Return(fakeTimestamp));
EXPECT_THAT(mReactor.computeNextRefresh(numPeriodsOut), Eq(fakeTimestamp));
}
@@ -267,4 +299,194 @@
EXPECT_TRUE(periodFlushed);
}
+static nsecs_t computeWorkload(nsecs_t period, nsecs_t phase) {
+ return period - phase;
+}
+
+TEST_F(VSyncReactorTest, addEventListener) {
+ 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(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ mReactor.removeEventListener(&outerCb, &lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, addEventListenerTwiceChangesPhase) {
+ 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(*mMockDispatch,
+ schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _)) // mFakeNow))
+ .InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, eventListenerGetsACallbackAndReschedules) {
+ Sequence seq;
+ EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+ .InSequence(seq)
+ .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+ .InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeCbTime))
+ .Times(2)
+ .InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ ASSERT_TRUE(innerCb);
+ innerCb(mFakeCbTime);
+ innerCb(mFakeCbTime);
+}
+
+TEST_F(VSyncReactorTest, callbackTimestampReadapted) {
+ Sequence seq;
+ EXPECT_CALL(*mMockDispatch, registerCallback(_, _))
+ .InSequence(seq)
+ .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+ .InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeCbTime))
+ .InSequence(seq);
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ ASSERT_TRUE(innerCb);
+ innerCb(mFakeCbTime);
+ EXPECT_THAT(outerCb.lastCallTime(), Optional(mFakeCbTime - period));
+}
+
+TEST_F(VSyncReactorTest, eventListenersRemovedOnDestruction) {
+ 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(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, addEventListenerChangePeriod) {
+ 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(*mMockDispatch,
+ schedule(mFakeToken, computeWorkload(period, mAnotherPhase), mFakeNow))
+ .InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, changingPeriodChangesOfsetsOnNextCb) {
+ 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(*mMockDispatch,
+ schedule(mFakeToken, computeWorkload(mAnotherPeriod, mPhase), mFakeNow))
+ .InSequence(seq);
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ mReactor.setPeriod(mAnotherPeriod);
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, offsetsAppliedOnNextOpportunity) {
+ Sequence seq;
+ EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+ .InSequence(seq)
+ .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), _))
+ .InSequence(seq)
+ .WillOnce(Return(ScheduleResult::Scheduled));
+
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
+ .InSequence(seq)
+ .WillOnce(Return(ScheduleResult::Scheduled));
+
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
+ .InSequence(seq)
+ .WillOnce(Return(ScheduleResult::Scheduled));
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ mReactor.changePhaseOffset(&outerCb, mAnotherPhase);
+ ASSERT_TRUE(innerCb);
+ innerCb(mFakeCbTime);
+}
+
+TEST_F(VSyncReactorTest, negativeOffsetsApplied) {
+ nsecs_t const negativePhase = -4000;
+ Sequence seq;
+ EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+ .InSequence(seq)
+ .WillOnce(Return(mFakeToken));
+ EXPECT_CALL(*mMockDispatch,
+ schedule(mFakeToken, computeWorkload(period, negativePhase), mFakeNow))
+ .InSequence(seq);
+ mReactor.addEventListener(mName, negativePhase, &outerCb, lastCallbackTime);
+}
+
+using VSyncReactorDeathTest = VSyncReactorTest;
+TEST_F(VSyncReactorDeathTest, invalidRemoval) {
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ mReactor.removeEventListener(&outerCb, &lastCallbackTime);
+ EXPECT_DEATH(mReactor.removeEventListener(&outerCb, &lastCallbackTime), ".*");
+}
+
+TEST_F(VSyncReactorDeathTest, invalidChange) {
+ EXPECT_DEATH(mReactor.changePhaseOffset(&outerCb, mPhase), ".*");
+
+ // the current DispSync-interface usage pattern has evolved around an implementation quirk,
+ // which is a callback is assumed to always exist, and it is valid api usage to change the
+ // offset of an object that is in the removed state.
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ mReactor.removeEventListener(&outerCb, &lastCallbackTime);
+ mReactor.changePhaseOffset(&outerCb, mPhase);
+}
+
+TEST_F(VSyncReactorDeathTest, cannotScheduleOnRegistration) {
+ ON_CALL(*mMockDispatch, schedule(_, _, _))
+ .WillByDefault(Return(ScheduleResult::CannotSchedule));
+ EXPECT_DEATH(mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime), ".*");
+}
+
+TEST_F(VSyncReactorDeathTest, cannotScheduleOnCallback) {
+ EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+ .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+ EXPECT_CALL(*mMockDispatch, schedule(_, _, _)).WillOnce(Return(ScheduleResult::Scheduled));
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ ASSERT_TRUE(innerCb);
+ Mock::VerifyAndClearExpectations(mMockDispatch.get());
+
+ ON_CALL(*mMockDispatch, schedule(_, _, _))
+ .WillByDefault(Return(ScheduleResult::CannotSchedule));
+ EXPECT_DEATH(innerCb(mFakeCbTime), ".*");
+}
+
} // namespace android::scheduler
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index a020e74..a44b9e7 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -24,6 +24,7 @@
#include <system/window.h>
#include <ui/BufferQueueDefs.h>
#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
#include <utils/Trace.h>
#include <algorithm>
@@ -228,8 +229,10 @@
mailbox_mode(present_mode == VK_PRESENT_MODE_MAILBOX_KHR),
pre_transform(pre_transform_),
frame_timestamps_enabled(false),
+ acquire_next_image_timeout(-1),
shared(present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
- present_mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
+ present_mode ==
+ VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
ANativeWindow* window = surface.window.get();
native_window_get_refresh_cycle_duration(
window,
@@ -251,6 +254,7 @@
int pre_transform;
bool frame_timestamps_enabled;
int64_t refresh_duration;
+ nsecs_t acquire_next_image_timeout;
bool shared;
struct Image {
@@ -363,7 +367,7 @@
int64_t composition_latch_time = 0;
int64_t actual_present_time = 0;
// Obtain timestamps:
- int ret = native_window_get_frame_timestamps(
+ int err = native_window_get_frame_timestamps(
swapchain.surface.window.get(), ti.native_frame_id_,
&desired_present_time, &render_complete_time,
&composition_latch_time,
@@ -374,7 +378,7 @@
nullptr, //&dequeue_ready_time,
nullptr /*&reads_done_time*/);
- if (ret != android::NO_ERROR) {
+ if (err != android::OK) {
continue;
}
@@ -530,7 +534,7 @@
surface->swapchain_handle = VK_NULL_HANDLE;
int err = native_window_get_consumer_usage(surface->window.get(),
&surface->consumer_usage);
- if (err != android::NO_ERROR) {
+ if (err != android::OK) {
ALOGE("native_window_get_consumer_usage() failed: %s (%d)",
strerror(-err), err);
surface->~Surface();
@@ -540,7 +544,7 @@
err =
native_window_api_connect(surface->window.get(), NATIVE_WINDOW_API_EGL);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("native_window_api_connect() failed: %s (%d)", strerror(-err),
err);
surface->~Surface();
@@ -588,7 +592,7 @@
int query_value;
int err = window->query(window, NATIVE_WINDOW_FORMAT, &query_value);
- if (err != 0 || query_value < 0) {
+ if (err != android::OK || query_value < 0) {
ALOGE("NATIVE_WINDOW_FORMAT query failed: %s (%d) value=%d",
strerror(-err), err, query_value);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -629,13 +633,13 @@
int width, height;
err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -643,7 +647,7 @@
int transform_hint;
err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -651,7 +655,7 @@
int max_buffer_count;
err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &max_buffer_count);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -694,20 +698,6 @@
const InstanceData& instance_data = GetData(pdev);
- // TODO(b/143296550): Fill out the set of supported formats. Longer term,
- // add a new gralloc method to query whether a (format, usage) pair is
- // supported, and check that for each gralloc format that corresponds to a
- // Vulkan format. Shorter term, just add a few more formats to the ones
- // hardcoded below.
-
- const VkSurfaceFormatKHR kFormats[] = {
- {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
- {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
- {VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
- };
- const uint32_t kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
- uint32_t total_num_formats = kNumFormats;
-
bool wide_color_support = false;
Surface& surface = *SurfaceFromHandle(surface_handle);
int err = native_window_get_wide_color_support(surface.window.get(),
@@ -720,43 +710,72 @@
wide_color_support &&
instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
- const VkSurfaceFormatKHR kWideColorFormats[] = {
- {VK_FORMAT_R8G8B8A8_UNORM,
- VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
- {VK_FORMAT_R8G8B8A8_SRGB,
- VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
- {VK_FORMAT_R16G16B16A16_SFLOAT,
- VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT},
- {VK_FORMAT_R16G16B16A16_SFLOAT,
- VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT},
- {VK_FORMAT_A2B10G10R10_UNORM_PACK32,
- VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
- };
- const uint32_t kNumWideColorFormats =
- sizeof(kWideColorFormats) / sizeof(kWideColorFormats[0]);
+ AHardwareBuffer_Desc desc = {};
+ desc.width = 1;
+ desc.height = 1;
+ desc.layers = 1;
+ desc.usage = surface.consumer_usage |
+ AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
+ AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
+
+ // We must support R8G8B8A8
+ std::vector<VkSurfaceFormatKHR> all_formats = {
+ {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
+ {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}};
+
if (wide_color_support) {
- total_num_formats += kNumWideColorFormats;
+ all_formats.emplace_back(VkSurfaceFormatKHR{
+ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
+ all_formats.emplace_back(VkSurfaceFormatKHR{
+ VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
+ }
+
+ desc.format = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
+ if (AHardwareBuffer_isSupported(&desc)) {
+ all_formats.emplace_back(VkSurfaceFormatKHR{
+ VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+ }
+
+ desc.format = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT;
+ if (AHardwareBuffer_isSupported(&desc)) {
+ all_formats.emplace_back(VkSurfaceFormatKHR{
+ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+ if (wide_color_support) {
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
+ VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT});
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
+ VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT});
+ }
+ }
+
+ desc.format = AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM;
+ if (AHardwareBuffer_isSupported(&desc)) {
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
+ VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+ if (wide_color_support) {
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
+ VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
+ }
}
VkResult result = VK_SUCCESS;
if (formats) {
- uint32_t out_count = 0;
- uint32_t transfer_count = 0;
- if (*count < total_num_formats)
+ uint32_t transfer_count = all_formats.size();
+ if (transfer_count > *count) {
+ transfer_count = *count;
result = VK_INCOMPLETE;
- transfer_count = std::min(*count, kNumFormats);
- std::copy(kFormats, kFormats + transfer_count, formats);
- out_count += transfer_count;
- if (wide_color_support) {
- transfer_count = std::min(*count - out_count, kNumWideColorFormats);
- std::copy(kWideColorFormats, kWideColorFormats + transfer_count,
- formats + out_count);
- out_count += transfer_count;
}
- *count = out_count;
+ std::copy(all_formats.begin(), all_formats.begin() + transfer_count,
+ formats);
+ *count = transfer_count;
} else {
- *count = total_num_formats;
+ *count = all_formats.size();
}
+
return result;
}
@@ -841,7 +860,7 @@
ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
- if (err != 0 || query_value < 0) {
+ if (err != android::OK || query_value < 0) {
ALOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) value=%d",
strerror(-err), err, query_value);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -849,7 +868,7 @@
uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value);
- if (err != 0 || query_value < 0) {
+ if (err != android::OK || query_value < 0) {
ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d) value=%d",
strerror(-err), err, query_value);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -941,12 +960,12 @@
int width = 0, height = 0;
err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
strerror(-err), err);
}
err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
strerror(-err), err);
}
@@ -1070,17 +1089,23 @@
// dequeue all buffers.
//
// TODO(http://b/134186185) recycle swapchain images more efficiently
- err = native_window_api_disconnect(surface.window.get(),
- NATIVE_WINDOW_API_EGL);
- ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)",
+ ANativeWindow* window = surface.window.get();
+ err = native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+ ALOGW_IF(err != android::OK, "native_window_api_disconnect failed: %s (%d)",
strerror(-err), err);
- err =
- native_window_api_connect(surface.window.get(), NATIVE_WINDOW_API_EGL);
- ALOGW_IF(err != 0, "native_window_api_connect failed: %s (%d)",
+ err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+ ALOGW_IF(err != android::OK, "native_window_api_connect failed: %s (%d)",
strerror(-err), err);
- err = native_window_set_buffer_count(surface.window.get(), 0);
- if (err != 0) {
+ err = window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, -1);
+ if (err != android::OK) {
+ ALOGE("window->perform(SET_DEQUEUE_TIMEOUT) failed: %s (%d)",
+ strerror(-err), err);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+
+ err = native_window_set_buffer_count(window, 0);
+ if (err != android::OK) {
ALOGE("native_window_set_buffer_count(0) failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -1088,22 +1113,22 @@
int swap_interval =
create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1;
- err = surface.window->setSwapInterval(surface.window.get(), swap_interval);
- if (err != 0) {
+ err = window->setSwapInterval(window, swap_interval);
+ if (err != android::OK) {
ALOGE("native_window->setSwapInterval(1) failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
- err = native_window_set_shared_buffer_mode(surface.window.get(), false);
- if (err != 0) {
+ err = native_window_set_shared_buffer_mode(window, false);
+ if (err != android::OK) {
ALOGE("native_window_set_shared_buffer_mode(false) failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
- err = native_window_set_auto_refresh(surface.window.get(), false);
- if (err != 0) {
+ err = native_window_set_auto_refresh(window, false);
+ if (err != android::OK) {
ALOGE("native_window_set_auto_refresh(false) failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -1113,25 +1138,23 @@
const auto& dispatch = GetData(device).driver;
- err = native_window_set_buffers_format(surface.window.get(),
- native_pixel_format);
- if (err != 0) {
+ err = native_window_set_buffers_format(window, native_pixel_format);
+ if (err != android::OK) {
ALOGE("native_window_set_buffers_format(%d) failed: %s (%d)",
native_pixel_format, strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
- err = native_window_set_buffers_data_space(surface.window.get(),
- native_dataspace);
- if (err != 0) {
+ err = native_window_set_buffers_data_space(window, native_dataspace);
+ if (err != android::OK) {
ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)",
native_dataspace, strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
err = native_window_set_buffers_dimensions(
- surface.window.get(), static_cast<int>(create_info->imageExtent.width),
+ window, static_cast<int>(create_info->imageExtent.width),
static_cast<int>(create_info->imageExtent.height));
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
create_info->imageExtent.width, create_info->imageExtent.height,
strerror(-err), err);
@@ -1147,9 +1170,8 @@
// it's job the two transforms cancel each other out and the compositor ends
// up applying an identity transform to the app's buffer.
err = native_window_set_buffers_transform(
- surface.window.get(),
- InvertTransformToNative(create_info->preTransform));
- if (err != 0) {
+ window, InvertTransformToNative(create_info->preTransform));
+ if (err != android::OK) {
ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
InvertTransformToNative(create_info->preTransform),
strerror(-err), err);
@@ -1157,8 +1179,8 @@
}
err = native_window_set_scaling_mode(
- surface.window.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- if (err != 0) {
+ window, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ if (err != android::OK) {
ALOGE("native_window_set_scaling_mode(SCALE_TO_WINDOW) failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -1168,26 +1190,25 @@
if (create_info->presentMode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
swapchain_image_usage |= VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID;
- err = native_window_set_shared_buffer_mode(surface.window.get(), true);
- if (err != 0) {
+ err = native_window_set_shared_buffer_mode(window, true);
+ if (err != android::OK) {
ALOGE("native_window_set_shared_buffer_mode failed: %s (%d)", strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
}
if (create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
- err = native_window_set_auto_refresh(surface.window.get(), true);
- if (err != 0) {
+ err = native_window_set_auto_refresh(window, true);
+ if (err != android::OK) {
ALOGE("native_window_set_auto_refresh failed: %s (%d)", strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
}
int query_value;
- err = surface.window->query(surface.window.get(),
- NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &query_value);
- if (err != 0 || query_value < 0) {
+ err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &query_value);
+ if (err != android::OK || query_value < 0) {
ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err,
query_value);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -1203,8 +1224,8 @@
// in place for that to work yet. Note we only lie to the lower layer-- we
// don't want to give the app back a swapchain with extra images (which they
// can't actually use!).
- err = native_window_set_buffer_count(surface.window.get(), std::max(2u, num_images));
- if (err != 0) {
+ err = native_window_set_buffer_count(window, std::max(2u, num_images));
+ if (err != android::OK) {
ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", num_images,
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -1242,16 +1263,15 @@
createProtectedSwapchain = true;
native_usage |= BufferUsage::PROTECTED;
}
- err = native_window_set_usage(surface.window.get(), native_usage);
- if (err != 0) {
+ err = native_window_set_usage(window, native_usage);
+ if (err != android::OK) {
ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
int transform_hint;
- err = surface.window->query(surface.window.get(),
- NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
- if (err != 0) {
+ err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
+ if (err != android::OK) {
ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -1307,9 +1327,8 @@
Swapchain::Image& img = swapchain->images[i];
ANativeWindowBuffer* buffer;
- err = surface.window->dequeueBuffer(surface.window.get(), &buffer,
- &img.dequeue_fence);
- if (err != 0) {
+ err = window->dequeueBuffer(window, &buffer, &img.dequeue_fence);
+ if (err != android::OK) {
ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err);
switch (-err) {
case ENOMEM:
@@ -1353,8 +1372,8 @@
Swapchain::Image& img = swapchain->images[i];
if (img.dequeued) {
if (!swapchain->shared) {
- surface.window->cancelBuffer(surface.window.get(), img.buffer.get(),
- img.dequeue_fence);
+ window->cancelBuffer(window, img.buffer.get(),
+ img.dequeue_fence);
img.dequeue_fence = -1;
img.dequeued = false;
}
@@ -1432,10 +1451,6 @@
if (swapchain.surface.swapchain_handle != swapchain_handle)
return VK_ERROR_OUT_OF_DATE_KHR;
- ALOGW_IF(
- timeout != UINT64_MAX,
- "vkAcquireNextImageKHR: non-infinite timeouts not yet implemented");
-
if (swapchain.shared) {
// In shared mode, we keep the buffer dequeued all the time, so we don't
// want to dequeue a buffer here. Instead, just ask the driver to ensure
@@ -1446,10 +1461,27 @@
return result;
}
+ const nsecs_t acquire_next_image_timeout =
+ timeout > (uint64_t)std::numeric_limits<nsecs_t>::max() ? -1 : timeout;
+ if (acquire_next_image_timeout != swapchain.acquire_next_image_timeout) {
+ // Cache the timeout to avoid the duplicate binder cost.
+ err = window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT,
+ acquire_next_image_timeout);
+ if (err != android::OK) {
+ ALOGE("window->perform(SET_DEQUEUE_TIMEOUT) failed: %s (%d)",
+ strerror(-err), err);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ swapchain.acquire_next_image_timeout = acquire_next_image_timeout;
+ }
+
ANativeWindowBuffer* buffer;
int fence_fd;
err = window->dequeueBuffer(window, &buffer, &fence_fd);
- if (err != 0) {
+ if (err == android::TIMED_OUT) {
+ ALOGW("dequeueBuffer timed out: %s (%d)", strerror(-err), err);
+ return timeout ? VK_TIMEOUT : VK_NOT_READY;
+ } else if (err != android::OK) {
ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
@@ -1656,7 +1688,7 @@
uint64_t nativeFrameId = 0;
err = native_window_get_next_frame_id(
window, &nativeFrameId);
- if (err != android::NO_ERROR) {
+ if (err != android::OK) {
ALOGE("Failed to get next native frame ID.");
}
@@ -1680,7 +1712,7 @@
err = window->queueBuffer(window, img.buffer.get(), fence);
// queueBuffer always closes fence, even on error
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
swapchain_result = WorstPresentResult(
swapchain_result, VK_ERROR_OUT_OF_DATE_KHR);
@@ -1700,17 +1732,15 @@
ANativeWindowBuffer* buffer;
int fence_fd;
err = window->dequeueBuffer(window, &buffer, &fence_fd);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
swapchain_result = WorstPresentResult(swapchain_result,
VK_ERROR_SURFACE_LOST_KHR);
- }
- else if (img.buffer != buffer) {
+ } else if (img.buffer != buffer) {
ALOGE("got wrong image back for shared swapchain");
swapchain_result = WorstPresentResult(swapchain_result,
VK_ERROR_SURFACE_LOST_KHR);
- }
- else {
+ } else {
img.dequeue_fence = fence_fd;
img.dequeued = true;
}
@@ -1722,7 +1752,7 @@
int window_transform_hint;
err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
&window_transform_hint);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
strerror(-err), err);
swapchain_result = WorstPresentResult(