Merge "Use uint32_t for input source"
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index e61eb6e..d236f76 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -104,12 +104,12 @@
EXPECT_EQ(-1, validate_apk_path(badint2))
<< badint2 << " should be rejected as a invalid path";
- // Only one subdir should be allowed.
- const char *bad_path3 = TEST_APP_DIR "example.com/subdir/pkg.apk";
+ // Should not have more than two sub directories
+ const char *bad_path3 = TEST_APP_DIR "random/example.com/subdir/pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path3))
<< bad_path3 << " should be rejected as a invalid path";
- const char *bad_path4 = TEST_APP_DIR "example.com/subdir/../pkg.apk";
+ const char *bad_path4 = TEST_APP_DIR "random/example.com/subdir/pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path4))
<< bad_path4 << " should be rejected as a invalid path";
@@ -120,6 +120,7 @@
TEST_F(UtilsTest, IsValidApkPath_TopDir) {
EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example"));
+ EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/random/com.example"));
EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example"));
EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example"));
EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example"));
@@ -127,6 +128,7 @@
TEST_F(UtilsTest, IsValidApkPath_TopFile) {
EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example/base.apk"));
+ EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/random/com.example/base.apk"));
EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example/base.apk"));
EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example/base.apk"));
EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example/base.apk"));
@@ -134,6 +136,7 @@
TEST_F(UtilsTest, IsValidApkPath_OatDir) {
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat"));
+ EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat"));
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat"));
@@ -141,6 +144,7 @@
TEST_F(UtilsTest, IsValidApkPath_OatDirDir) {
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64"));
+ EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat/arm64"));
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64"));
@@ -148,6 +152,7 @@
TEST_F(UtilsTest, IsValidApkPath_OatDirDirFile) {
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64/base.odex"));
+ EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat/arm64/base.odex"));
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64/base.odex"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64/base.odex"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64/base.odex"));
@@ -164,6 +169,10 @@
EXPECT_EQ(0, validate_apk_path(path2))
<< path2 << " should be allowed as a valid path";
+ const char *path3 = TEST_APP_DIR "random/example.com/example.apk";
+ EXPECT_EQ(0, validate_apk_path(path3))
+ << path3 << " should be allowed as a valid path";
+
const char *badpriv1 = TEST_APP_PRIVATE_DIR "../example.apk";
EXPECT_EQ(-1, validate_apk_path(badpriv1))
<< badpriv1 << " should be rejected as a invalid path";
@@ -172,16 +181,16 @@
EXPECT_EQ(-1, validate_apk_path(badpriv2))
<< badpriv2 << " should be rejected as a invalid path";
- // Only one subdir should be allowed.
- const char *bad_path3 = TEST_APP_PRIVATE_DIR "example.com/subdir/pkg.apk";
+ // Only one or two subdir should be allowed.
+ const char *bad_path3 = TEST_APP_PRIVATE_DIR "random/example.com/subdir/pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path3))
<< bad_path3 << " should be rejected as a invalid path";
- const char *bad_path4 = TEST_APP_PRIVATE_DIR "example.com/subdir/../pkg.apk";
+ const char *bad_path4 = TEST_APP_PRIVATE_DIR "random/example.com/subdir/../pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path4))
<< bad_path4 << " should be rejected as a invalid path";
- const char *bad_path5 = TEST_APP_PRIVATE_DIR "example.com1/../example.com2/pkg.apk";
+ const char *bad_path5 = TEST_APP_PRIVATE_DIR "random/example.com1/../example.com2/pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path5))
<< bad_path5 << " should be rejected as a invalid path";
}
@@ -229,10 +238,16 @@
<< badasec6 << " should be rejected as a invalid path";
}
-TEST_F(UtilsTest, IsValidApkPath_TwoSubdirFail) {
- const char *badasec7 = TEST_ASEC_DIR "com.example.asec/subdir1/pkg.apk";
- EXPECT_EQ(-1, validate_apk_path(badasec7))
- << badasec7 << " should be rejected as a invalid path";
+TEST_F(UtilsTest, IsValidApkPath_TwoSubdir) {
+ const char *badasec7 = TEST_ASEC_DIR "random/com.example.asec/pkg.apk";
+ EXPECT_EQ(0, validate_apk_path(badasec7))
+ << badasec7 << " should be allowed as a valid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_ThreeSubdirFail) {
+ const char *badasec8 = TEST_ASEC_DIR "random/com.example.asec/subdir/pkg.apk";
+ EXPECT_EQ(-1, validate_apk_path(badasec8))
+ << badasec8 << " should be rejcted as an invalid path";
}
TEST_F(UtilsTest, CheckSystemApp_Dir1) {
@@ -511,8 +526,8 @@
EXPECT_EQ(0, validate_apk_path("/data/app/com.example"));
EXPECT_EQ(0, validate_apk_path("/data/app/com.example/file"));
EXPECT_EQ(0, validate_apk_path("/data/app/com.example//file"));
- EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/"));
- EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/file"));
+ EXPECT_EQ(0, validate_apk_path("/data/app/random/com.example/"));
+ EXPECT_EQ(0, validate_apk_path("/data/app/random/com.example/file"));
EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/file"));
EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir//file"));
EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/dir/file"));
@@ -527,8 +542,10 @@
EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/file"));
EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/file"));
EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir//file"));
- EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/file"));
- EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file"));
+ EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/file"));
+ EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file"));
+ EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/dir/file"));
+ EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/dir//file"));
}
TEST_F(UtilsTest, MatchExtension_Valid) {
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 6012822..939cf90 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -954,11 +954,11 @@
}
int validate_apk_path(const char* path) {
- return validate_apk_path_internal(path, 1 /* maxSubdirs */);
+ return validate_apk_path_internal(path, 2 /* maxSubdirs */);
}
int validate_apk_path_subdirs(const char* path) {
- return validate_apk_path_internal(path, 3 /* maxSubdirs */);
+ return validate_apk_path_internal(path, 4 /* maxSubdirs */);
}
int ensure_config_user_dirs(userid_t userid) {
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 238c9dc..f16c39c 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -435,7 +435,8 @@
Vector<Obituary>* obits = mObituaries;
if(obits != nullptr) {
if (!obits->isEmpty()) {
- ALOGI("onLastStrongRef automatically unlinking death recipients");
+ ALOGI("onLastStrongRef automatically unlinking death recipients: %s",
+ mDescriptorCache.size() ? String8(mDescriptorCache).c_str() : "<uncached descriptor>");
}
if (ipc) ipc->clearDeathNotification(mHandle, this);
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index b3afd81..55419eb 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -19,6 +19,9 @@
"name": "binderStabilityTest"
},
{
+ "name": "libbinder_ndk_unit_test"
+ },
+ {
"name": "CtsNdkBinderTestCases"
}
]
diff --git a/libs/binder/ndk/runtests.sh b/libs/binder/ndk/runtests.sh
deleted file mode 100755
index a0c49fb..0000000
--- a/libs/binder/ndk/runtests.sh
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env bash
-
-# Copyright (C) 2018 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.
-
-if [ -z $ANDROID_BUILD_TOP ]; then
- echo "You need to source and lunch before you can use this script"
- exit 1
-fi
-
-set -ex
-
-function run_libbinder_ndk_test() {
- adb shell /data/nativetest64/libbinder_ndk_test_server/libbinder_ndk_test_server &
-
- # avoid getService 1s delay for most runs, non-critical
- sleep 0.1
-
- adb shell /data/nativetest64/libbinder_ndk_test_client/libbinder_ndk_test_client; \
- adb shell killall libbinder_ndk_test_server
-}
-
-[ "$1" != "--skip-build" ] && $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode \
- MODULES-IN-frameworks-native-libs-binder-ndk
-
-adb root
-adb wait-for-device
-adb sync data
-
-# very simple unit tests, tests things outside of the NDK as well
-run_libbinder_ndk_test
-
-# CTS tests (much more comprehensive, new tests should ideally go here)
-atest android.binder.cts
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp
index daaaa5a..513d8c2 100644
--- a/libs/binder/ndk/test/Android.bp
+++ b/libs/binder/ndk/test/Android.bp
@@ -56,16 +56,17 @@
// specifically the parts which are outside of the NDK. Actual users should
// also instead use AIDL to generate these stubs. See android.binder.cts.
cc_test {
- name: "libbinder_ndk_test_client",
+ name: "libbinder_ndk_unit_test",
defaults: ["test_libbinder_ndk_test_defaults"],
- srcs: ["main_client.cpp"],
-}
+ srcs: ["libbinder_ndk_unit_test.cpp"],
+ static_libs: [
+ "IBinderNdkUnitTest-ndk_platform",
+ ],
+ test_suites: ["general-tests"],
+ require_root: true,
-cc_test {
- name: "libbinder_ndk_test_server",
- defaults: ["test_libbinder_ndk_test_defaults"],
- srcs: ["main_server.cpp"],
- gtest: false,
+ // force since binderVendorDoubleLoadTest has its own
+ auto_gen_config: true,
}
cc_test {
@@ -85,7 +86,7 @@
"libbinder_ndk",
"libutils",
],
- test_suites: ["device-tests"],
+ test_suites: ["general-tests"],
}
aidl_interface {
@@ -95,3 +96,11 @@
"IBinderVendorDoubleLoadTest.aidl",
],
}
+
+aidl_interface {
+ name: "IBinderNdkUnitTest",
+ srcs: [
+ "IBinderNdkUnitTest.aidl",
+ "IEmpty.aidl",
+ ],
+}
diff --git a/libs/binder/ndk/test/IBinderNdkUnitTest.aidl b/libs/binder/ndk/test/IBinderNdkUnitTest.aidl
new file mode 100644
index 0000000..6e8e463
--- /dev/null
+++ b/libs/binder/ndk/test/IBinderNdkUnitTest.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This AIDL is to test things that can't be tested in CtsNdkBinderTestCases
+// because it requires libbinder_ndk implementation details or APIs not
+// available to apps. Please prefer adding tests to CtsNdkBinderTestCases
+// over here.
+
+import IEmpty;
+
+interface IBinderNdkUnitTest {
+ void takeInterface(IEmpty test);
+ void forceFlushCommands();
+}
diff --git a/libs/binder/ndk/test/IEmpty.aidl b/libs/binder/ndk/test/IEmpty.aidl
new file mode 100644
index 0000000..95e4341
--- /dev/null
+++ b/libs/binder/ndk/test/IEmpty.aidl
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface IEmpty { }
diff --git a/libs/binder/ndk/test/main_client.cpp b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
similarity index 66%
rename from libs/binder/ndk/test/main_client.cpp
rename to libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
index 8467734..51dd169 100644
--- a/libs/binder/ndk/test/main_client.cpp
+++ b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <aidl/BnBinderNdkUnitTest.h>
+#include <aidl/BnEmpty.h>
#include <android-base/logging.h>
#include <android/binder_ibinder_jni.h>
#include <android/binder_manager.h>
@@ -21,6 +23,11 @@
#include <gtest/gtest.h>
#include <iface/iface.h>
+// warning: this is assuming that libbinder_ndk is using the same copy
+// of libbinder that we are.
+#include <binder/IPCThreadState.h>
+
+#include <sys/prctl.h>
#include <chrono>
#include <condition_variable>
#include <mutex>
@@ -28,6 +35,65 @@
using ::android::sp;
constexpr char kExistingNonNdkService[] = "SurfaceFlinger";
+constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest";
+
+class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest {
+ ndk::ScopedAStatus takeInterface(const std::shared_ptr<aidl::IEmpty>& empty) {
+ (void)empty;
+ return ndk::ScopedAStatus::ok();
+ }
+ ndk::ScopedAStatus forceFlushCommands() {
+ // warning: this is assuming that libbinder_ndk is using the same copy
+ // of libbinder that we are.
+ android::IPCThreadState::self()->flushCommands();
+ return ndk::ScopedAStatus::ok();
+ }
+};
+
+int generatedService() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+ auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>();
+ binder_status_t status =
+ AServiceManager_addService(service->asBinder().get(), kBinderNdkUnitTestService);
+
+ if (status != STATUS_OK) {
+ LOG(FATAL) << "Could not register: " << status << " " << kBinderNdkUnitTestService;
+ }
+
+ ABinderProcess_joinThreadPool();
+
+ return 1; // should not return
+}
+
+// manually-written parceling class considered bad practice
+class MyFoo : public IFoo {
+ binder_status_t doubleNumber(int32_t in, int32_t* out) override {
+ *out = 2 * in;
+ LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
+ return STATUS_OK;
+ }
+
+ binder_status_t die() override {
+ LOG(FATAL) << "IFoo::die called!";
+ return STATUS_UNKNOWN_ERROR;
+ }
+};
+
+int manualService(const char* instance) {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+ // Strong reference to MyFoo kept by service manager.
+ binder_status_t status = (new MyFoo)->addService(instance);
+
+ if (status != STATUS_OK) {
+ LOG(FATAL) << "Could not register: " << status << " " << instance;
+ }
+
+ ABinderProcess_joinThreadPool();
+
+ return 1; // should not return
+}
// This is too slow
// TEST(NdkBinder, GetServiceThatDoesntExist) {
@@ -87,14 +153,14 @@
EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
foo = nullptr;
- AIBinder_decStrong(binder);
- binder = nullptr;
std::unique_lock<std::mutex> lock(deathMutex);
EXPECT_TRUE(deathCv.wait_for(lock, 1s, [&] { return deathRecieved; }));
EXPECT_TRUE(deathRecieved);
AIBinder_DeathRecipient_delete(recipient);
+ AIBinder_decStrong(binder);
+ binder = nullptr;
}
TEST(NdkBinder, RetrieveNonNdkService) {
@@ -196,9 +262,56 @@
EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2));
}
+TEST(NdkBinder, SentAidlBinderCanBeDestroyed) {
+ static volatile bool destroyed = false;
+ static std::mutex dMutex;
+ static std::condition_variable cv;
+
+ class MyEmpty : public aidl::BnEmpty {
+ virtual ~MyEmpty() {
+ destroyed = true;
+ cv.notify_one();
+ }
+ };
+
+ std::shared_ptr<MyEmpty> empty = ndk::SharedRefBase::make<MyEmpty>();
+
+ ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService));
+ std::shared_ptr<aidl::IBinderNdkUnitTest> service =
+ aidl::IBinderNdkUnitTest::fromBinder(binder);
+
+ EXPECT_FALSE(destroyed);
+
+ service->takeInterface(empty);
+ service->forceFlushCommands();
+ empty = nullptr;
+
+ // give other binder thread time to process commands
+ {
+ using namespace std::chrono_literals;
+ std::unique_lock<std::mutex> lk(dMutex);
+ cv.wait_for(lk, 1s, [] { return destroyed; });
+ }
+
+ EXPECT_TRUE(destroyed);
+}
+
int main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return manualService(IFoo::kInstanceNameToDieFor);
+ }
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return manualService(IFoo::kSomeInstanceName);
+ }
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return generatedService();
+ }
+
ABinderProcess_setThreadPoolMaxThreadCount(1); // to recieve death notifications/callbacks
ABinderProcess_startThreadPool();
diff --git a/libs/binder/ndk/test/main_server.cpp b/libs/binder/ndk/test/main_server.cpp
deleted file mode 100644
index a6e17e8..0000000
--- a/libs/binder/ndk/test/main_server.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/logging.h>
-#include <android/binder_process.h>
-#include <iface/iface.h>
-
-using ::android::sp;
-
-class MyFoo : public IFoo {
- binder_status_t doubleNumber(int32_t in, int32_t* out) override {
- *out = 2 * in;
- LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
- return STATUS_OK;
- }
-
- binder_status_t die() override {
- LOG(FATAL) << "IFoo::die called!";
- return STATUS_UNKNOWN_ERROR;
- }
-};
-
-int service(const char* instance) {
- ABinderProcess_setThreadPoolMaxThreadCount(0);
-
- // Strong reference to MyFoo kept by service manager.
- binder_status_t status = (new MyFoo)->addService(instance);
-
- if (status != STATUS_OK) {
- LOG(FATAL) << "Could not register: " << status << " " << instance;
- }
-
- ABinderProcess_joinThreadPool();
-
- return 1; // should not return
-}
-
-int main() {
- if (fork() == 0) {
- return service(IFoo::kInstanceNameToDieFor);
- }
-
- return service(IFoo::kSomeInstanceName);
-}
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 3f359f5..4e62da7 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -83,6 +83,7 @@
if (stats.size() > 0) {
mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
mTransformHint = stats[0].transformHint;
+ mBufferItemConsumer->setTransformHint(mTransformHint);
} else {
ALOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
mPendingReleaseItem.releaseFence = nullptr;
@@ -151,7 +152,7 @@
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->setFrame(mSurfaceControl, {0, 0, mWidth, mHeight});
t->setCrop(mSurfaceControl, computeCrop(bufferItem));
t->setTransform(mSurfaceControl, bufferItem.mTransform);
t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 3b0120b..9e5d681 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -84,54 +84,54 @@
return INVALID_OPERATION;
}
-BufferQueueCore::BufferQueueCore() :
- mMutex(),
- mIsAbandoned(false),
- mConsumerControlledByApp(false),
- mConsumerName(getUniqueName()),
- mConsumerListener(),
- mConsumerUsageBits(0),
- mConsumerIsProtected(false),
- mConnectedApi(NO_CONNECTED_API),
- mLinkedToDeath(),
- mConnectedProducerListener(),
- mBufferReleasedCbEnabled(false),
- mSlots(),
- mQueue(),
- mFreeSlots(),
- mFreeBuffers(),
- mUnusedSlots(),
- mActiveBuffers(),
- mDequeueCondition(),
- mDequeueBufferCannotBlock(false),
- mQueueBufferCanDrop(false),
- mLegacyBufferDrop(true),
- mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
- mDefaultWidth(1),
- mDefaultHeight(1),
- mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
- mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
- mMaxAcquiredBufferCount(1),
- mMaxDequeuedBufferCount(1),
- mBufferHasBeenQueued(false),
- mFrameCounter(0),
- mTransformHint(0),
- mIsAllocating(false),
- mIsAllocatingCondition(),
- mAllowAllocation(true),
- mBufferAge(0),
- mGenerationNumber(0),
- mAsyncMode(false),
- mSharedBufferMode(false),
- mAutoRefresh(false),
- mSharedBufferSlot(INVALID_BUFFER_SLOT),
- mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
- HAL_DATASPACE_UNKNOWN),
- mLastQueuedSlot(INVALID_BUFFER_SLOT),
- mUniqueId(getUniqueId()),
- mAutoPrerotation(false),
- mTransformHintInUse(0)
-{
+BufferQueueCore::BufferQueueCore()
+ : mMutex(),
+ mIsAbandoned(false),
+ mConsumerControlledByApp(false),
+ mConsumerName(getUniqueName()),
+ mConsumerListener(),
+ mConsumerUsageBits(0),
+ mConsumerIsProtected(false),
+ mConnectedApi(NO_CONNECTED_API),
+ mLinkedToDeath(),
+ mConnectedProducerListener(),
+ mBufferReleasedCbEnabled(false),
+ mSlots(),
+ mQueue(),
+ mFreeSlots(),
+ mFreeBuffers(),
+ mUnusedSlots(),
+ mActiveBuffers(),
+ mDequeueCondition(),
+ mDequeueBufferCannotBlock(false),
+ mQueueBufferCanDrop(false),
+ mLegacyBufferDrop(true),
+ mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
+ mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
+ mMaxAcquiredBufferCount(1),
+ mMaxDequeuedBufferCount(1),
+ mBufferHasBeenQueued(false),
+ mFrameCounter(0),
+ mTransformHint(0),
+ mIsAllocating(false),
+ mIsAllocatingCondition(),
+ mAllowAllocation(true),
+ mBufferAge(0),
+ mGenerationNumber(0),
+ mAsyncMode(false),
+ mSharedBufferMode(false),
+ mAutoRefresh(false),
+ mSharedBufferSlot(INVALID_BUFFER_SLOT),
+ mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
+ HAL_DATASPACE_UNKNOWN),
+ mLastQueuedSlot(INVALID_BUFFER_SLOT),
+ mUniqueId(getUniqueId()),
+ mAutoPrerotation(false),
+ mTransformHintInUse(0),
+ mFrameRate(0) {
int numStartingBuffers = getMaxBufferCountLocked();
for (int s = 0; s < numStartingBuffers; s++) {
mFreeSlots.insert(s);
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 3a5fc7f..85b0fd0 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -321,17 +321,17 @@
float MotionEvent::getXCursorPosition() const {
const float rawX = getRawXCursorPosition();
- return rawX + mXOffset;
+ return rawX * mXScale + mXOffset;
}
float MotionEvent::getYCursorPosition() const {
const float rawY = getRawYCursorPosition();
- return rawY + mYOffset;
+ return rawY * mYScale + mYOffset;
}
void MotionEvent::setCursorPosition(float x, float y) {
- mRawXCursorPosition = x - mXOffset;
- mRawYCursorPosition = y - mYOffset;
+ mRawXCursorPosition = (x - mXOffset) / mXScale;
+ mRawYCursorPosition = (y - mYOffset) / mYScale;
}
const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
@@ -346,9 +346,9 @@
float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis);
switch (axis) {
case AMOTION_EVENT_AXIS_X:
- return value + mXOffset;
+ return value * mXScale + mXOffset;
case AMOTION_EVENT_AXIS_Y:
- return value + mYOffset;
+ return value * mYScale + mYOffset;
}
return value;
}
@@ -368,9 +368,9 @@
float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
switch (axis) {
case AMOTION_EVENT_AXIS_X:
- return value + mXOffset;
+ return value * mXScale + mXOffset;
case AMOTION_EVENT_AXIS_Y:
- return value + mYOffset;
+ return value * mYScale + mYOffset;
}
return value;
}
@@ -442,11 +442,11 @@
float oldXOffset = mXOffset;
float oldYOffset = mYOffset;
float newX, newY;
- float rawX = getRawX(0);
- float rawY = getRawY(0);
- transformPoint(matrix, rawX + oldXOffset, rawY + oldYOffset, &newX, &newY);
- mXOffset = newX - rawX;
- mYOffset = newY - rawY;
+ float scaledRawX = getRawX(0) * mXScale;
+ float scaledRawY = getRawY(0) * mYScale;
+ transformPoint(matrix, scaledRawX + oldXOffset, scaledRawY + oldYOffset, &newX, &newY);
+ mXOffset = newX - scaledRawX;
+ mYOffset = newY - scaledRawY;
// Determine how the origin is transformed by the matrix so that we
// can transform orientation vectors.
@@ -455,22 +455,22 @@
// Apply the transformation to cursor position.
if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
- float x = mRawXCursorPosition + oldXOffset;
- float y = mRawYCursorPosition + oldYOffset;
+ float x = mRawXCursorPosition * mXScale + oldXOffset;
+ float y = mRawYCursorPosition * mYScale + oldYOffset;
transformPoint(matrix, x, y, &x, &y);
- mRawXCursorPosition = x - mXOffset;
- mRawYCursorPosition = y - mYOffset;
+ mRawXCursorPosition = (x - mXOffset) / mXScale;
+ mRawYCursorPosition = (y - mYOffset) / mYScale;
}
// Apply the transformation to all samples.
size_t numSamples = mSamplePointerCoords.size();
for (size_t i = 0; i < numSamples; i++) {
PointerCoords& c = mSamplePointerCoords.editItemAt(i);
- float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) + oldXOffset;
- float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) + oldYOffset;
+ float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) * mXScale + oldXOffset;
+ float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) * mYScale + oldYOffset;
transformPoint(matrix, x, y, &x, &y);
- c.setAxisValue(AMOTION_EVENT_AXIS_X, x - mXOffset);
- c.setAxisValue(AMOTION_EVENT_AXIS_Y, y - mYOffset);
+ c.setAxisValue(AMOTION_EVENT_AXIS_X, (x - mXOffset) / mXScale);
+ c.setAxisValue(AMOTION_EVENT_AXIS_Y, (y - mYOffset) / mYScale);
float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 3d7c34e..dce1f29 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -375,19 +375,19 @@
ASSERT_EQ(211, event->getRawY(0));
ASSERT_EQ(221, event->getRawY(1));
- ASSERT_EQ(X_OFFSET + 10, event->getHistoricalX(0, 0));
- ASSERT_EQ(X_OFFSET + 20, event->getHistoricalX(1, 0));
- ASSERT_EQ(X_OFFSET + 110, event->getHistoricalX(0, 1));
- ASSERT_EQ(X_OFFSET + 120, event->getHistoricalX(1, 1));
- ASSERT_EQ(X_OFFSET + 210, event->getX(0));
- ASSERT_EQ(X_OFFSET + 220, event->getX(1));
+ ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0));
+ ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0));
+ ASSERT_EQ(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1));
+ ASSERT_EQ(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1));
+ ASSERT_EQ(X_OFFSET + 210 * X_SCALE, event->getX(0));
+ ASSERT_EQ(X_OFFSET + 220 * X_SCALE, event->getX(1));
- ASSERT_EQ(Y_OFFSET + 11, event->getHistoricalY(0, 0));
- ASSERT_EQ(Y_OFFSET + 21, event->getHistoricalY(1, 0));
- ASSERT_EQ(Y_OFFSET + 111, event->getHistoricalY(0, 1));
- ASSERT_EQ(Y_OFFSET + 121, event->getHistoricalY(1, 1));
- ASSERT_EQ(Y_OFFSET + 211, event->getY(0));
- ASSERT_EQ(Y_OFFSET + 221, event->getY(1));
+ ASSERT_EQ(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0));
+ ASSERT_EQ(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0));
+ ASSERT_EQ(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1));
+ ASSERT_EQ(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1));
+ ASSERT_EQ(Y_OFFSET + 211 * Y_SCALE, event->getY(0));
+ ASSERT_EQ(Y_OFFSET + 221 * Y_SCALE, event->getY(1));
ASSERT_EQ(12, event->getHistoricalPressure(0, 0));
ASSERT_EQ(22, event->getHistoricalPressure(1, 0));
@@ -513,8 +513,8 @@
ASSERT_EQ(210 * 2, event.getRawX(0));
ASSERT_EQ(211 * 2, event.getRawY(0));
- ASSERT_EQ((X_OFFSET + 210) * 2, event.getX(0));
- ASSERT_EQ((Y_OFFSET + 211) * 2, event.getY(0));
+ ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0));
+ ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0));
ASSERT_EQ(212, event.getPressure(0));
ASSERT_EQ(213, event.getSize(0));
ASSERT_EQ(214 * 2, event.getTouchMajor(0));
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index b002893..d4bbf6c 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -221,8 +221,8 @@
EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition());
EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
- EXPECT_EQ(xCursorPosition + xOffset, motionEvent->getXCursorPosition());
- EXPECT_EQ(yCursorPosition + yOffset, motionEvent->getYCursorPosition());
+ EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
+ EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
EXPECT_EQ(downTime, motionEvent->getDownTime());
EXPECT_EQ(eventTime, motionEvent->getEventTime());
EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
@@ -237,10 +237,10 @@
motionEvent->getRawX(i));
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
motionEvent->getRawY(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset,
- motionEvent->getX(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset,
- motionEvent->getY(i));
+ EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) * xScale + xOffset,
+ motionEvent->getX(i));
+ EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) * yScale + yOffset,
+ motionEvent->getY(i));
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
motionEvent->getPressure(i));
EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index e257704..98605ba 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -1003,7 +1003,7 @@
setViewportAndProjection(display.physicalDisplay, display.clip);
} else {
setViewportAndProjection(display.physicalDisplay, display.clip);
- auto status = mBlurFilter->setAsDrawTarget(display);
+ auto status = mBlurFilter->setAsDrawTarget(display, blurLayer->backgroundBlurRadius);
if (status != NO_ERROR) {
ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
buffer->handle);
@@ -1037,7 +1037,7 @@
.build();
for (auto const layer : layers) {
if (blurLayer == layer) {
- auto status = mBlurFilter->prepare(layer->backgroundBlurRadius);
+ auto status = mBlurFilter->prepare();
if (status != NO_ERROR) {
ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
buffer->handle);
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index 091eac9..153935b 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -122,6 +122,14 @@
glBindFramebuffer(GL_FRAMEBUFFER, mFramebufferName);
}
+void GLFramebuffer::bindAsReadBuffer() const {
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, mFramebufferName);
+}
+
+void GLFramebuffer::bindAsDrawBuffer() const {
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferName);
+}
+
void GLFramebuffer::unbind() const {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
index 668685a..69102d6 100644
--- a/libs/renderengine/gl/GLFramebuffer.h
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -48,6 +48,8 @@
int32_t getBufferWidth() const { return mBufferWidth; }
GLenum getStatus() const { return mStatus; }
void bind() const;
+ void bindAsReadBuffer() const;
+ void bindAsDrawBuffer() const;
void unbind() const;
private:
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
index a18a999..48c2560 100644
--- a/libs/renderengine/gl/filters/BlurFilter.cpp
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -31,29 +31,24 @@
namespace gl {
BlurFilter::BlurFilter(GLESRenderEngine& engine)
- : mEngine(engine), mCompositionFbo(engine), mBlurredFbo(engine), mSimpleProgram(engine) {
- mSimpleProgram.compile(getVertexShader(), getSimpleFragShader());
- mSPosLoc = mSimpleProgram.getAttributeLocation("aPosition");
- mSUvLoc = mSimpleProgram.getAttributeLocation("aUV");
- mSTextureLoc = mSimpleProgram.getUniformLocation("uTexture");
+ : mEngine(engine), mCompositionFbo(engine), mBlurredFbo(engine), mMixProgram(engine) {
+ mMixProgram.compile(getVertexShader(), getMixFragShader());
+ mMPosLoc = mMixProgram.getAttributeLocation("aPosition");
+ mMUvLoc = mMixProgram.getAttributeLocation("aUV");
+ mMTextureLoc = mMixProgram.getUniformLocation("uTexture");
+ mMCompositionTextureLoc = mMixProgram.getUniformLocation("uCompositionTexture");
+ mMMixLoc = mMixProgram.getUniformLocation("uMix");
}
-status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display) {
+status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
ATRACE_NAME("BlurFilter::setAsDrawTarget");
+ mRadius = radius;
if (!mTexturesAllocated) {
mDisplayWidth = display.physicalDisplay.width();
mDisplayHeight = display.physicalDisplay.height();
mCompositionFbo.allocateBuffers(mDisplayWidth, mDisplayHeight);
- // Let's use mimap filtering on the offscreen composition texture,
- // this will drastically improve overall shader quality.
- glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 3);
- glBindTexture(GL_TEXTURE_2D, 0);
-
const uint32_t fboWidth = floorf(mDisplayWidth * kFboScale);
const uint32_t fboHeight = floorf(mDisplayHeight * kFboScale);
mBlurredFbo.allocateBuffers(fboWidth, fboHeight);
@@ -94,27 +89,41 @@
status_t BlurFilter::render() {
ATRACE_NAME("BlurFilter::render");
- // Now let's scale our blur up
- mSimpleProgram.useProgram();
+ // Now let's scale our blur up. It will be interpolated with the larger composited
+ // texture for the first frames, to hide downscaling artifacts.
+ GLfloat mix = fmin(1.0, mRadius / kMaxCrossFadeRadius);
+ if (mix >= 1) {
+ mBlurredFbo.bindAsReadBuffer();
+ glBlitFramebuffer(0, 0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(), 0, 0,
+ mDisplayWidth, mDisplayHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ return NO_ERROR;
+ }
+
+ mMixProgram.useProgram();
+ glUniform1f(mMMixLoc, mix);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
- glUniform1i(mSTextureLoc, 0);
+ glUniform1i(mMTextureLoc, 0);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
+ glUniform1i(mMCompositionTextureLoc, 1);
mEngine.checkErrors("Setting final pass uniforms");
- drawMesh(mSUvLoc, mSPosLoc);
+ drawMesh(mMUvLoc, mMPosLoc);
glUseProgram(0);
+ glActiveTexture(GL_TEXTURE0);
return NO_ERROR;
}
string BlurFilter::getVertexShader() const {
return R"SHADER(
#version 310 es
- precision lowp float;
in vec2 aPosition;
- in mediump vec2 aUV;
- out mediump vec2 vUV;
+ in highp vec2 aUV;
+ out highp vec2 vUV;
void main() {
vUV = aUV;
@@ -123,18 +132,22 @@
)SHADER";
}
-string BlurFilter::getSimpleFragShader() const {
+string BlurFilter::getMixFragShader() const {
string shader = R"SHADER(
#version 310 es
- precision lowp float;
+ precision mediump float;
- in mediump vec2 vUV;
+ in highp vec2 vUV;
out vec4 fragColor;
+ uniform sampler2D uCompositionTexture;
uniform sampler2D uTexture;
+ uniform float uMix;
void main() {
- fragColor = texture(uTexture, vUV);
+ vec4 blurred = texture(uTexture, vUV);
+ vec4 composition = texture(uCompositionTexture, vUV);
+ fragColor = mix(composition, blurred, uMix);
}
)SHADER";
return shader;
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
index e265b51..6889939 100644
--- a/libs/renderengine/gl/filters/BlurFilter.h
+++ b/libs/renderengine/gl/filters/BlurFilter.h
@@ -31,22 +31,25 @@
public:
// Downsample FBO to improve performance
static constexpr float kFboScale = 0.25f;
+ // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+ // image, up to this radius.
+ static constexpr float kMaxCrossFadeRadius = 15.0f;
explicit BlurFilter(GLESRenderEngine& engine);
virtual ~BlurFilter(){};
// Set up render targets, redirecting output to offscreen texture.
- status_t setAsDrawTarget(const DisplaySettings&);
+ status_t setAsDrawTarget(const DisplaySettings&, uint32_t radius);
// Allocate any textures needed for the filter.
virtual void allocateTextures() = 0;
// Execute blur passes, rendering to offscreen texture.
- virtual status_t prepare(uint32_t radius) = 0;
+ virtual status_t prepare() = 0;
// Render blur to the bound framebuffer (screen).
status_t render();
protected:
+ uint32_t mRadius;
void drawMesh(GLuint uv, GLuint position);
- string getSimpleFragShader() const;
string getVertexShader() const;
GLESRenderEngine& mEngine;
@@ -58,12 +61,15 @@
uint32_t mDisplayHeight;
private:
+ string getMixFragShader() const;
bool mTexturesAllocated = false;
- GenericProgram mSimpleProgram;
- GLuint mSPosLoc;
- GLuint mSUvLoc;
- GLuint mSTextureLoc;
+ GenericProgram mMixProgram;
+ GLuint mMPosLoc;
+ GLuint mMUvLoc;
+ GLuint mMMixLoc;
+ GLuint mMTextureLoc;
+ GLuint mMCompositionTextureLoc;
};
} // namespace gl
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
index f5ba02a..4d7bf44 100644
--- a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
+++ b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
@@ -26,6 +26,10 @@
#include <utils/Trace.h>
+#define PI 3.14159265359
+#define THETA 0.352
+#define K 1.0 / (2.0 * THETA * THETA)
+
namespace android {
namespace renderengine {
namespace gl {
@@ -39,22 +43,24 @@
mVPosLoc = mVerticalProgram.getAttributeLocation("aPosition");
mVUvLoc = mVerticalProgram.getAttributeLocation("aUV");
mVTextureLoc = mVerticalProgram.getUniformLocation("uTexture");
- mVSizeLoc = mVerticalProgram.getUniformLocation("uSize");
- mVRadiusLoc = mVerticalProgram.getUniformLocation("uRadius");
+ mVIncrementLoc = mVerticalProgram.getUniformLocation("uIncrement");
+ mVNumSamplesLoc = mVerticalProgram.getUniformLocation("uSamples");
+ mVGaussianWeightLoc = mVerticalProgram.getUniformLocation("uGaussianWeights");
mHorizontalProgram.compile(getVertexShader(), getFragmentShader(true));
mHPosLoc = mHorizontalProgram.getAttributeLocation("aPosition");
mHUvLoc = mHorizontalProgram.getAttributeLocation("aUV");
mHTextureLoc = mHorizontalProgram.getUniformLocation("uTexture");
- mHSizeLoc = mHorizontalProgram.getUniformLocation("uSize");
- mHRadiusLoc = mHorizontalProgram.getUniformLocation("uRadius");
+ mHIncrementLoc = mHorizontalProgram.getUniformLocation("uIncrement");
+ mHNumSamplesLoc = mHorizontalProgram.getUniformLocation("uSamples");
+ mHGaussianWeightLoc = mHorizontalProgram.getUniformLocation("uGaussianWeights");
}
void GaussianBlurFilter::allocateTextures() {
mVerticalPassFbo.allocateBuffers(mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight());
}
-status_t GaussianBlurFilter::prepare(uint32_t radius) {
+status_t GaussianBlurFilter::prepare() {
ATRACE_NAME("GaussianBlurFilter::prepare");
if (mVerticalPassFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
@@ -70,21 +76,38 @@
return GL_INVALID_OPERATION;
}
+ mCompositionFbo.bindAsReadBuffer();
+ mBlurredFbo.bindAsDrawBuffer();
+ glBlitFramebuffer(0, 0, mCompositionFbo.getBufferWidth(), mCompositionFbo.getBufferHeight(), 0,
+ 0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(),
+ GL_COLOR_BUFFER_BIT, GL_LINEAR);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+
// First, we'll apply the vertical pass, that receives the flattened background layers.
mVerticalPassFbo.bind();
mVerticalProgram.useProgram();
+ // Precompute gaussian bell curve, and send it to the shader to avoid
+ // unnecessary computations.
+ auto samples = min(mRadius, kNumSamples);
+ GLfloat gaussianWeights[kNumSamples] = {};
+ for (size_t i = 0; i < samples; i++) {
+ float normalized = float(i) / samples;
+ gaussianWeights[i] = (float)exp(-K * normalized * normalized);
+ }
+
// set uniforms
auto width = mVerticalPassFbo.getBufferWidth();
auto height = mVerticalPassFbo.getBufferHeight();
- auto radiusF = fmax(1.0f, radius * kFboScale);
+ auto radiusF = fmax(1.0f, mRadius * kFboScale);
glViewport(0, 0, width, height);
glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
- glGenerateMipmap(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
glUniform1i(mVTextureLoc, 0);
- glUniform2f(mVSizeLoc, width, height);
- glUniform1f(mVRadiusLoc, radiusF);
+ glUniform2f(mVIncrementLoc, radiusF / (width * 2.0f), radiusF / (height * 2.0f));
+ glUniform1i(mVNumSamplesLoc, samples);
+ glUniform1fv(mVGaussianWeightLoc, kNumSamples, gaussianWeights);
mEngine.checkErrors("Setting vertical-diagonal pass uniforms");
drawMesh(mVUvLoc, mVPosLoc);
@@ -97,8 +120,9 @@
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mVerticalPassFbo.getTextureName());
glUniform1i(mHTextureLoc, 0);
- glUniform2f(mHSizeLoc, width, height);
- glUniform1f(mHRadiusLoc, radiusF);
+ glUniform2f(mHIncrementLoc, radiusF / (width * 2.0f), radiusF / (height * 2.0f));
+ glUniform1i(mHNumSamplesLoc, samples);
+ glUniform1fv(mHGaussianWeightLoc, kNumSamples, gaussianWeights);
mEngine.checkErrors("Setting vertical pass uniforms");
drawMesh(mHUvLoc, mHPosLoc);
@@ -115,42 +139,31 @@
}
string GaussianBlurFilter::getFragmentShader(bool horizontal) const {
- string shader = "#version 310 es\n#define DIRECTION ";
- shader += (horizontal ? "1" : "0");
- shader += R"SHADER(
- precision lowp float;
+ stringstream shader;
+ shader << "#version 310 es\n"
+ << "#define DIRECTION " << (horizontal ? "1" : "0") << "\n"
+ << "#define NUM_SAMPLES " << kNumSamples <<
+ R"SHADER(
+ precision mediump float;
uniform sampler2D uTexture;
- uniform vec2 uSize;
- uniform float uRadius;
+ uniform vec2 uIncrement;
+ uniform float[NUM_SAMPLES] uGaussianWeights;
+ uniform int uSamples;
- mediump in vec2 vUV;
+ highp in vec2 vUV;
out vec4 fragColor;
- #define PI 3.14159265359
- #define THETA 0.352
- #define MU 0.0
- #define A 1.0 / (THETA * sqrt(2.0 * PI))
- #define K 1.0 / (2.0 * THETA * THETA)
- #define MAX_SAMPLES 10
-
- float gaussianBellCurve(float x) {
- float tmp = (x - MU);
- return exp(-K * tmp * tmp);
- }
-
- vec3 gaussianBlur(sampler2D texture, mediump vec2 uv, float size,
- mediump vec2 direction, float radius) {
+ vec3 gaussianBlur(sampler2D texture, highp vec2 uv, float inc, vec2 direction) {
float totalWeight = 0.0;
- vec3 blurred = vec3(0.);
- int samples = min(int(ceil(radius / 2.0)), MAX_SAMPLES);
- float inc = radius / (size * 2.0);
+ vec3 blurred = vec3(0.0);
+ float fSamples = 1.0 / float(uSamples);
- for (int i = -samples; i <= samples; i++) {
- float normalized = float(i) / float(samples);
- float weight = gaussianBellCurve(normalized);
+ for (int i = -uSamples; i <= uSamples; i++) {
+ float weight = uGaussianWeights[abs(i)];
+ float normalized = float(i) * fSamples;
float radInc = inc * normalized;
- blurred += weight * (texture(texture, uv + radInc * direction)).rgb;;
+ blurred += weight * (texture(texture, radInc * direction + uv, 0.0)).rgb;
totalWeight += weight;
}
@@ -159,15 +172,15 @@
void main() {
#if DIRECTION == 1
- vec3 color = gaussianBlur(uTexture, vUV, uSize.x, vec2(1.0, 0.0), uRadius);
+ vec3 color = gaussianBlur(uTexture, vUV, uIncrement.x, vec2(1.0, 0.0));
#else
- vec3 color = gaussianBlur(uTexture, vUV, uSize.y, vec2(0.0, 1.0), uRadius);
+ vec3 color = gaussianBlur(uTexture, vUV, uIncrement.y, vec2(0.0, 1.0));
#endif
fragColor = vec4(color, 1.0);
}
)SHADER";
- return shader;
+ return shader.str();
}
} // namespace gl
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.h b/libs/renderengine/gl/filters/GaussianBlurFilter.h
index acf0f07..8580522 100644
--- a/libs/renderengine/gl/filters/GaussianBlurFilter.h
+++ b/libs/renderengine/gl/filters/GaussianBlurFilter.h
@@ -30,8 +30,10 @@
class GaussianBlurFilter : public BlurFilter {
public:
+ static constexpr uint32_t kNumSamples = 12;
+
explicit GaussianBlurFilter(GLESRenderEngine& engine);
- status_t prepare(uint32_t radius) override;
+ status_t prepare() override;
void allocateTextures() override;
private:
@@ -45,16 +47,18 @@
GLuint mVPosLoc;
GLuint mVUvLoc;
GLuint mVTextureLoc;
- GLuint mVSizeLoc;
- GLuint mVRadiusLoc;
+ GLuint mVIncrementLoc;
+ GLuint mVNumSamplesLoc;
+ GLuint mVGaussianWeightLoc;
// Horizontal pass and its uniforms
GenericProgram mHorizontalProgram;
GLuint mHPosLoc;
GLuint mHUvLoc;
GLuint mHTextureLoc;
- GLuint mHSizeLoc;
- GLuint mHRadiusLoc;
+ GLuint mHIncrementLoc;
+ GLuint mHNumSamplesLoc;
+ GLuint mHGaussianWeightLoc;
};
} // namespace gl
diff --git a/libs/renderengine/gl/filters/LensBlurFilter.cpp b/libs/renderengine/gl/filters/LensBlurFilter.cpp
index 799deac..fb29fbb 100644
--- a/libs/renderengine/gl/filters/LensBlurFilter.cpp
+++ b/libs/renderengine/gl/filters/LensBlurFilter.cpp
@@ -62,7 +62,7 @@
mBlurredFbo.getBufferHeight());
}
-status_t LensBlurFilter::prepare(uint32_t radius) {
+status_t LensBlurFilter::prepare() {
ATRACE_NAME("LensBlurFilter::prepare");
if (mVerticalDiagonalPassFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
@@ -86,11 +86,10 @@
// set uniforms
auto width = mVerticalDiagonalPassFbo.getBufferWidth();
auto height = mVerticalDiagonalPassFbo.getBufferHeight();
- auto radiusF = fmax(1.0f, radius * kFboScale);
+ auto radiusF = fmax(1.0f, mRadius * kFboScale);
glViewport(0, 0, width, height);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
- glGenerateMipmap(GL_TEXTURE_2D);
glUniform1i(mVDTexture0Loc, 0);
glUniform2f(mVDSizeLoc, mDisplayWidth, mDisplayHeight);
glUniform1f(mVDRadiusLoc, radiusF);
@@ -134,8 +133,7 @@
string shader = "#version 310 es\n#define DIRECTION ";
shader += (forComposition ? "1" : "0");
shader += R"SHADER(
- precision lowp float;
-
+ precision mediump float;
#define PI 3.14159265359
uniform sampler2D uTexture0;
@@ -143,7 +141,7 @@
uniform float uRadius;
uniform int uNumSamples;
- mediump in vec2 vUV;
+ highp in vec2 vUV;
#if DIRECTION == 0
layout(location = 0) out vec4 fragColor0;
diff --git a/libs/renderengine/gl/filters/LensBlurFilter.h b/libs/renderengine/gl/filters/LensBlurFilter.h
index 8543f0d..1620c5a 100644
--- a/libs/renderengine/gl/filters/LensBlurFilter.h
+++ b/libs/renderengine/gl/filters/LensBlurFilter.h
@@ -31,7 +31,7 @@
class LensBlurFilter : public BlurFilter {
public:
explicit LensBlurFilter(GLESRenderEngine& engine);
- status_t prepare(uint32_t radius) override;
+ status_t prepare() override;
void allocateTextures() override;
private:
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index f6b5935..308e93a 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -83,6 +83,7 @@
srcs: [
"InputListener.cpp",
"InputReaderBase.cpp",
+ "InputThread.cpp",
],
shared_libs: [
diff --git a/services/inputflinger/InputThread.cpp b/services/inputflinger/InputThread.cpp
new file mode 100644
index 0000000..b87f7a1
--- /dev/null
+++ b/services/inputflinger/InputThread.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InputThread.h"
+
+namespace android {
+
+namespace {
+
+// Implementation of Thread from libutils.
+class InputThreadImpl : public Thread {
+public:
+ explicit InputThreadImpl(std::function<void()> loop)
+ : Thread(/* canCallJava */ true), mThreadLoop(loop) {}
+
+ ~InputThreadImpl() {}
+
+private:
+ std::function<void()> mThreadLoop;
+
+ bool threadLoop() override {
+ mThreadLoop();
+ return true;
+ }
+};
+
+} // namespace
+
+InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake)
+ : mName(name), mThreadWake(wake) {
+ mThread = new InputThreadImpl(loop);
+ mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
+}
+
+InputThread::~InputThread() {
+ mThread->requestExit();
+ if (mThreadWake) {
+ mThreadWake();
+ }
+ mThread->requestExitAndWait();
+}
+
+bool InputThread::isCallingThread() {
+ return gettid() == mThread->getTid();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 5ae2419..b2b5145 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -325,24 +325,6 @@
return dispatchEntry;
}
-// --- InputDispatcherThread ---
-
-class InputDispatcher::InputDispatcherThread : public Thread {
-public:
- explicit InputDispatcherThread(InputDispatcher* dispatcher)
- : Thread(/* canCallJava */ true), mDispatcher(dispatcher) {}
-
- ~InputDispatcherThread() {}
-
-private:
- InputDispatcher* mDispatcher;
-
- virtual bool threadLoop() override {
- mDispatcher->dispatchOnce();
- return true;
- }
-};
-
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
@@ -367,8 +349,6 @@
mKeyRepeatState.lastKeyEntry = nullptr;
policy->getDispatcherConfiguration(&mConfig);
-
- mThread = new InputDispatcherThread(this);
}
InputDispatcher::~InputDispatcher() {
@@ -387,25 +367,21 @@
}
status_t InputDispatcher::start() {
- if (mThread->isRunning()) {
+ if (mThread) {
return ALREADY_EXISTS;
}
- return mThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
+ mThread = std::make_unique<InputThread>(
+ "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
+ return OK;
}
status_t InputDispatcher::stop() {
- if (!mThread->isRunning()) {
- return OK;
- }
- if (gettid() == mThread->getTid()) {
- ALOGE("InputDispatcher can only be stopped from outside of the InputDispatcherThread!");
+ if (mThread && mThread->isCallingThread()) {
+ ALOGE("InputDispatcher cannot be stopped from its own thread!");
return INVALID_OPERATION;
}
- // Directly calling requestExitAndWait() causes the thread to not exit
- // if mLooper is waiting for a long timeout.
- mThread->requestExit();
- mLooper->wake();
- return mThread->requestExitAndWait();
+ mThread.reset();
+ return OK;
}
void InputDispatcher::dispatchOnce() {
@@ -2424,26 +2400,28 @@
PointerCoords scaledCoords[MAX_POINTERS];
const PointerCoords* usingCoords = motionEntry->pointerCoords;
- // Set the X and Y offset depending on the input source.
- float xOffset, yOffset;
+ // Set the X and Y offset and X and Y scale depending on the input source.
+ float xOffset = 0.0f, yOffset = 0.0f;
+ float xScale = 1.0f, yScale = 1.0f;
if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
!(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
float globalScaleFactor = dispatchEntry->globalScaleFactor;
- float wxs = dispatchEntry->windowXScale;
- float wys = dispatchEntry->windowYScale;
- xOffset = dispatchEntry->xOffset * wxs;
- yOffset = dispatchEntry->yOffset * wys;
- if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) {
+ xScale = dispatchEntry->windowXScale;
+ yScale = dispatchEntry->windowYScale;
+ xOffset = dispatchEntry->xOffset * xScale;
+ yOffset = dispatchEntry->yOffset * yScale;
+ if (globalScaleFactor != 1.0f) {
for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
scaledCoords[i] = motionEntry->pointerCoords[i];
- scaledCoords[i].scale(globalScaleFactor, wxs, wys);
+ // Don't apply window scale here since we don't want scale to affect raw
+ // coordinates. The scale will be sent back to the client and applied
+ // later when requesting relative coordinates.
+ scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */,
+ 1 /* windowYScale */);
}
usingCoords = scaledCoords;
}
} else {
- xOffset = 0.0f;
- yOffset = 0.0f;
-
// We don't want the dispatch target to know.
if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
@@ -2462,10 +2440,8 @@
dispatchEntry->resolvedFlags,
motionEntry->edgeFlags, motionEntry->metaState,
motionEntry->buttonState,
- motionEntry->classification,
- dispatchEntry->windowXScale,
- dispatchEntry->windowYScale, xOffset, yOffset,
- motionEntry->xPrecision,
+ motionEntry->classification, xScale, yScale,
+ xOffset, yOffset, motionEntry->xPrecision,
motionEntry->yPrecision,
motionEntry->xCursorPosition,
motionEntry->yCursorPosition,
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index a4ba0de..93de18d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -25,6 +25,7 @@
#include "InputDispatcherPolicyInterface.h"
#include "InputState.h"
#include "InputTarget.h"
+#include "InputThread.h"
#include "Monitor.h"
#include "TouchState.h"
#include "TouchedWindow.h"
@@ -124,8 +125,7 @@
STALE,
};
- class InputDispatcherThread;
- sp<InputDispatcherThread> mThread;
+ std::unique_ptr<InputThread> mThread;
sp<InputDispatcherPolicyInterface> mPolicy;
android::InputDispatcherConfiguration mConfig;
diff --git a/services/inputflinger/include/InputThread.h b/services/inputflinger/include/InputThread.h
new file mode 100644
index 0000000..407365a
--- /dev/null
+++ b/services/inputflinger/include/InputThread.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_THREAD_H
+#define _UI_INPUT_THREAD_H
+
+#include <utils/Thread.h>
+
+namespace android {
+
+/* A thread that loops continuously until destructed to process input events.
+ *
+ * Creating the InputThread starts it immediately. The thread begins looping the loop
+ * function until the InputThread is destroyed. The wake function is used to wake anything
+ * that sleeps in the loop when it is time for the thread to be destroyed.
+ */
+class InputThread {
+public:
+ explicit InputThread(std::string name, std::function<void()> loop,
+ std::function<void()> wake = nullptr);
+ virtual ~InputThread();
+
+ bool isCallingThread();
+
+private:
+ std::string mName;
+ std::function<void()> mThreadWake;
+ sp<Thread> mThread;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_THREAD_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 05f0db1..2023c6e 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -49,25 +49,6 @@
namespace android {
-// --- InputReader::InputReaderThread ---
-
-/* Thread that reads raw events from the event hub and processes them, endlessly. */
-class InputReader::InputReaderThread : public Thread {
-public:
- explicit InputReaderThread(InputReader* reader)
- : Thread(/* canCallJava */ true), mReader(reader) {}
-
- ~InputReaderThread() {}
-
-private:
- InputReader* mReader;
-
- bool threadLoop() override {
- mReader->loopOnce();
- return true;
- }
-};
-
// --- InputReader ---
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
@@ -83,7 +64,6 @@
mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
- mThread = new InputReaderThread(this);
{ // acquire lock
AutoMutex _l(mLock);
@@ -100,25 +80,21 @@
}
status_t InputReader::start() {
- if (mThread->isRunning()) {
+ if (mThread) {
return ALREADY_EXISTS;
}
- return mThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+ mThread = std::make_unique<InputThread>(
+ "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
+ return OK;
}
status_t InputReader::stop() {
- if (!mThread->isRunning()) {
- return OK;
- }
- if (gettid() == mThread->getTid()) {
- ALOGE("InputReader can only be stopped from outside of the InputReaderThread!");
+ if (mThread && mThread->isCallingThread()) {
+ ALOGE("InputReader cannot be stopped from its own thread!");
return INVALID_OPERATION;
}
- // Directly calling requestExitAndWait() causes the thread to not exit
- // if mEventHub is waiting for a long timeout.
- mThread->requestExit();
- mEventHub->wake();
- return mThread->requestExitAndWait();
+ mThread.reset();
+ return OK;
}
void InputReader::loopOnce() {
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 5024906..02957cd 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -21,6 +21,7 @@
#include "InputListener.h"
#include "InputReaderBase.h"
#include "InputReaderContext.h"
+#include "InputThread.h"
#include <utils/Condition.h>
#include <utils/Mutex.h>
@@ -116,8 +117,7 @@
friend class ContextImpl;
private:
- class InputReaderThread;
- sp<InputReaderThread> mThread;
+ std::unique_ptr<InputThread> mThread;
Mutex mLock;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index e85281d..46bbbe1 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -130,8 +130,10 @@
}
std::optional<float> BufferQueueLayer::getFrameRate() const {
- if (mLatchedFrameRate > 0.f || mLatchedFrameRate == FRAME_RATE_NO_VOTE)
- return mLatchedFrameRate;
+ const auto frameRate = mLatchedFrameRate.load();
+ if (frameRate > 0.f || frameRate == FRAME_RATE_NO_VOTE) {
+ return frameRate;
+ }
return {};
}
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index cd4227c..c86c538 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -239,7 +239,7 @@
mReleasePreviousBuffer = true;
}
- mFrameCounter++;
+ mCurrentState.frameNumber++;
mCurrentState.buffer = buffer;
mCurrentState.clientCacheId = clientCacheId;
@@ -247,10 +247,11 @@
setTransactionFlags(eTransactionNeeded);
const int32_t layerId = getSequence();
- mFlinger->mTimeStats->setPostTime(layerId, mFrameNumber, getName().c_str(), postTime);
+ mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(),
+ postTime);
mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
- mFlinger->mFrameTracer->traceTimestamp(layerId, buffer->getId(), mFrameNumber, postTime,
- FrameTracer::FrameEvent::POST);
+ mFlinger->mFrameTracer->traceTimestamp(layerId, buffer->getId(), mCurrentState.frameNumber,
+ postTime, FrameTracer::FrameEvent::POST);
mCurrentState.desiredPresentTime = desiredPresentTime;
mFlinger->mScheduler->recordLayerHistory(this,
@@ -414,7 +415,7 @@
}
uint64_t BufferStateLayer::getFrameNumber(nsecs_t /*expectedPresentTime*/) const {
- return mFrameNumber;
+ return mDrawingState.frameNumber;
}
bool BufferStateLayer::getAutoRefresh() const {
@@ -491,7 +492,7 @@
ALOGE("[%s] rejecting buffer: "
"bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
getDebugName(), bufferWidth, bufferHeight, s.active.w, s.active.h);
- mFlinger->mTimeStats->removeTimeRecord(layerId, mFrameNumber);
+ mFlinger->mTimeStats->removeTimeRecord(layerId, mDrawingState.frameNumber);
return BAD_VALUE;
}
@@ -499,8 +500,6 @@
handle->latchTime = latchTime;
}
- mFrameNumber = mFrameCounter;
-
if (!SyncFeatures::getInstance().useNativeFenceSync()) {
// Bind the new buffer to the GL texture.
//
@@ -517,11 +516,13 @@
}
const uint64_t bufferID = getCurrentBufferId();
- mFlinger->mTimeStats->setAcquireFence(layerId, mFrameNumber, mBufferInfo.mFenceTime);
- mFlinger->mFrameTracer->traceFence(layerId, bufferID, mFrameNumber, mBufferInfo.mFenceTime,
+ mFlinger->mTimeStats->setAcquireFence(layerId, mDrawingState.frameNumber,
+ mBufferInfo.mFenceTime);
+ mFlinger->mFrameTracer->traceFence(layerId, bufferID, mDrawingState.frameNumber,
+ mBufferInfo.mFenceTime,
FrameTracer::FrameEvent::ACQUIRE_FENCE);
- mFlinger->mTimeStats->setLatchTime(layerId, mFrameNumber, latchTime);
- mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, mFrameNumber, latchTime,
+ mFlinger->mTimeStats->setLatchTime(layerId, mDrawingState.frameNumber, latchTime);
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, mDrawingState.frameNumber, latchTime,
FrameTracer::FrameEvent::LATCH);
mCurrentStateModified = false;
@@ -548,7 +549,7 @@
status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) {
// TODO(marissaw): support frame history events
mPreviousFrameNumber = mCurrentFrameNumber;
- mCurrentFrameNumber = mFrameNumber;
+ mCurrentFrameNumber = mDrawingState.frameNumber;
return NO_ERROR;
}
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index 04854d0..dbdfd5b 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -61,7 +61,7 @@
}
bool ColorLayer::isVisible() const {
- return !isHiddenByPolicy() && getAlpha() > 0.0f;
+ return !isHiddenByPolicy() && getAlpha() > 0.0_hf;
}
bool ColorLayer::setColor(const half3& color) {
@@ -104,7 +104,9 @@
}
bool ColorLayer::isOpaque(const Layer::State& s) const {
- return (s.flags & layer_state_t::eLayerOpaque) != 0;
+ // Consider the layer to be opaque if its opaque flag is set or its effective
+ // alpha (considering the alpha of its parents as well) is 1.0;
+ return (s.flags & layer_state_t::eLayerOpaque) != 0 || getAlpha() == 1.0_hf;
}
ui::Dataspace ColorLayer::getDataSpace() const {
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 55371df..f12d1c7 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -903,8 +903,7 @@
// GPU composition can finish in time. We must reset GPU frequency afterwards,
// because high frequency consumes extra battery.
const bool expensiveRenderingExpected =
- clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3 ||
- mLayerRequestingBackgroundBlur != nullptr;
+ clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3;
if (expensiveRenderingExpected) {
setExpensiveRenderingExpected(true);
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 6f8df95..cedab59 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -101,6 +101,7 @@
mCurrentState.active.w = UINT32_MAX;
mCurrentState.active.h = UINT32_MAX;
mCurrentState.active.transform.set(0, 0);
+ mCurrentState.frameNumber = 0;
mCurrentState.transform = 0;
mCurrentState.transformToDisplayInverse = false;
mCurrentState.crop.makeInvalid();
@@ -1803,6 +1804,15 @@
}
}
+void Layer::traverse(LayerVector::StateSet state, const LayerVector::Visitor& visitor) {
+ visitor(this);
+ const LayerVector& children =
+ state == LayerVector::StateSet::Drawing ? mDrawingChildren : mCurrentChildren;
+ for (const sp<Layer>& child : children) {
+ child->traverse(state, visitor);
+ }
+}
+
LayerVector Layer::makeChildrenTraversalList(LayerVector::StateSet stateSet,
const std::vector<Layer*>& layersInTree) {
LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid,
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c75a570..da3df8f 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -189,6 +189,7 @@
ui::Dataspace dataspace;
// The fields below this point are only used by BufferStateLayer
+ uint64_t frameNumber;
Geometry active;
uint32_t transform;
@@ -678,6 +679,15 @@
renderengine::ShadowSettings getShadowSettings(const Rect& viewport) const;
+ /**
+ * Traverse this layer and it's hierarchy of children directly. Unlike traverseInZOrder
+ * which will not emit children who have relativeZOrder to another layer, this method
+ * just directly emits all children. It also emits them in no particular order.
+ * So this method is not suitable for graphical operations, as it doesn't represent
+ * the scene state, but it's also more efficient than traverseInZOrder and so useful for
+ * book-keeping.
+ */
+ void traverse(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
void traverseInReverseZOrder(LayerVector::StateSet stateSet,
const LayerVector::Visitor& visitor);
void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
index 7c959b9..9b94920 100644
--- a/services/surfaceflinger/LayerVector.cpp
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -86,6 +86,14 @@
layer->traverseInReverseZOrder(stateSet, visitor);
}
}
+
+void LayerVector::traverse(const Visitor& visitor) const {
+ for (auto i = static_cast<int64_t>(size()) - 1; i >= 0; i--) {
+ const auto& layer = (*this)[i];
+ layer->traverse(mStateSet, visitor);
+ }
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/LayerVector.h b/services/surfaceflinger/LayerVector.h
index 88d7711..a531f4f 100644
--- a/services/surfaceflinger/LayerVector.h
+++ b/services/surfaceflinger/LayerVector.h
@@ -50,7 +50,7 @@
using Visitor = std::function<void(Layer*)>;
void traverseInReverseZOrder(StateSet stateSet, const Visitor& visitor) const;
void traverseInZOrder(StateSet stateSet, const Visitor& visitor) const;
-
+ void traverse(const Visitor& visitor) const;
private:
const StateSet mStateSet;
};
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
index d94d758..f309d4d 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -57,7 +57,7 @@
bool LayerInfoV2::isFrequent(nsecs_t now) const {
// Assume layer is infrequent if too few present times have been recorded.
if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
- return true;
+ return false;
}
// Layer is frequent if the earliest value in the window of most recent present times is
@@ -100,8 +100,7 @@
static_cast<float>(totalPresentTimeDeltas) / (mFrameTimes.size() - 1);
// Now once we calculated the refresh rate we need to make sure that all the frames we captured
- // are evenly distrubuted and we don't calculate the average across some burst of frames.
-
+ // are evenly distributed and we don't calculate the average across some burst of frames.
for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
const nsecs_t presentTimeDeltas =
std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index c187049..e4b0287 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -125,12 +125,13 @@
}
for (const auto& layer : layers) {
+ ALOGV("Calculating score for %s (type: %d)", layer.name.c_str(), layer.vote);
if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min ||
layer.vote == LayerVoteType::Max) {
continue;
}
- // If we have Explicit layers, ignore the Huristic ones
+ // If we have Explicit layers, ignore the Hueristic ones
if (explicitVoteLayers > 0 && layer.vote == LayerVoteType::Heuristic) {
continue;
}
@@ -148,24 +149,26 @@
}
float layerScore;
+ static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
if (displayFramesRem == 0) {
// Layer desired refresh rate matches the display rate.
layerScore = layer.weight * 1.0f;
} else if (displayFramesQuot == 0) {
// Layer desired refresh rate is higher the display rate.
- layerScore = layer.weight * layerPeriod / displayPeriod;
+ layerScore = layer.weight *
+ (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
+ (1.0f / (MAX_FRAMES_TO_FIT + 1));
} else {
// Layer desired refresh rate is lower the display rate. Check how well it fits the
// cadence
auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem));
int iter = 2;
- static constexpr size_t MAX_ITERATOR = 10; // Stop calculating when score < 0.1
- while (diff > MARGIN && iter < MAX_ITERATOR) {
+ while (diff > MARGIN && iter < MAX_FRAMES_TO_FIT) {
diff = diff - (displayPeriod - diff);
iter++;
}
- layerScore = layer.weight * 1.0f / iter;
+ layerScore = layer.weight * (1.0f / iter);
}
ALOGV("%s (weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(),
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index ac32633..e688415 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -71,12 +71,12 @@
return std::get<0>(mRateMap.find(mIdealPeriod)->second);
}
-void VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
+bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
std::lock_guard<std::mutex> lk(mMutex);
if (!validate(timestamp)) {
ALOGV("timestamp was too far off the last known timestamp");
- return;
+ return false;
}
if (timestamps.size() != kHistorySize) {
@@ -89,7 +89,7 @@
if (timestamps.size() < kMinimumSamplesForPrediction) {
mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
- return;
+ return true;
}
// This is a 'simple linear regression' calculation of Y over X, with Y being the
@@ -143,7 +143,7 @@
if (CC_UNLIKELY(bottom == 0)) {
it->second = {mIdealPeriod, 0};
- return;
+ return false;
}
nsecs_t const anticipatedPeriod = top / bottom * kScalingFactor;
@@ -156,6 +156,7 @@
ALOGV("model update ts: %" PRId64 " slope: %" PRId64 " intercept: %" PRId64, timestamp,
anticipatedPeriod, intercept);
+ return true;
}
nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index e366555..532fe9e 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -38,7 +38,7 @@
uint32_t outlierTolerancePercent);
~VSyncPredictor();
- void addVsyncTimestamp(nsecs_t timestamp) final;
+ bool addVsyncTimestamp(nsecs_t timestamp) final;
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
nsecs_t currentPeriod() const final;
void resetModel() final;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 53fa212..70e4760 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -130,10 +130,11 @@
}
std::lock_guard<std::mutex> lk(mMutex);
- if (mIgnorePresentFences) {
+ if (mExternalIgnoreFences || mInternalIgnoreFences) {
return true;
}
+ bool timestampAccepted = true;
for (auto it = mUnfiredFences.begin(); it != mUnfiredFences.end();) {
auto const time = (*it)->getCachedSignalTime();
if (time == Fence::SIGNAL_TIME_PENDING) {
@@ -141,7 +142,8 @@
} else if (time == Fence::SIGNAL_TIME_INVALID) {
it = mUnfiredFences.erase(it);
} else {
- mTracker->addVsyncTimestamp(time);
+ timestampAccepted &= mTracker->addVsyncTimestamp(time);
+
it = mUnfiredFences.erase(it);
}
}
@@ -152,7 +154,13 @@
}
mUnfiredFences.push_back(fence);
} else {
- mTracker->addVsyncTimestamp(signalTime);
+ timestampAccepted &= mTracker->addVsyncTimestamp(signalTime);
+ }
+
+ if (!timestampAccepted) {
+ mMoreSamplesNeeded = true;
+ setIgnorePresentFencesInternal(true);
+ mPeriodConfirmationInProgress = true;
}
return mMoreSamplesNeeded;
@@ -160,8 +168,17 @@
void VSyncReactor::setIgnorePresentFences(bool ignoration) {
std::lock_guard<std::mutex> lk(mMutex);
- mIgnorePresentFences = ignoration;
- if (mIgnorePresentFences == true) {
+ mExternalIgnoreFences = ignoration;
+ updateIgnorePresentFencesInternal();
+}
+
+void VSyncReactor::setIgnorePresentFencesInternal(bool ignoration) {
+ mInternalIgnoreFences = ignoration;
+ updateIgnorePresentFencesInternal();
+}
+
+void VSyncReactor::updateIgnorePresentFencesInternal() {
+ if (mExternalIgnoreFences || mInternalIgnoreFences) {
mUnfiredFences.clear();
}
}
@@ -177,14 +194,18 @@
}
void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) {
+ mPeriodConfirmationInProgress = true;
mPeriodTransitioningTo = newPeriod;
mMoreSamplesNeeded = true;
+ setIgnorePresentFencesInternal(true);
}
void VSyncReactor::endPeriodTransition() {
- mPeriodTransitioningTo.reset();
- mLastHwVsync.reset();
+ setIgnorePresentFencesInternal(false);
mMoreSamplesNeeded = false;
+ mPeriodTransitioningTo.reset();
+ mPeriodConfirmationInProgress = false;
+ mLastHwVsync.reset();
}
void VSyncReactor::setPeriod(nsecs_t period) {
@@ -208,27 +229,33 @@
void VSyncReactor::endResync() {}
-bool VSyncReactor::periodChangeDetected(nsecs_t vsync_timestamp) {
- if (!mLastHwVsync || !mPeriodTransitioningTo) {
+bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp) {
+ if (!mLastHwVsync || !mPeriodConfirmationInProgress) {
return false;
}
+ auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod();
+
+ static constexpr int allowancePercent = 10;
+ static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
+ auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den;
auto const distance = vsync_timestamp - *mLastHwVsync;
- return std::abs(distance - *mPeriodTransitioningTo) < std::abs(distance - getPeriod());
+ return std::abs(distance - period) < allowance;
}
bool VSyncReactor::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
assert(periodFlushed);
std::lock_guard<std::mutex> lk(mMutex);
- if (periodChangeDetected(timestamp)) {
- mTracker->setPeriod(*mPeriodTransitioningTo);
- for (auto& entry : mCallbacks) {
- entry.second->setPeriod(*mPeriodTransitioningTo);
+ if (periodConfirmed(timestamp)) {
+ if (mPeriodTransitioningTo) {
+ mTracker->setPeriod(*mPeriodTransitioningTo);
+ for (auto& entry : mCallbacks) {
+ entry.second->setPeriod(*mPeriodTransitioningTo);
+ }
+ *periodFlushed = true;
}
-
endPeriodTransition();
- *periodFlushed = true;
- } else if (mPeriodTransitioningTo) {
+ } else if (mPeriodConfirmationInProgress) {
mLastHwVsync = timestamp;
mMoreSamplesNeeded = true;
*periodFlushed = false;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index f318dcb..5b79f35 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -61,9 +61,11 @@
void reset() final;
private:
+ void setIgnorePresentFencesInternal(bool ignoration) REQUIRES(mMutex);
+ void updateIgnorePresentFencesInternal() REQUIRES(mMutex);
void startPeriodTransition(nsecs_t newPeriod) REQUIRES(mMutex);
void endPeriodTransition() REQUIRES(mMutex);
- bool periodChangeDetected(nsecs_t vsync_timestamp) REQUIRES(mMutex);
+ bool periodConfirmed(nsecs_t vsync_timestamp) REQUIRES(mMutex);
std::unique_ptr<Clock> const mClock;
std::unique_ptr<VSyncTracker> const mTracker;
@@ -71,10 +73,12 @@
size_t const mPendingLimit;
std::mutex mMutex;
- bool mIgnorePresentFences GUARDED_BY(mMutex) = false;
+ bool mInternalIgnoreFences GUARDED_BY(mMutex) = false;
+ bool mExternalIgnoreFences GUARDED_BY(mMutex) = false;
std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false;
+ bool mPeriodConfirmationInProgress GUARDED_BY(mMutex) = false;
std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 2b27884..a25b8a9 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -33,8 +33,10 @@
* to the model.
*
* \param [in] timestamp The timestamp when the vsync signal was.
+ * \return True if the timestamp was consistent with the internal model,
+ * False otherwise
*/
- virtual void addVsyncTimestamp(nsecs_t timestamp) = 0;
+ virtual bool addVsyncTimestamp(nsecs_t timestamp) = 0;
/*
* Access the next anticipated vsync time such that the anticipated time >= timePoint.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 62d47e1..61d197c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2059,7 +2059,7 @@
compositorTiming = getBE().mCompositorTiming;
}
- mDrawingState.traverseInZOrder([&](Layer* layer) {
+ mDrawingState.traverse([&](Layer* layer) {
bool frameLatched = layer->onPostComposition(displayDevice, glCompositionDoneFenceTime,
presentFenceTime, compositorTiming);
if (frameLatched) {
@@ -2496,7 +2496,7 @@
const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
// Notify all layers of available frames
- mCurrentState.traverseInZOrder([expectedPresentTime](Layer* layer) {
+ mCurrentState.traverse([expectedPresentTime](Layer* layer) {
layer->notifyAvailableFrames(expectedPresentTime);
});
@@ -2506,7 +2506,7 @@
*/
if ((transactionFlags & eTraversalNeeded) || mTraversalNeededMainThread) {
- mCurrentState.traverseInZOrder([&](Layer* layer) {
+ mCurrentState.traverse([&](Layer* layer) {
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
if (!trFlags) return;
@@ -2553,7 +2553,7 @@
sp<const DisplayDevice> hintDisplay;
uint32_t currentlayerStack = 0;
bool first = true;
- mCurrentState.traverseInZOrder([&](Layer* layer) {
+ mCurrentState.traverse([&](Layer* layer) {
// NOTE: we rely on the fact that layers are sorted by
// layerStack first (so we don't have to traverse the list
// of displays for every layer).
@@ -2781,7 +2781,7 @@
// clear the "changed" flags in current state
mCurrentState.colorMatrixChanged = false;
- mDrawingState.traverseInZOrder([&](Layer* layer) {
+ mDrawingState.traverse([&](Layer* layer) {
layer->commitChildList();
// If the layer can be reached when traversing mDrawingState, then the layer is no
@@ -2792,7 +2792,7 @@
});
commitOffscreenLayers();
- mDrawingState.traverseInZOrder([&](Layer* layer) { layer->updateMirrorInfo(); });
+ mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); });
}
void SurfaceFlinger::withTracingLock(std::function<void()> lockedOperation) {
@@ -2817,7 +2817,7 @@
void SurfaceFlinger::commitOffscreenLayers() {
for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing, [](Layer* layer) {
+ offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) {
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
if (!trFlags) return;
@@ -2858,7 +2858,7 @@
// 3.) Layer 1 is latched.
// Display is now waiting on Layer 1's frame, which is behind layer 0's
// second frame. But layer 0's second frame could be waiting on display.
- mDrawingState.traverseInZOrder([&](Layer* layer) {
+ mDrawingState.traverse([&](Layer* layer) {
if (layer->hasReadyFrame()) {
frameQueued = true;
if (layer->shouldPresentNow(expectedPresentTime)) {
@@ -2876,7 +2876,7 @@
// be shown on screen. Therefore, we need to latch and release buffers of offscreen
// layers to ensure dequeueBuffer doesn't block indefinitely.
for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing,
+ offscreenLayer->traverse(LayerVector::StateSet::Drawing,
[&](Layer* l) { l->latchAndReleaseBuffer(); });
}
@@ -2911,7 +2911,7 @@
mBootStage = BootStage::BOOTANIMATION;
}
- mDrawingState.traverseInZOrder([&](Layer* layer) { layer->updateCloneBufferInfo(); });
+ mDrawingState.traverse([&](Layer* layer) { layer->updateCloneBufferInfo(); });
// Only continue with the refresh if there is actually new work to do
return !mLayersWithQueuedFrames.empty() && newDataLatched;
@@ -3733,7 +3733,7 @@
bool matchFound = true;
while (matchFound) {
matchFound = false;
- mCurrentState.traverseInZOrder([&](Layer* layer) {
+ mCurrentState.traverse([&](Layer* layer) {
if (layer->getName() == uniqueName) {
matchFound = true;
uniqueName = base::StringPrintf("%s#%u", name, ++dupeCounter);
@@ -4104,7 +4104,7 @@
const bool clearAll = args.size() < 2;
const auto name = clearAll ? String8() : String8(args[1]);
- mCurrentState.traverseInZOrder([&](Layer* layer) {
+ mCurrentState.traverse([&](Layer* layer) {
if (clearAll || layer->getName() == name.string()) {
layer->clearFrameStats();
}
@@ -4120,7 +4120,7 @@
// This should only be called from the main thread. Otherwise it would need
// the lock and should use mCurrentState rather than mDrawingState.
void SurfaceFlinger::logFrameStats() {
- mDrawingState.traverseInZOrder([&](Layer* layer) {
+ mDrawingState.traverse([&](Layer* layer) {
layer->logFrameStats();
});
@@ -4355,7 +4355,7 @@
result.append("Offscreen Layers:\n");
postMessageSync(new LambdaMessage([&]() {
for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
+ offscreenLayer->traverse(LayerVector::StateSet::Drawing, [&](Layer* layer) {
layer->dumpCallingUidPid(result);
});
}
@@ -5588,6 +5588,10 @@
// ---------------------------------------------------------------------------
+void SurfaceFlinger::State::traverse(const LayerVector::Visitor& visitor) const {
+ layersSortedByZ.traverse(visitor);
+}
+
void SurfaceFlinger::State::traverseInZOrder(const LayerVector::Visitor& visitor) const {
layersSortedByZ.traverseInZOrder(stateSet, visitor);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f8980a5..4c8775d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -384,6 +384,7 @@
renderengine::ShadowSettings globalShadowSettings;
+ void traverse(const LayerVector::Visitor& visitor) const;
void traverseInZOrder(const LayerVector::Visitor& visitor) const;
void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
};
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index cab33ae..eed975e 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -1,7 +1,13 @@
{
"presubmit": [
{
- "name": "libsurfaceflinger_unittest"
+ "name": "libsurfaceflinger_unittest",
+ // TODO(b/148517641): re-enable once this test is fixed
+ "options": [
+ {
+ "exclude-filter": "FrameTracerTest.*"
+ }
+ ]
},
{
"name": "libcompositionengine_test"
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 12c98da..130e99a 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -40,13 +40,18 @@
status_pull_atom_return_t TimeStats::pullAtomCallback(int32_t atom_tag,
pulled_stats_event_list* data, void* cookie) {
impl::TimeStats* timeStats = reinterpret_cast<impl::TimeStats*>(cookie);
+ status_pull_atom_return_t result = STATS_PULL_SKIP;
if (atom_tag == android::util::SURFACEFLINGER_STATS_GLOBAL_INFO) {
- return timeStats->populateGlobalAtom(data);
+ result = timeStats->populateGlobalAtom(data);
} else if (atom_tag == android::util::SURFACEFLINGER_STATS_LAYER_INFO) {
- return timeStats->populateLayerAtom(data);
+ result = timeStats->populateLayerAtom(data);
}
- return STATS_PULL_SKIP;
+ // Enable timestats now. The first full pull for a given build is expected to
+ // have empty or very little stats, as stats are first enabled after the
+ // first pull is completed for either the global or layer stats.
+ timeStats->enable();
+ return result;
}
status_pull_atom_return_t TimeStats::populateGlobalAtom(pulled_stats_event_list* data) {
@@ -167,13 +172,19 @@
}
}
+TimeStats::~TimeStats() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mStatsDelegate->unregisterStatsPullAtomCallback(
+ android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+ mStatsDelegate->unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
+}
+
void TimeStats::onBootFinished() {
- // Temporarily enable TimeStats by default. Telemetry is disabled while
- // we move onto statsd, so TimeStats is currently not exercised at all
- // during testing without enabling by default.
- // TODO: remove this, as we should only be paying this overhead on devices
- // where statsd exists.
- enable();
+ std::lock_guard<std::mutex> lock(mMutex);
+ mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ TimeStats::pullAtomCallback, nullptr, this);
+ mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ TimeStats::pullAtomCallback, nullptr, this);
}
void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
@@ -735,10 +746,6 @@
mEnabled.store(true);
mTimeStats.statsStart = static_cast<int64_t>(std::time(0));
mPowerTime.prevTime = systemTime();
- mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- TimeStats::pullAtomCallback, nullptr, this);
- mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- TimeStats::pullAtomCallback, nullptr, this);
ALOGD("Enabled");
}
@@ -751,9 +758,6 @@
flushPowerTimeLocked();
mEnabled.store(false);
mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
- mStatsDelegate->unregisterStatsPullAtomCallback(
- android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
- mStatsDelegate->unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
ALOGD("Disabled");
}
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 71f06af..67b9d10 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -182,6 +182,8 @@
std::optional<size_t> maxPulledLayers,
std::optional<size_t> maxPulledHistogramBuckets);
+ ~TimeStats() override;
+
void onBootFinished() override;
void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
bool isEnabled() override;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index d046f76..7681283 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -32,6 +32,7 @@
// option to false temporarily.
address: true,
},
+ data: ["libsurfaceflinger_unittest.filter"],
srcs: [
":libsurfaceflinger_sources",
"libsurfaceflinger_unittest_main.cpp",
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index 11ace05..922966a 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -34,6 +34,7 @@
class LayerHistoryTestV2 : public testing::Test {
protected:
+ static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfoV2::FREQUENT_LAYER_WINDOW_SIZE;
static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE;
static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS;
@@ -105,7 +106,10 @@
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
history().record(layer.get(), 0, mTime);
ASSERT_EQ(1, history().summarize(mTime).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(mTime)[0].vote);
+ const auto expectedType = (i + 1 < FREQUENT_LAYER_WINDOW_SIZE)
+ ? LayerHistory::LayerVoteType::Min
+ : LayerHistory::LayerVoteType::Max;
+ EXPECT_EQ(expectedType, history().summarize(mTime)[0].vote);
EXPECT_EQ(1, activeLayerCount());
}
@@ -129,7 +133,8 @@
history().record(layer.get(), 0, mTime);
auto summary = history().summarize(mTime);
ASSERT_EQ(1, history().summarize(mTime).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(mTime)[0].vote);
+ // Layer is still considered inactive so we expect to get Min
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(mTime)[0].vote);
EXPECT_EQ(1, activeLayerCount());
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 78009b8..19a58dc 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -495,7 +495,7 @@
EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
lr.desiredRefreshRate = 45.0f;
- EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+ EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
@@ -702,6 +702,32 @@
ASSERT_FALSE(expectedDefaultConfig.inPolicy(50.0f, 59.998f));
}
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_75HzContent) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ lr.vote = LayerVoteType::Explicit;
+ for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
+ lr.desiredRefreshRate = fps;
+ const auto& refreshRate = refreshRateConfigs->getRefreshRateForContentV2(layers);
+ printf("%.2fHz chooses %s\n", fps, refreshRate.name.c_str());
+ EXPECT_EQ(expected90Config, refreshRate);
+ }
+}
+
} // namespace
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 68e6697..f65af77 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -258,22 +258,25 @@
ASSERT_FALSE(mTimeStats->isEnabled());
}
-TEST_F(TimeStatsTest, enabledAfterBoot) {
+TEST_F(TimeStatsTest, registersCallbacksAfterBoot) {
mTimeStats->onBootFinished();
- ASSERT_TRUE(mTimeStats->isEnabled());
+ EXPECT_THAT(mDelegate->mAtomTags,
+ UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+}
+
+TEST_F(TimeStatsTest, unregistersCallbacksOnDestruction) {
+ EXPECT_CALL(*mDelegate,
+ unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+ EXPECT_CALL(*mDelegate,
+ unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ mTimeStats.reset();
}
TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
ASSERT_TRUE(mTimeStats->isEnabled());
- EXPECT_THAT(mDelegate->mAtomTags,
- UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- android::util::SURFACEFLINGER_STATS_LAYER_INFO));
- EXPECT_CALL(*mDelegate,
- unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
- EXPECT_CALL(*mDelegate,
- unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
EXPECT_TRUE(inputCommand(InputCommand::DISABLE, FMT_STRING).empty());
ASSERT_FALSE(mTimeStats->isEnabled());
}
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index acf852d..caac61d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -37,7 +37,7 @@
public:
FixedRateIdealStubTracker() : mPeriod{toNs(3ms)} {}
- void addVsyncTimestamp(nsecs_t) final {}
+ bool addVsyncTimestamp(nsecs_t) final { return true; }
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
auto const floor = timePoint % mPeriod;
@@ -60,7 +60,7 @@
public:
VRRStubTracker(nsecs_t period) : mPeriod{period} {}
- void addVsyncTimestamp(nsecs_t) final {}
+ bool addVsyncTimestamp(nsecs_t) final { return true; }
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
std::lock_guard<decltype(mMutex)> lk(mMutex);
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 70c9225..3ab38e4 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -39,9 +39,10 @@
MockVSyncTracker(nsecs_t period) : mPeriod{period} {
ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_))
.WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime));
+ ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true));
}
- MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
+ MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index ce1fafe..1de72b9 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -35,7 +35,8 @@
class MockVSyncTracker : public VSyncTracker {
public:
- MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
+ MockVSyncTracker() { ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true)); }
+ MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
@@ -46,7 +47,9 @@
public:
VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {}
- void addVsyncTimestamp(nsecs_t timestamp) final { mTracker->addVsyncTimestamp(timestamp); }
+ bool addVsyncTimestamp(nsecs_t timestamp) final {
+ return mTracker->addVsyncTimestamp(timestamp);
+ }
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
}
@@ -239,6 +242,22 @@
EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
}
+TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) {
+ bool periodFlushed = true;
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
+ mReactor.setIgnorePresentFences(true);
+
+ nsecs_t const newPeriod = 5000;
+ mReactor.setPeriod(newPeriod);
+
+ EXPECT_TRUE(mReactor.addResyncSample(0, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ EXPECT_FALSE(mReactor.addResyncSample(newPeriod, &periodFlushed));
+ EXPECT_TRUE(periodFlushed);
+
+ EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
nsecs_t const fakeTimestamp = 4839;
EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
@@ -330,6 +349,35 @@
EXPECT_TRUE(periodFlushed);
}
+TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSync) {
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_))
+ .WillOnce(Return(false))
+ .WillOnce(Return(true))
+ .WillOnce(Return(true));
+ EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+ EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+
+ nsecs_t skewyPeriod = period >> 1;
+ bool periodFlushed = false;
+ nsecs_t sampleTime = 0;
+ EXPECT_TRUE(mReactor.addResyncSample(sampleTime += skewyPeriod, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+ EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSyncPendingFence) {
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_))
+ .Times(2)
+ .WillOnce(Return(false))
+ .WillOnce(Return(true));
+
+ auto fence = generatePendingFence();
+ EXPECT_FALSE(mReactor.addPresentFence(fence));
+ signalFenceWithTime(fence, period >> 1);
+ EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) {
nsecs_t const newPeriod = 5000;
mReactor.setPeriod(newPeriod);
diff --git a/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest.filter b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest.filter
new file mode 100644
index 0000000..8e9c3cf
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest.filter
@@ -0,0 +1,6 @@
+{
+ // TODO(b/148517641): re-enable once this test is fixed
+ "presubmit": {
+ "filter": "*:-FrameTracerTest.*"
+ }
+}
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 90a73e2..a7ec4ae 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -164,6 +164,11 @@
"ro.board.platform",
}};
+// LoadDriver returns:
+// * 0 when succeed, or
+// * -ENOENT when fail to open binary libraries, or
+// * -EINVAL when fail to find HAL_MODULE_INFO_SYM_AS_STR or
+// HWVULKAN_HARDWARE_MODULE_ID in the library.
int LoadDriver(android_namespace_t* library_namespace,
const hwvulkan_module_t** module) {
ATRACE_CALL();
@@ -221,7 +226,13 @@
return -ENOENT;
android::GraphicsEnv::getInstance().setDriverToLoad(
android::GpuStatsInfo::Driver::VULKAN_UPDATED);
- return LoadDriver(ns, module);
+ int result = LoadDriver(ns, module);
+ if (result != 0) {
+ LOG_ALWAYS_FATAL(
+ "couldn't find an updated Vulkan implementation from %s",
+ android::GraphicsEnv::getInstance().getDriverPath().c_str());
+ }
+ return result;
}
bool Hal::Open() {