diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2020-11-03 18:16:50 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2020-11-03 18:16:50 +0000 |
commit | 301900feebf8c73487fcf844d3f3660bcda1c130 (patch) | |
tree | 0fabfae5646a515ec03e6d8d18cd658f25ed2890 | |
parent | 31164e545a67126206bdc4afebd9ca5ef8b68189 (diff) | |
parent | 2eba03bbc500f0116c3f9862a7009b9fa2b6d583 (diff) | |
download | native-301900feebf8c73487fcf844d3f3660bcda1c130.tar.gz |
Snap for 6948038 from 2eba03bbc500f0116c3f9862a7009b9fa2b6d583 to rvc-platform-releaseandroid-platform-11.0.0_r3
Change-Id: Ie887575acf8a1f16a50f00e689e5cfe91b6b1bbb
39 files changed, 693 insertions, 99 deletions
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index 1f9892a262..e80c3210f0 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -213,17 +213,18 @@ Status ServiceManager::addService(const std::string& name, const sp<IBinder>& bi return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } - auto entry = mNameToService.emplace(name, Service { + // Overwrite the old service if it exists + mNameToService[name] = Service { .binder = binder, .allowIsolated = allowIsolated, .dumpPriority = dumpPriority, .debugPid = ctx.debugPid, - }); + }; auto it = mNameToRegistrationCallback.find(name); if (it != mNameToRegistrationCallback.end()) { for (const sp<IServiceCallback>& cb : it->second) { - entry.first->second.guaranteeClient = true; + mNameToService[name].guaranteeClient = true; // permission checked in registerForNotifications cb->onRegistration(name, binder); } diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp index 25245beaf7..fb9f9df856 100644 --- a/cmds/servicemanager/test_sm.cpp +++ b/cmds/servicemanager/test_sm.cpp @@ -135,6 +135,26 @@ TEST(AddService, HappyOverExistingService) { IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); } +TEST(AddService, OverwriteExistingService) { + auto sm = getPermissiveServiceManager(); + sp<IBinder> serviceA = getBinder(); + EXPECT_TRUE(sm->addService("foo", serviceA, false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + + sp<IBinder> outA; + EXPECT_TRUE(sm->getService("foo", &outA).isOk()); + EXPECT_EQ(serviceA, outA); + + // serviceA should be overwritten by serviceB + sp<IBinder> serviceB = getBinder(); + EXPECT_TRUE(sm->addService("foo", serviceB, false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + + sp<IBinder> outB; + EXPECT_TRUE(sm->getService("foo", &outB).isOk()); + EXPECT_EQ(serviceB, outB); +} + TEST(AddService, NoPermissions) { std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>(); diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml index 50f117dd11..6ffb94721d 100644 --- a/data/etc/car_core_hardware.xml +++ b/data/etc/car_core_hardware.xml @@ -38,7 +38,6 @@ <!-- basic system services --> <feature name="android.software.connectionservice" /> <feature name="android.software.voice_recognizers" notLowRam="true" /> - <feature name="android.software.backup" /> <feature name="android.software.home_screen" /> <feature name="android.software.companion_device_setup" /> <feature name="android.software.autofill" /> diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp index 258a4e3748..80aa8916da 100644 --- a/libs/arect/Android.bp +++ b/libs/arect/Android.bp @@ -22,6 +22,8 @@ ndk_headers { cc_library_headers { name: "libarect_headers", + // TODO(b/153609531): remove when no longer needed. + native_bridge_supported: true, export_include_dirs: ["include"], } diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index e0fb54384e..6ca3b16324 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -24,6 +24,7 @@ #include <binder/IShellCallback.h> #include <binder/Parcel.h> +#include <linux/sched.h> #include <stdio.h> namespace android { @@ -133,6 +134,8 @@ public: // unlocked objects bool mRequestingSid = false; sp<IBinder> mExtension; + int mPolicy = SCHED_NORMAL; + int mPriority = 0; // for below objects Mutex mLock; @@ -279,6 +282,47 @@ sp<IBinder> BBinder::getExtension() { return e->mExtension; } +void BBinder::setMinSchedulerPolicy(int policy, int priority) { + switch (policy) { + case SCHED_NORMAL: + LOG_ALWAYS_FATAL_IF(priority < -20 || priority > 19, "Invalid priority for SCHED_NORMAL: %d", priority); + break; + case SCHED_RR: + case SCHED_FIFO: + LOG_ALWAYS_FATAL_IF(priority < 1 || priority > 99, "Invalid priority for sched %d: %d", policy, priority); + break; + default: + LOG_ALWAYS_FATAL("Unrecognized scheduling policy: %d", policy); + } + + Extras* e = mExtras.load(std::memory_order_acquire); + + if (e == nullptr) { + // Avoid allocations if called with default. + if (policy == SCHED_NORMAL && priority == 0) { + return; + } + + e = getOrCreateExtras(); + if (!e) return; // out of memory + } + + e->mPolicy = policy; + e->mPriority = priority; +} + +int BBinder::getMinSchedulerPolicy() { + Extras* e = mExtras.load(std::memory_order_acquire); + if (e == nullptr) return SCHED_NORMAL; + return e->mPolicy; +} + +int BBinder::getMinSchedulerPriority() { + Extras* e = mExtras.load(std::memory_order_acquire); + if (e == nullptr) return 0; + return e->mPriority; +} + pid_t BBinder::getDebugPid() { return getpid(); } diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index d67ce15ca9..847b73ad71 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -860,6 +860,10 @@ status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) err = FAILED_TRANSACTION; goto finish; + case BR_FROZEN_REPLY: + err = FAILED_TRANSACTION; + goto finish; + case BR_ACQUIRE_RESULT: { ALOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT"); @@ -1316,6 +1320,42 @@ void IPCThreadState::threadDestructor(void *st) } } +status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, bool *sync_received, bool *async_received) +{ + int ret = 0; + binder_frozen_status_info info; + info.pid = pid; + +#if defined(__ANDROID__) + if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info) < 0) + ret = -errno; +#endif + *sync_received = info.sync_recv; + *async_received = info.async_recv; + + return ret; +} + +status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) { + struct binder_freeze_info info; + int ret = 0; + + info.pid = pid; + info.enable = enable; + info.timeout_ms = timeout_ms; + + +#if defined(__ANDROID__) + if (ioctl(self()->mProcess->mDriverFD, BINDER_FREEZE, &info) < 0) + ret = -errno; +#endif + + // + // ret==-EAGAIN indicates that transactions have not drained. + // Call again to poll for completion. + // + return ret; +} void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data, size_t /*dataSize*/, diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 9642a87f4e..a5549fc07d 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -20,6 +20,7 @@ #include <errno.h> #include <fcntl.h> #include <inttypes.h> +#include <linux/sched.h> #include <pthread.h> #include <stdint.h> #include <stdio.h> @@ -188,16 +189,18 @@ status_t Parcel::finishUnflattenBinder( return OK; } +static constexpr inline int schedPolicyMask(int policy, int priority) { + return (priority & FLAT_BINDER_FLAG_PRIORITY_MASK) | ((policy & 3) << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT); +} + status_t Parcel::flattenBinder(const sp<IBinder>& binder) { flat_binder_object obj; + obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS; - if (IPCThreadState::self()->backgroundSchedulingDisabled()) { - /* minimum priority for all nodes is nice 0 */ - obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS; - } else { - /* minimum priority for all nodes is MAX_NICE(19) */ - obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS; + int schedBits = 0; + if (!IPCThreadState::self()->backgroundSchedulingDisabled()) { + schedBits = schedPolicyMask(SCHED_NORMAL, 19); } if (binder != nullptr) { @@ -213,6 +216,13 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) obj.handle = handle; obj.cookie = 0; } else { + int policy = local->getMinSchedulerPolicy(); + int priority = local->getMinSchedulerPriority(); + + if (policy != 0 || priority != 0) { + // override value, since it is set explicitly + schedBits = schedPolicyMask(policy, priority); + } if (local->isRequestingSid()) { obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX; } @@ -226,6 +236,8 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) obj.cookie = 0; } + obj.flags |= schedBits; + return finishFlattenBinder(binder, obj); } @@ -2460,7 +2472,7 @@ status_t Parcel::restartWrite(size_t desired) releaseObjects(); - if (data) { + if (data || desired == 0) { LOG_ALLOC("Parcel %p: restart from %zu to %zu capacity", this, mDataCapacity, desired); pthread_mutex_lock(&gParcelGlobalAllocSizeLock); gParcelGlobalAllocSize += desired; diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h index 6afcd77e70..d93935ae5d 100644 --- a/libs/binder/include/binder/AppOpsManager.h +++ b/libs/binder/include/binder/AppOpsManager.h @@ -131,7 +131,11 @@ public: OP_DEPRECATED_1 = 96, OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97, OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98, - _NUM_OP = 99 + OP_NO_ISOLATED_STORAGE = 99, + OP_PHONE_CALL_MICROPHONE = 100, + OP_PHONE_CALL_CAMERA = 101, + OP_RECORD_AUDIO_HOTWORD = 102, + _NUM_OP = 103 }; AppOpsManager(); diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h index 74e52db53f..4cf051563a 100644 --- a/libs/binder/include/binder/Binder.h +++ b/libs/binder/include/binder/Binder.h @@ -72,6 +72,25 @@ public: // This must be called before the object is sent to another process. Not thread safe. void setExtension(const sp<IBinder>& extension); + // This must be called before the object is sent to another process. Not thread safe. + // + // This function will abort if improper parameters are set. This is like + // sched_setscheduler. However, it sets the minimum scheduling policy + // only for the duration that this specific binder object is handling the + // call in a threadpool. By default, this API is set to SCHED_NORMAL/0. In + // this case, the scheduling priority will not actually be modified from + // binder defaults. See also IPCThreadState::disableBackgroundScheduling. + // + // Appropriate values are: + // SCHED_NORMAL: -20 <= priority <= 19 + // SCHED_RR/SCHED_FIFO: 1 <= priority <= 99 + __attribute__((weak)) + void setMinSchedulerPolicy(int policy, int priority); + __attribute__((weak)) + int getMinSchedulerPolicy(); + __attribute__((weak)) + int getMinSchedulerPriority(); + pid_t getDebugPid(); protected: diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index 4818889436..8ac71659ba 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -34,7 +34,26 @@ class IPCThreadState public: static IPCThreadState* self(); static IPCThreadState* selfOrNull(); // self(), but won't instantiate - + + // Freeze or unfreeze the binder interface to a specific process. When freezing, this method + // will block up to timeout_ms to process pending transactions directed to pid. Unfreeze + // is immediate. Transactions to processes frozen via this method won't be delivered and the + // driver will return BR_FROZEN_REPLY to the client sending them. After unfreeze, + // transactions will be delivered normally. + // + // pid: id for the process for which the binder interface is to be frozen + // enable: freeze (true) or unfreeze (false) + // timeout_ms: maximum time this function is allowed to block the caller waiting for pending + // binder transactions to be processed. + // + // returns: 0 in case of success, a value < 0 in case of error + __attribute__((weak)) + static status_t freeze(pid_t pid, bool enabled, uint32_t timeout_ms); + + // Provide information about the state of a frozen process + __attribute__((weak)) + static status_t getProcessFreezeInfo(pid_t pid, bool *sync_received, + bool *async_received); sp<ProcessState> process(); status_t clearLastError(); diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h index c22be9f786..7be8f7b2d9 100644 --- a/libs/binder/include/private/binder/binder_module.h +++ b/libs/binder/include/private/binder/binder_module.h @@ -36,6 +36,60 @@ namespace android { #include <sys/ioctl.h> #include <linux/android/binder.h> +#ifndef BR_FROZEN_REPLY +// Temporary definition of BR_FROZEN_REPLY. For production +// this will come from UAPI binder.h +#define BR_FROZEN_REPLY _IO('r', 18) +#endif //BR_FROZEN_REPLY + +#ifndef BINDER_FREEZE +/* + * Temporary definitions for freeze support. For the final version + * these will be defined in the UAPI binder.h file from upstream kernel. + */ +#define BINDER_FREEZE _IOW('b', 14, struct binder_freeze_info) + +struct binder_freeze_info { + // + // Group-leader PID of process to be frozen + // + uint32_t pid; + // + // Enable(1) / Disable(0) freeze for given PID + // + uint32_t enable; + // + // Timeout to wait for transactions to drain. + // 0: don't wait (ioctl will return EAGAIN if not drained) + // N: number of ms to wait + uint32_t timeout_ms; +}; +#endif //BINDER_FREEZE + +#ifndef BINDER_GET_FROZEN_INFO + +#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info) + +struct binder_frozen_status_info { + // + // Group-leader PID of process to be queried + // + __u32 pid; + // + // Indicates whether the process has received any sync calls since last + // freeze (cleared at freeze/unfreeze) + // + __u32 sync_recv; + // + // Indicates whether the process has received any async calls since last + // freeze (cleared at freeze/unfreeze) + // + __u32 async_recv; +}; +#endif //BINDER_GET_FROZEN_INFO + + + #ifdef __cplusplus } // namespace android #endif diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 40de2db2cb..98f0868bca 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -16,6 +16,7 @@ #include <errno.h> #include <fcntl.h> +#include <fstream> #include <poll.h> #include <pthread.h> #include <stdio.h> @@ -50,6 +51,9 @@ static char *binderservername; static char *binderserversuffix; static char binderserverarg[] = "--binderserver"; +static constexpr int kSchedPolicy = SCHED_RR; +static constexpr int kSchedPriority = 7; + static String16 binderLibTestServiceName = String16("test.binderLib"); enum BinderLibTestTranscationCode { @@ -75,6 +79,9 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, + BINDER_LIB_TEST_GET_SCHEDULING_POLICY, + BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, + BINDER_LIB_TEST_GETPID, BINDER_LIB_TEST_ECHO_VECTOR, BINDER_LIB_TEST_REJECT_BUF, }; @@ -395,6 +402,49 @@ TEST_F(BinderLibTest, NopTransaction) { EXPECT_EQ(NO_ERROR, ret); } +TEST_F(BinderLibTest, Freeze) { + status_t ret; + Parcel data, reply, replypid; + std::ifstream freezer_file("/sys/fs/cgroup/freezer/cgroup.freeze"); + + //Pass test on devices where the freezer is not supported + if (freezer_file.fail()) { + GTEST_SKIP(); + return; + } + + std::string freezer_enabled; + std::getline(freezer_file, freezer_enabled); + + //Pass test on devices where the freezer is disabled + if (freezer_enabled != "1") { + GTEST_SKIP(); + return; + } + + ret = m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid); + int32_t pid = replypid.readInt32(); + EXPECT_EQ(NO_ERROR, ret); + for (int i = 0; i < 10; i++) { + EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, data, &reply, TF_ONE_WAY)); + } + EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, 1, 0)); + EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, 1, 0)); + EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 1, 1000)); + EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply)); + + bool sync_received, async_received; + + EXPECT_EQ(NO_ERROR, IPCThreadState::self()->getProcessFreezeInfo(pid, &sync_received, + &async_received)); + + EXPECT_EQ(sync_received, 1); + EXPECT_EQ(async_received, 0); + + EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 0, 0)); + EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply)); +} + TEST_F(BinderLibTest, SetError) { int32_t testValue[] = { 0, -123, 123 }; for (size_t i = 0; i < ARRAY_SIZE(testValue); i++) { @@ -1015,6 +1065,22 @@ TEST_F(BinderLibTest, WorkSourcePropagatedForAllFollowingBinderCalls) EXPECT_EQ(NO_ERROR, ret2); } +TEST_F(BinderLibTest, SchedPolicySet) { + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != nullptr); + + Parcel data, reply; + status_t ret = server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply); + EXPECT_EQ(NO_ERROR, ret); + + int policy = reply.readInt32(); + int priority = reply.readInt32(); + + EXPECT_EQ(kSchedPolicy, policy & (~SCHED_RESET_ON_FORK)); + EXPECT_EQ(kSchedPriority, priority); +} + + TEST_F(BinderLibTest, VectorSent) { Parcel data, reply; sp<IBinder> server = addServer(); @@ -1158,6 +1224,12 @@ class BinderLibTestService : public BBinder pthread_mutex_unlock(&m_serverWaitMutex); return ret; } + case BINDER_LIB_TEST_GETPID: + reply->writeInt32(getpid()); + return NO_ERROR; + case BINDER_LIB_TEST_NOP_TRANSACTION_WAIT: + usleep(5000); + return NO_ERROR; case BINDER_LIB_TEST_NOP_TRANSACTION: return NO_ERROR; case BINDER_LIB_TEST_DELAYED_CALL_BACK: { @@ -1332,6 +1404,16 @@ class BinderLibTestService : public BBinder reply->writeInt32(IPCThreadState::self()->getCallingWorkSourceUid()); return NO_ERROR; } + case BINDER_LIB_TEST_GET_SCHEDULING_POLICY: { + int policy = 0; + sched_param param; + if (0 != pthread_getschedparam(pthread_self(), &policy, ¶m)) { + return UNKNOWN_ERROR; + } + reply->writeInt32(policy); + reply->writeInt32(param.sched_priority); + return NO_ERROR; + } case BINDER_LIB_TEST_ECHO_VECTOR: { std::vector<uint64_t> vector; auto err = data.readUint64Vector(&vector); @@ -1368,6 +1450,8 @@ int run_server(int index, int readypipefd, bool usePoll) { sp<BinderLibTestService> testService = new BinderLibTestService(index); + testService->setMinSchedulerPolicy(kSchedPolicy, kSchedPriority); + /* * Normally would also contain functionality as well, but we are only * testing the extension mechanism. diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index cf269b33ba..e1a17db3d9 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -732,6 +732,8 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { mSharedBufferHasBeenQueued = false; } + mDequeuedSlots.insert(buf); + return OK; } @@ -760,6 +762,8 @@ int Surface::cancelBuffer(android_native_buffer_t* buffer, mSharedBufferHasBeenQueued = true; } + mDequeuedSlots.erase(i); + return OK; } @@ -895,6 +899,8 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err); } + mDequeuedSlots.erase(i); + if (mEnableFrameTimestamps) { mFrameEventHistory->applyDelta(output.frameTimestamps); // Update timestamps with the local acquire fence. @@ -1660,6 +1666,7 @@ int Surface::attachBuffer(ANativeWindowBuffer* buffer) mRemovedBuffers.push_back(mSlots[attachedSlot].buffer); } mSlots[attachedSlot].buffer = graphicBuffer; + mDequeuedSlots.insert(attachedSlot); return NO_ERROR; } @@ -1926,6 +1933,10 @@ Dataspace Surface::getBuffersDataSpace() { } void Surface::freeAllBuffers() { + if (!mDequeuedSlots.empty()) { + ALOGE("%s: %zu buffers were freed while being dequeued!", + __FUNCTION__, mDequeuedSlots.size()); + } for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { mSlots[i].buffer = nullptr; } @@ -1947,6 +1958,10 @@ status_t Surface::getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots, ALOGW("%s: Discarded slot %d doesn't contain buffer!", __FUNCTION__, i); continue; } + // Don't flush currently dequeued buffers + if (mDequeuedSlots.count(i) > 0) { + continue; + } outBuffers->push_back(mSlots[i].buffer); mSlots[i].buffer = nullptr; } diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 49c83da319..55b4101908 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -30,6 +30,7 @@ #include <utils/RefBase.h> #include <shared_mutex> +#include <unordered_set> namespace android { @@ -543,8 +544,15 @@ protected: int mMaxBufferCount; sp<IProducerListener> mListenerProxy; + + // Get and flush the buffers of given slots, if the buffer in the slot + // is currently dequeued then it won't be flushed and won't be returned + // in outBuffers. status_t getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots, std::vector<sp<GraphicBuffer>>* outBuffers); + + // Buffers that are successfully dequeued/attached and handed to clients + std::unordered_set<int> mDequeuedSlots; }; } // namespace android diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index 92e7e715ea..0285c2f6f0 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -409,6 +409,23 @@ GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisp mImageManager = std::make_unique<ImageManager>(this); mImageManager->initThread(); mDrawingBuffer = createFramebuffer(); + sp<GraphicBuffer> buf = + new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888, 1, + GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE, "placeholder"); + + const status_t err = buf->initCheck(); + if (err != OK) { + ALOGE("Error allocating placeholder buffer: %d", err); + return; + } + mPlaceholderBuffer = buf.get(); + EGLint attributes[] = { + EGL_NONE, + }; + mPlaceholderImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + mPlaceholderBuffer, attributes); + ALOGE_IF(mPlaceholderImage == EGL_NO_IMAGE_KHR, "Failed to create placeholder image: %#x", + eglGetError()); } GLESRenderEngine::~GLESRenderEngine() { @@ -423,6 +440,7 @@ GLESRenderEngine::~GLESRenderEngine() { eglDestroyImageKHR(mEGLDisplay, expired); DEBUG_EGL_IMAGE_TRACKER_DESTROY(); } + eglDestroyImageKHR(mEGLDisplay, mPlaceholderImage); mImageCache.clear(); eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglTerminate(mEGLDisplay); @@ -589,6 +607,9 @@ void GLESRenderEngine::genTextures(size_t count, uint32_t* names) { } void GLESRenderEngine::deleteTextures(size_t count, uint32_t const* names) { + for (int i = 0; i < count; ++i) { + mTextureView.erase(names[i]); + } glDeleteTextures(count, names); } @@ -646,6 +667,7 @@ status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, } bindExternalTextureImage(texName, *cachedImage->second); + mTextureView.insert_or_assign(texName, buffer->getId()); } // Wait for the new buffer to be ready. @@ -887,7 +909,7 @@ void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /*framebuffer*/) { glBindFramebuffer(GL_FRAMEBUFFER, 0); } -bool GLESRenderEngine::cleanupPostRender() { +bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) { ATRACE_CALL(); if (mPriorResourcesCleaned || @@ -896,6 +918,30 @@ bool GLESRenderEngine::cleanupPostRender() { return false; } + // This is a bit of a band-aid fix for FrameCaptureProcessor, as we should + // not need to keep memory around if we don't need to do so. + if (mode == CleanupMode::CLEAN_ALL) { + // TODO: SurfaceFlinger memory utilization may benefit from resetting + // texture bindings as well. Assess if it does and there's no performance regression + // when rebinding the same image data to the same texture, and if so then its mode + // behavior can be tweaked. + if (mPlaceholderImage != EGL_NO_IMAGE_KHR) { + for (auto [textureName, bufferId] : mTextureView) { + if (bufferId && mPlaceholderImage != EGL_NO_IMAGE_KHR) { + glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureName); + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, + static_cast<GLeglImageOES>(mPlaceholderImage)); + mTextureView[textureName] = std::nullopt; + checkErrors(); + } + } + } + { + std::lock_guard<std::mutex> lock(mRenderingMutex); + mImageCache.clear(); + } + } + // Bind the texture to dummy data so that backing image data can be freed. GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing()); glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer); @@ -1616,6 +1662,16 @@ bool GLESRenderEngine::isImageCachedForTesting(uint64_t bufferId) { return cachedImage != mImageCache.end(); } +bool GLESRenderEngine::isTextureNameKnownForTesting(uint32_t texName) { + const auto& entry = mTextureView.find(texName); + return entry != mTextureView.end(); +} + +std::optional<uint64_t> GLESRenderEngine::getBufferIdForTextureNameForTesting(uint32_t texName) { + const auto& entry = mTextureView.find(texName); + return entry != mTextureView.end() ? entry->second : std::nullopt; +} + bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) { std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex); return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(), diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index 42b8537b94..d5254a8a80 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -75,7 +75,7 @@ public: const std::vector<const LayerSettings*>& layers, ANativeWindowBuffer* buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence, base::unique_fd* drawFence) override; - bool cleanupPostRender() override; + bool cleanupPostRender(CleanupMode mode) override; EGLDisplay getEGLDisplay() const { return mEGLDisplay; } // Creates an output image for rendering to @@ -86,6 +86,12 @@ public: // Test-only methods // Returns true iff mImageCache contains an image keyed by bufferId bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex); + // Returns true iff texName was previously generated by RenderEngine and was + // not destroyed. + bool isTextureNameKnownForTesting(uint32_t texName); + // Returns the buffer ID of the content bound to texName, or nullopt if no + // such mapping exists. + std::optional<uint64_t> getBufferIdForTextureNameForTesting(uint32_t texName); // Returns true iff mFramebufferImageCache contains an image keyed by bufferId bool isFramebufferImageCachedForTesting(uint64_t bufferId) EXCLUDES(mFramebufferImageCacheMutex); @@ -224,6 +230,8 @@ private: // Cache of GL images that we'll store per GraphicBuffer ID std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex); + std::unordered_map<uint32_t, std::optional<uint64_t>> mTextureView; + // Mutex guarding rendering operations, so that: // 1. GL operations aren't interleaved, and // 2. Internal state related to rendering that is potentially modified by @@ -237,6 +245,11 @@ private: // ensure that we align on a word. Allocating 16 bytes will provide a // guarantee that we don't clobber memory. uint32_t mPlaceholderDrawBuffer[4]; + // Placeholder buffer and image, similar to mPlaceholderDrawBuffer, but + // instead these are intended for cleaning up texture memory with the + // GL_TEXTURE_EXTERNAL_OES target. + ANativeWindowBuffer* mPlaceholderBuffer = nullptr; + EGLImage mPlaceholderImage = EGL_NO_IMAGE_KHR; sp<Fence> mLastDrawFence; // Store a separate boolean checking if prior resources were cleaned up, as // devices that don't support native sync fences can't rely on a last draw diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index e06e1287c1..74bc44b22f 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -111,14 +111,25 @@ public: // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation. virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0; virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0; + + enum class CleanupMode { + CLEAN_OUTPUT_RESOURCES, + CLEAN_ALL, + }; // Clean-up method that should be called on the main thread after the // drawFence returned by drawLayers fires. This method will free up // resources used by the most recently drawn frame. If the frame is still // being drawn, then this call is silently ignored. // + // If mode is CLEAN_OUTPUT_RESOURCES, then only resources related to the + // output framebuffer are cleaned up, including the sibling texture. + // + // If mode is CLEAN_ALL, then we also cleanup resources related to any input + // buffers. + // // Returns true if resources were cleaned up, and false if we didn't need to // do any work. - virtual bool cleanupPostRender() = 0; + virtual bool cleanupPostRender(CleanupMode mode = CleanupMode::CLEAN_OUTPUT_RESOURCES) = 0; // queries virtual size_t getMaxTextureSize() const = 0; diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index df0f17a6d5..101cbb70fd 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -56,7 +56,7 @@ public: MOCK_CONST_METHOD0(isProtected, bool()); MOCK_CONST_METHOD0(supportsProtectedContent, bool()); MOCK_METHOD1(useProtectedContext, bool(bool)); - MOCK_METHOD0(cleanupPostRender, bool()); + MOCK_METHOD1(cleanupPostRender, bool(CleanupMode mode)); MOCK_METHOD6(drawLayers, status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&, ANativeWindowBuffer*, const bool, base::unique_fd&&, base::unique_fd*)); diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 16a8a0decb..f577eb3dc7 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -81,6 +81,7 @@ struct RenderEngineTest : public ::testing::Test { } for (uint32_t texName : mTexNames) { sRE->deleteTextures(1, &texName); + EXPECT_FALSE(sRE->isTextureNameKnownForTesting(texName)); } } @@ -1424,10 +1425,44 @@ TEST_F(RenderEngineTest, cleanupPostRender_cleansUpOnce) { if (fd >= 0) { sync_wait(fd, -1); } - // Only cleanup the first time. - EXPECT_TRUE(sRE->cleanupPostRender()); - EXPECT_FALSE(sRE->cleanupPostRender()); + EXPECT_TRUE(sRE->cleanupPostRender( + renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES)); + EXPECT_FALSE(sRE->cleanupPostRender( + renderengine::RenderEngine::CleanupMode::CLEAN_OUTPUT_RESOURCES)); +} + +TEST_F(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory) { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + + std::vector<const renderengine::LayerSettings*> layers; + renderengine::LayerSettings layer; + layer.geometry.boundaries = fullscreenRect().toFloatRect(); + BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this); + layer.alpha = 1.0; + layers.push_back(&layer); + + base::unique_fd fence; + sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, base::unique_fd(), &fence); + + const int fd = fence.get(); + if (fd >= 0) { + sync_wait(fd, -1); + } + + uint64_t bufferId = layer.source.buffer.buffer->getId(); + uint32_t texName = layer.source.buffer.textureName; + EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); + EXPECT_EQ(bufferId, sRE->getBufferIdForTextureNameForTesting(texName)); + + EXPECT_TRUE(sRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL)); + + // Now check that our view of memory is good. + EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); + EXPECT_EQ(std::nullopt, sRE->getBufferIdForTextureNameForTesting(bufferId)); + EXPECT_TRUE(sRE->isTextureNameKnownForTesting(texName)); } } // namespace android diff --git a/services/inputflinger/reader/include/TouchVideoDevice.h b/services/inputflinger/reader/include/TouchVideoDevice.h index 5a32443f29..7de9b830b2 100644 --- a/services/inputflinger/reader/include/TouchVideoDevice.h +++ b/services/inputflinger/reader/include/TouchVideoDevice.h @@ -102,7 +102,7 @@ private: * How many buffers to keep for the internal queue. When the internal buffer * exceeds this capacity, oldest frames will be dropped. */ - static constexpr size_t MAX_QUEUE_SIZE = 10; + static constexpr size_t MAX_QUEUE_SIZE = 20; std::vector<TouchVideoFrame> mFrames; /** diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index 8a282e2382..e355594176 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -153,12 +153,18 @@ void SensorDevice::initializeSensorList() { SensorDeviceUtils::defaultResolutionForType(sensor.type); } - double promotedResolution = sensor.resolution; - double promotedMaxRange = sensor.maxRange; - if (fmod(promotedMaxRange, promotedResolution) != 0) { - ALOGW("%s's max range %f is not a multiple of the resolution %f", - sensor.name, sensor.maxRange, sensor.resolution); - SensorDeviceUtils::quantizeValue(&sensor.maxRange, promotedResolution); + // Some sensors don't have a default resolution and will be left at 0. + // Don't crash in this case since CTS will verify that devices don't go to + // production with a resolution of 0. + if (sensor.resolution != 0) { + double promotedResolution = sensor.resolution; + double promotedMaxRange = sensor.maxRange; + if (fmod(promotedMaxRange, promotedResolution) != 0) { + ALOGW("%s's max range %f is not a multiple of the resolution %f", + sensor.name, sensor.maxRange, sensor.resolution); + SensorDeviceUtils::quantizeValue( + &sensor.maxRange, promotedResolution); + } } } diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp index b4b5f98609..d14a3014c8 100644 --- a/services/sensorservice/SensorEventConnection.cpp +++ b/services/sensorservice/SensorEventConnection.cpp @@ -28,6 +28,12 @@ #define UNUSED(x) (void)(x) namespace android { +namespace { + +// Used as the default value for the target SDK until it's obtained via getTargetSdkVersion. +constexpr int kTargetSdkUnknown = 0; + +} // namespace SensorService::SensorEventConnection::SensorEventConnection( const sp<SensorService>& service, uid_t uid, String8 packageName, bool isDataInjectionMode, @@ -35,9 +41,9 @@ SensorService::SensorEventConnection::SensorEventConnection( : mService(service), mUid(uid), mWakeLockRefCount(0), mHasLooperCallbacks(false), mDead(false), mDataInjectionMode(isDataInjectionMode), mEventCache(nullptr), mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0), - mPackageName(packageName), mOpPackageName(opPackageName), mDestroyed(false) { + mPackageName(packageName), mOpPackageName(opPackageName), mTargetSdk(kTargetSdkUnknown), + mDestroyed(false) { mChannel = new BitTube(mService->mSocketBufferSize); - mTargetSdk = SensorService::getTargetSdkVersion(opPackageName); #if DEBUG_CONNECTIONS mEventsReceived = mEventsSentFromCache = mEventsSent = 0; mTotalAcksNeeded = mTotalAcksReceived = 0; @@ -445,6 +451,14 @@ bool SensorService::SensorEventConnection::noteOpIfRequired(const sensors_event_ bool success = true; const auto iter = mHandleToAppOp.find(event.sensor); if (iter != mHandleToAppOp.end()) { + if (mTargetSdk == kTargetSdkUnknown) { + // getTargetSdkVersion returns -1 if it fails so this operation should only be run once + // per connection and then cached. Perform this here as opposed to in the constructor to + // avoid log spam for NDK/VNDK clients that don't use sensors guarded with permissions + // and pass in invalid op package names. + mTargetSdk = SensorService::getTargetSdkVersion(mOpPackageName); + } + // Special handling for step count/detect backwards compatibility: if the app's target SDK // is pre-Q, still permit delivering events to the app even if permission isn't granted // (since this permission was only introduced in Q) diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 60f9cd90c8..3ca34bba1b 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -79,6 +79,8 @@ uint8_t SensorService::sHmacGlobalKey[128] = {}; bool SensorService::sHmacGlobalKeyIsValid = false; std::map<String16, int> SensorService::sPackageTargetVersion; Mutex SensorService::sPackageTargetVersionLock; +String16 SensorService::sSensorInterfaceDescriptorPrefix = + String16("android.frameworks.sensorservice@"); AppOpsManager SensorService::sAppOpsManager; #define SENSOR_SERVICE_DIR "/data/system/sensor_service" @@ -1847,6 +1849,13 @@ bool SensorService::hasPermissionForSensor(const Sensor& sensor) { } int SensorService::getTargetSdkVersion(const String16& opPackageName) { + // Don't query the SDK version for the ISensorManager descriptor as it doesn't have one. This + // descriptor tends to be used for VNDK clients, but can technically be set by anyone so don't + // give it elevated privileges. + if (opPackageName.startsWith(sSensorInterfaceDescriptorPrefix)) { + return -1; + } + Mutex::Autolock packageLock(sPackageTargetVersionLock); int targetSdkVersion = -1; auto entry = sPackageTargetVersion.find(opPackageName); diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index 3bb8421a14..052cbfe290 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -424,6 +424,7 @@ private: static AppOpsManager sAppOpsManager; static std::map<String16, int> sPackageTargetVersion; static Mutex sPackageTargetVersionLock; + static String16 sSensorInterfaceDescriptorPrefix; }; } // namespace android diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp index 80c3b65ae8..ae0a9843f6 100644 --- a/services/stats/StatsHal.cpp +++ b/services/stats/StatsHal.cpp @@ -58,7 +58,7 @@ hardware::Return<void> StatsHal::reportChargeCycles(const ChargeCycles& chargeCy std::vector<int32_t> buckets = chargeCycles.cycleBucket; int initialSize = buckets.size(); for (int i = 0; i < 10 - initialSize; i++) { - buckets.push_back(-1); // Push -1 for buckets that do not exist. + buckets.push_back(0); // Push 0 for buckets that do not exist. } android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1], buckets[2], buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8], diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index 07be7916ee..6e4235e409 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -324,9 +324,6 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t } uint64_t bufferID = mQueueItems[0].mGraphicBuffer->getId(); - mFlinger->mFrameTracer->traceFence(layerId, bufferID, currentFrameNumber, - mQueueItems[0].mFenceTime, - FrameTracer::FrameEvent::ACQUIRE_FENCE); mFlinger->mTimeStats->setLatchTime(layerId, currentFrameNumber, latchTime); mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, currentFrameNumber, latchTime, FrameTracer::FrameEvent::LATCH); @@ -393,8 +390,12 @@ void BufferQueueLayer::onFrameCancelled(const uint64_t bufferId) { void BufferQueueLayer::onFrameAvailable(const BufferItem& item) { const int32_t layerId = getSequence(); - mFlinger->mFrameTracer->traceTimestamp(layerId, item.mGraphicBuffer->getId(), item.mFrameNumber, - systemTime(), FrameTracer::FrameEvent::QUEUE); + const uint64_t bufferId = item.mGraphicBuffer->getId(); + mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, item.mFrameNumber, systemTime(), + FrameTracer::FrameEvent::QUEUE); + mFlinger->mFrameTracer->traceFence(layerId, bufferId, item.mFrameNumber, + std::make_shared<FenceTime>(item.mFence), + FrameTracer::FrameEvent::ACQUIRE_FENCE); ATRACE_CALL(); // Add this buffer from our internal queue tracker @@ -460,8 +461,12 @@ void BufferQueueLayer::onFrameReplaced(const BufferItem& item) { } const int32_t layerId = getSequence(); - mFlinger->mFrameTracer->traceTimestamp(layerId, item.mGraphicBuffer->getId(), item.mFrameNumber, - systemTime(), FrameTracer::FrameEvent::QUEUE); + const uint64_t bufferId = item.mGraphicBuffer->getId(); + mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, item.mFrameNumber, systemTime(), + FrameTracer::FrameEvent::QUEUE); + mFlinger->mFrameTracer->traceFence(layerId, bufferId, item.mFrameNumber, + std::make_shared<FenceTime>(item.mFence), + FrameTracer::FrameEvent::ACQUIRE_FENCE); mConsumer->onBufferAvailable(item); } diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 03903f6d07..e9965d4717 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -1445,6 +1445,13 @@ Layer::FrameRate Layer::getFrameRateForLayerTree() const { void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) { ATRACE_CALL(); + if (mLayerDetached) { + // If the layer is detached, then we don't defer this transaction since we will not + // commit the pending state while the layer is detached. Adding sync points may cause + // the barrier layer to wait for the states to be committed before dequeuing a buffer. + return; + } + mCurrentState.barrierLayer_legacy = barrierLayer; mCurrentState.frameNumber_legacy = frameNumber; // We don't set eTransactionNeeded, because just receiving a deferral @@ -2403,7 +2410,15 @@ InputWindowInfo Layer::fillInputInfo() { xSurfaceInset = (xSurfaceInset >= 0) ? std::min(xSurfaceInset, layerBounds.getWidth() / 2) : 0; ySurfaceInset = (ySurfaceInset >= 0) ? std::min(ySurfaceInset, layerBounds.getHeight() / 2) : 0; - layerBounds.inset(xSurfaceInset, ySurfaceInset, xSurfaceInset, ySurfaceInset); + // inset while protecting from overflow TODO(b/161235021): What is going wrong + // in the overflow scenario? + { + int32_t tmp; + if (!__builtin_add_overflow(layerBounds.left, xSurfaceInset, &tmp)) layerBounds.left = tmp; + if (!__builtin_sub_overflow(layerBounds.right, xSurfaceInset, &tmp)) layerBounds.right = tmp; + if (!__builtin_add_overflow(layerBounds.top, ySurfaceInset, &tmp)) layerBounds.top = tmp; + if (!__builtin_sub_overflow(layerBounds.bottom, ySurfaceInset, &tmp)) layerBounds.bottom = tmp; + } // Input coordinate should match the layer bounds. info.frameLeft = layerBounds.left; diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp index a596bce002..2a6fd0521f 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp @@ -243,7 +243,8 @@ void VSyncDispatchTimerQueue::timerCallback() { std::vector<Invocation> invocations; { std::lock_guard<decltype(mMutex)> lk(mMutex); - mLastTimerCallback = mTimeKeeper->now(); + auto const now = mTimeKeeper->now(); + mLastTimerCallback = now; for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { auto& callback = it->second; auto const wakeupTime = callback->wakeupTime(); @@ -251,7 +252,8 @@ void VSyncDispatchTimerQueue::timerCallback() { continue; } - if (*wakeupTime < mIntendedWakeupTime + mTimerSlack) { + auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0)); + if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) { callback->executing(); invocations.emplace_back( Invocation{callback, *callback->lastExecutedVsyncTarget(), *wakeupTime}); diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index ab5773dc09..61f3fbbdf1 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -255,21 +255,9 @@ void VSyncPredictor::clearTimestamps() { } } -bool VSyncPredictor::needsMoreSamples(nsecs_t now) const { - using namespace std::literals::chrono_literals; +bool VSyncPredictor::needsMoreSamples() const { std::lock_guard<std::mutex> lk(mMutex); - bool needsMoreSamples = true; - if (mTimestamps.size() >= kMinimumSamplesForPrediction) { - nsecs_t constexpr aLongTime = - std::chrono::duration_cast<std::chrono::nanoseconds>(500ms).count(); - if (!(mLastTimestampIndex < 0 || mTimestamps.empty())) { - auto const lastTimestamp = mTimestamps[mLastTimestampIndex]; - needsMoreSamples = !((lastTimestamp + aLongTime) > now); - } - } - - ATRACE_INT("VSP-moreSamples", needsMoreSamples); - return needsMoreSamples; + return mTimestamps.size() < kMinimumSamplesForPrediction; } void VSyncPredictor::resetModel() { diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index 3ca878d77d..5f3c418fed 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -52,11 +52,10 @@ public: */ void setPeriod(nsecs_t period) final; - /* Query if the model is in need of more samples to make a prediction at timePoint. - * \param [in] timePoint The timePoint to inquire of. + /* Query if the model is in need of more samples to make a prediction. * \return True, if model would benefit from more samples, False if not. */ - bool needsMoreSamples(nsecs_t timePoint) const; + bool needsMoreSamples() const final; std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const; diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index c743de07ae..efa8bab8fe 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -233,6 +233,7 @@ nsecs_t VSyncReactor::expectedPresentTime(nsecs_t now) { } void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) { + ATRACE_CALL(); mPeriodConfirmationInProgress = true; mPeriodTransitioningTo = newPeriod; mMoreSamplesNeeded = true; @@ -240,8 +241,7 @@ void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) { } void VSyncReactor::endPeriodTransition() { - setIgnorePresentFencesInternal(false); - mMoreSamplesNeeded = false; + ATRACE_CALL(); mPeriodTransitioningTo.reset(); mPeriodConfirmationInProgress = false; mLastHwVsync.reset(); @@ -254,6 +254,8 @@ void VSyncReactor::setPeriod(nsecs_t period) { if (!mSupportKernelIdleTimer && period == getPeriod()) { endPeriodTransition(); + setIgnorePresentFencesInternal(false); + mMoreSamplesNeeded = false; } else { startPeriodTransition(period); } @@ -303,6 +305,7 @@ bool VSyncReactor::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwc std::lock_guard<std::mutex> lk(mMutex); if (periodConfirmed(timestamp, hwcVsyncPeriod)) { + ATRACE_NAME("VSR: period confirmed"); if (mPeriodTransitioningTo) { mTracker->setPeriod(*mPeriodTransitioningTo); for (auto& entry : mCallbacks) { @@ -310,17 +313,29 @@ bool VSyncReactor::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwc } *periodFlushed = true; } + + if (mLastHwVsync) { + mTracker->addVsyncTimestamp(*mLastHwVsync); + } + mTracker->addVsyncTimestamp(timestamp); + endPeriodTransition(); + mMoreSamplesNeeded = mTracker->needsMoreSamples(); } else if (mPeriodConfirmationInProgress) { + ATRACE_NAME("VSR: still confirming period"); mLastHwVsync = timestamp; mMoreSamplesNeeded = true; *periodFlushed = false; } else { - mMoreSamplesNeeded = false; + ATRACE_NAME("VSR: adding sample"); *periodFlushed = false; + mTracker->addVsyncTimestamp(timestamp); + mMoreSamplesNeeded = mTracker->needsMoreSamples(); } - mTracker->addVsyncTimestamp(timestamp); + if (!mMoreSamplesNeeded) { + setIgnorePresentFencesInternal(false); + } return mMoreSamplesNeeded; } diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index 05a6fc3a8d..107c5400fe 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -66,6 +66,8 @@ public: /* Inform the tracker that the samples it has are not accurate for prediction. */ virtual void resetModel() = 0; + virtual bool needsMoreSamples() const = 0; + virtual void dump(std::string& result) const = 0; protected: diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 07690cbf32..c9e595abd1 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1566,7 +1566,7 @@ void SurfaceFlinger::signalRefresh() { mEventQueue->refresh(); } -nsecs_t SurfaceFlinger::getVsyncPeriod() const { +nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const { const auto displayId = getInternalDisplayIdLocked(); if (!displayId || !getHwComposer().isConnected(*displayId)) { return 0; @@ -1773,7 +1773,7 @@ void SurfaceFlinger::updateVrFlinger() { setPowerModeInternal(display, currentDisplayPowerMode); // Reset the timing values to account for the period of the swapped in HWC - const nsecs_t vsyncPeriod = getVsyncPeriod(); + const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod(); mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod); // The present fences returned from vr_hwc are not an accurate @@ -2121,7 +2121,8 @@ void SurfaceFlinger::onMessageRefresh() { mTimeStats->incrementCompositionStrategyChanges(); } - mVSyncModulator->onRefreshed(mHadClientComposition); + // TODO: b/160583065 Enable skip validation when SF caches all client composition layers + mVSyncModulator->onRefreshed(mHadClientComposition || mReusedClientComposition); mLayersWithQueuedFrames.clear(); if (mVisibleRegionsDirty) { @@ -4191,8 +4192,7 @@ void SurfaceFlinger::onInitializeDisplays() { {}); setPowerModeInternal(display, hal::PowerMode::ON); - - const nsecs_t vsyncPeriod = getVsyncPeriod(); + const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod(); mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod); // Use phase of 0 since phase is not known. @@ -4227,7 +4227,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: if (mInterceptor->isEnabled()) { mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode)); } - + const auto vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod(); if (currentMode == hal::PowerMode::OFF) { if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) { ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno)); @@ -4236,7 +4236,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: if (display->isPrimary() && mode != hal::PowerMode::DOZE_SUSPEND) { getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState); mScheduler->onScreenAcquired(mAppConnectionHandle); - mScheduler->resyncToHardwareVsync(true, getVsyncPeriod()); + mScheduler->resyncToHardwareVsync(true, vsyncPeriod); } mVisibleRegionsDirty = true; @@ -4263,7 +4263,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: getHwComposer().setPowerMode(*displayId, mode); if (display->isPrimary() && currentMode == hal::PowerMode::DOZE_SUSPEND) { mScheduler->onScreenAcquired(mAppConnectionHandle); - mScheduler->resyncToHardwareVsync(true, getVsyncPeriod()); + mScheduler->resyncToHardwareVsync(true, vsyncPeriod); } } else if (mode == hal::PowerMode::DOZE_SUSPEND) { // Leave display going to doze @@ -4376,7 +4376,7 @@ void SurfaceFlinger::listLayersLocked(std::string& result) const { } void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const { - StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriod()); + StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriodFromHWC()); if (args.size() > 1) { const auto name = String8(args[1]); @@ -4441,7 +4441,7 @@ void SurfaceFlinger::dumpVSync(std::string& result) const { mPhaseConfiguration->dump(result); StringAppendF(&result, " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n", - dispSyncPresentTimeOffset, getVsyncPeriod()); + dispSyncPresentTimeOffset, getVsyncPeriodFromHWC()); scheduler::RefreshRateConfigs::Policy policy = mRefreshRateConfigs->getDisplayManagerPolicy(); StringAppendF(&result, @@ -6227,6 +6227,11 @@ status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, Mutex::Autolock lock(mStateLock); if (authenticateSurfaceTextureLocked(surface)) { sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer(); + if (layer == nullptr) { + ALOGE("Attempt to set frame rate on a layer that no longer exists"); + return BAD_VALUE; + } + if (layer->setFrameRate( Layer::FrameRate(frameRate, Layer::FrameRate::convertCompatibility(compatibility)))) { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index ccaeb2d858..c727574780 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -852,7 +852,7 @@ private: /* ------------------------------------------------------------------------ * VSync */ - nsecs_t getVsyncPeriod() const REQUIRES(mStateLock); + nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock); // Sets the refresh rate by switching active configs, if they are available for // the desired refresh rate. diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index ce5f35cdf3..06bdcdc666 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -85,7 +85,7 @@ using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; using HotplugEvent = TestableSurfaceFlinger::HotplugEvent; using HWC2Display = TestableSurfaceFlinger::HWC2Display; -constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'666; +constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'667; constexpr int32_t DEFAULT_DPI = 320; constexpr int DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT = HAL_PIXEL_FORMAT_RGB_565; diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp index be49ef33f2..c2a77527a1 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp @@ -51,6 +51,7 @@ public: void setPeriod(nsecs_t) final {} void resetModel() final {} + bool needsMoreSamples() const final { return false; } void dump(std::string&) const final {} private: @@ -86,6 +87,7 @@ public: void setPeriod(nsecs_t) final {} void resetModel() final {} + bool needsMoreSamples() const final { return false; } void dump(std::string&) const final {} private: diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp index d940dc5870..373f6a52fb 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp @@ -47,6 +47,7 @@ public: MOCK_CONST_METHOD0(currentPeriod, nsecs_t()); MOCK_METHOD1(setPeriod, void(nsecs_t)); MOCK_METHOD0(resetModel, void()); + MOCK_CONST_METHOD0(needsMoreSamples, bool()); MOCK_CONST_METHOD1(dump, void(std::string&)); nsecs_t nextVSyncTime(nsecs_t timePoint) const { @@ -115,11 +116,15 @@ public: operator VSyncDispatch::CallbackToken() const { return mToken; } - void counter(nsecs_t time, nsecs_t) { mCalls.push_back(time); } + void counter(nsecs_t time, nsecs_t wakeup_time) { + mCalls.push_back(time); + mWakeupTime.push_back(wakeup_time); + } VSyncDispatch& mDispatch; VSyncDispatch::CallbackToken mToken; std::vector<nsecs_t> mCalls; + std::vector<nsecs_t> mWakeupTime; }; class PausingCallback { @@ -747,6 +752,31 @@ TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenCancelledAndIsNextScheduled) { EXPECT_THAT(cb2.mCalls.size(), Eq(1)); } +TEST_F(VSyncDispatchTimerQueueTest, laggedTimerGroupsCallbacksWithinLag) { + CountingCallback cb1(mDispatch); + CountingCallback cb2(mDispatch); + + Sequence seq; + EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)) + .InSequence(seq) + .WillOnce(Return(1000)); + EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq); + EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)) + .InSequence(seq) + .WillOnce(Return(1000)); + + EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb2, 390, 1000), ScheduleResult::Scheduled); + + mMockClock.setLag(100); + mMockClock.advanceBy(700); + + ASSERT_THAT(cb1.mWakeupTime.size(), Eq(1)); + EXPECT_THAT(cb1.mWakeupTime[0], Eq(600)); + ASSERT_THAT(cb2.mWakeupTime.size(), Eq(1)); + EXPECT_THAT(cb2.mWakeupTime[0], Eq(610)); +} + class VSyncDispatchTimerQueueEntryTest : public testing::Test { protected: nsecs_t const mPeriod = 1000; diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index fc39235a93..d4cd11dbe1 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -73,27 +73,27 @@ TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) { TEST_F(VSyncPredictorTest, reportsSamplesNeededWhenHasNoDataPoints) { for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { - EXPECT_TRUE(tracker.needsMoreSamples(mNow += mPeriod)); - tracker.addVsyncTimestamp(mNow); + EXPECT_TRUE(tracker.needsMoreSamples()); + tracker.addVsyncTimestamp(mNow += mPeriod); } - EXPECT_FALSE(tracker.needsMoreSamples(mNow)); + EXPECT_FALSE(tracker.needsMoreSamples()); } TEST_F(VSyncPredictorTest, reportsSamplesNeededAfterExplicitRateChange) { for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { tracker.addVsyncTimestamp(mNow += mPeriod); } - EXPECT_FALSE(tracker.needsMoreSamples(mNow)); + EXPECT_FALSE(tracker.needsMoreSamples()); auto const changedPeriod = mPeriod * 2; tracker.setPeriod(changedPeriod); - EXPECT_TRUE(tracker.needsMoreSamples(mNow)); + EXPECT_TRUE(tracker.needsMoreSamples()); for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { - EXPECT_TRUE(tracker.needsMoreSamples(mNow += changedPeriod)); - tracker.addVsyncTimestamp(mNow); + EXPECT_TRUE(tracker.needsMoreSamples()); + tracker.addVsyncTimestamp(mNow += changedPeriod); } - EXPECT_FALSE(tracker.needsMoreSamples(mNow)); + EXPECT_FALSE(tracker.needsMoreSamples()); } TEST_F(VSyncPredictorTest, transitionsToModelledPointsAfterSynthetic) { @@ -320,20 +320,6 @@ TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) { EXPECT_THAT(intercept, Eq(0)); } -TEST_F(VSyncPredictorTest, willBecomeInaccurateAfterA_longTimeWithNoSamples) { - auto const simulatedVsyncs = generateVsyncTimestamps(kMinimumSamplesForPrediction, mPeriod, 0); - - for (auto const& timestamp : simulatedVsyncs) { - tracker.addVsyncTimestamp(timestamp); - } - auto const mNow = *simulatedVsyncs.rbegin(); - EXPECT_FALSE(tracker.needsMoreSamples(mNow)); - - // TODO: would be better to decay this as a result of the variance of the samples - static auto constexpr aLongTimeOut = 1000000000; - EXPECT_TRUE(tracker.needsMoreSamples(mNow + aLongTimeOut)); -} - TEST_F(VSyncPredictorTest, idealModelPredictionsBeforeRegressionModelIsBuilt) { auto const simulatedVsyncs = generateVsyncTimestamps(kMinimumSamplesForPrediction + 1, mPeriod, 0); @@ -443,7 +429,7 @@ TEST_F(VSyncPredictorTest, slopeAlwaysValid) { // When VsyncPredictor returns the period it means that it doesn't know how to predict and // it needs to get more samples if (slope == mPeriod && intercept == 0) { - EXPECT_TRUE(tracker.needsMoreSamples(now)); + EXPECT_TRUE(tracker.needsMoreSamples()); } } } diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp index a97256203d..6856612b78 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp @@ -41,6 +41,7 @@ public: MOCK_CONST_METHOD0(currentPeriod, nsecs_t()); MOCK_METHOD1(setPeriod, void(nsecs_t)); MOCK_METHOD0(resetModel, void()); + MOCK_CONST_METHOD0(needsMoreSamples, bool()); MOCK_CONST_METHOD1(dump, void(std::string&)); }; @@ -57,6 +58,7 @@ public: nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); } void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); } void resetModel() final { mTracker->resetModel(); } + bool needsMoreSamples() const final { return mTracker->needsMoreSamples(); } void dump(std::string& result) const final { mTracker->dump(result); } private: @@ -455,6 +457,83 @@ TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsH EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); } +TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTracker) { + auto time = 0; + bool periodFlushed = false; + nsecs_t const newPeriod = 4000; + mReactor.setPeriod(newPeriod); + + static auto constexpr numSamplesWithNewPeriod = 4; + Sequence seq; + EXPECT_CALL(*mMockTracker, needsMoreSamples()) + .Times(numSamplesWithNewPeriod - 2) + .InSequence(seq) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*mMockTracker, needsMoreSamples()) + .Times(1) + .InSequence(seq) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(numSamplesWithNewPeriod); + + EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed)); + + EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed)); + // confirmed period, but predictor wants numRequest samples. This one and prior are valid. + EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed)); + EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed)); +} + +TEST_F(VSyncReactorTest, hwVsyncturnsOffOnConfirmationWhenTrackerDoesntRequest) { + auto time = 0; + bool periodFlushed = false; + nsecs_t const newPeriod = 4000; + mReactor.setPeriod(newPeriod); + + Sequence seq; + EXPECT_CALL(*mMockTracker, needsMoreSamples()) + .Times(1) + .InSequence(seq) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2); + + EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed)); + EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed)); +} + +TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) { + auto time = 0; + bool periodFlushed = false; + nsecs_t const newPeriod1 = 4000; + nsecs_t const newPeriod2 = 7000; + + mReactor.setPeriod(newPeriod1); + + Sequence seq; + EXPECT_CALL(*mMockTracker, needsMoreSamples()) + .Times(4) + .InSequence(seq) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*mMockTracker, needsMoreSamples()) + .Times(1) + .InSequence(seq) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(7); + + EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed)); + // confirmed period, but predictor wants numRequest samples. This one and prior are valid. + EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed)); + + mReactor.setPeriod(newPeriod2); + EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed)); + EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed)); +} + static nsecs_t computeWorkload(nsecs_t period, nsecs_t phase) { return period - phase; } @@ -648,7 +727,7 @@ TEST_F(VSyncReactorTest, beginResyncResetsModel) { TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) { bool periodFlushed = true; - EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(3); + EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2); mReactor.setIgnorePresentFences(true); nsecs_t const newPeriod = 5000; @@ -672,7 +751,7 @@ TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) { kPendingLimit, true /* supportKernelIdleTimer */); bool periodFlushed = true; - EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(5); + EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(4); idleReactor.setIgnorePresentFences(true); // First, set the same period, which should only be confirmed when we receive two |