Merge "Rollback android.hardware.telephony.data"
diff --git a/include/android/input.h b/include/android/input.h
index f51cd79..dbfd61e 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -793,6 +793,8 @@
AMOTION_EVENT_TOOL_TYPE_MOUSE = 3,
/** eraser */
AMOTION_EVENT_TOOL_TYPE_ERASER = 4,
+ /** palm */
+ AMOTION_EVENT_TOOL_TYPE_PALM = 5,
};
/**
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 5959340..55a892e 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -69,9 +69,6 @@
"SurfaceComposerClient.cpp",
"SyncFeatures.cpp",
"view/Surface.cpp",
- "surfacetexture/SurfaceTexture.cpp",
- "surfacetexture/ImageConsumer.cpp",
- "surfacetexture/EGLConsumer.cpp",
],
shared_libs: [
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index bcc99fc..c956578 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -45,6 +45,10 @@
srcs: [
"AChoreographer.cpp",
"ADisplay.cpp",
+ "surfacetexture/surface_texture.cpp",
+ "surfacetexture/SurfaceTexture.cpp",
+ "surfacetexture/ImageConsumer.cpp",
+ "surfacetexture/EGLConsumer.cpp",
],
shared_libs: [
@@ -53,6 +57,10 @@
"libnativewindow",
"libui",
"libutils",
+ "libcutils",
+ "libEGL",
+ "libGLESv2",
+ "libnativehelper",
],
header_libs: [
diff --git a/libs/gui/include/gui/surfacetexture/EGLConsumer.h b/libs/nativedisplay/include/surfacetexture/EGLConsumer.h
similarity index 100%
rename from libs/gui/include/gui/surfacetexture/EGLConsumer.h
rename to libs/nativedisplay/include/surfacetexture/EGLConsumer.h
diff --git a/libs/gui/include/gui/surfacetexture/ImageConsumer.h b/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
similarity index 100%
rename from libs/gui/include/gui/surfacetexture/ImageConsumer.h
rename to libs/nativedisplay/include/surfacetexture/ImageConsumer.h
diff --git a/libs/gui/include/gui/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
similarity index 100%
rename from libs/gui/include/gui/surfacetexture/SurfaceTexture.h
rename to libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
diff --git a/libs/gui/include/gui/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
similarity index 85%
rename from libs/gui/include/gui/surfacetexture/surface_texture_platform.h
rename to libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
index 72f98ad..6a94a77 100644
--- a/libs/gui/include/gui/surfacetexture/surface_texture_platform.h
+++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
@@ -22,17 +22,11 @@
#include <system/graphics.h>
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/surfacetexture/SurfaceTexture.h>
-
// This file provides a facade API on top of SurfaceTexture, which avoids using
// C++ types. This is still a C++ unstable API though. Ideally features here
// will be exposed via public NDK API and this file will be deleted.
-struct ASurfaceTexture {
- android::sp<android::SurfaceTexture> consumer;
- android::sp<android::IGraphicBufferProducer> producer;
-};
+struct ASurfaceTexture;
namespace android {
@@ -79,13 +73,6 @@
ASurfaceTexture_fenceWait fenceWait,
void* fencePassThroughHandle);
-/**
- * ASurfaceTexture_create creates an ASurfaceTexture, which is
- * a simple struct containing a SurfaceTexture and an IGraphicBufferProducer.
- */
-ASurfaceTexture* ASurfaceTexture_create(sp<SurfaceTexture> consumer,
- sp<IGraphicBufferProducer> producer);
-
} // namespace android
#endif // _ANDROID_GRAPHICS_SURFACE_TEXTURE_PLATFORM_H
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 650bdf1..483fb25 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -11,6 +11,8 @@
AChoreographer_destroy; # apex # introduced=30
AChoreographer_getFd; # apex # introduced=30
AChoreographer_handlePendingEvents; # apex # introduced=30
+ ASurfaceTexture_fromSurfaceTexture; # apex # introduced=30
+ ASurfaceTexture_release; # apex # introduced=30
local:
*;
};
@@ -30,5 +32,16 @@
android::ADisplayConfig_getFps*;
android::ADisplayConfig_getCompositorOffsetNanos*;
android::ADisplayConfig_getAppVsyncOffsetNanos*;
+ android::ASurfaceTexture_getCurrentTextureTarget*;
+ android::ASurfaceTexture_takeConsumerOwnership*;
+ android::ASurfaceTexture_releaseConsumerOwnership*;
+ android::ASurfaceTexture_dequeueBuffer*;
+ android::SurfaceTexture*;
};
+ ASurfaceTexture_acquireANativeWindow;
+ ASurfaceTexture_attachToGLContext;
+ ASurfaceTexture_detachFromGLContext;
+ ASurfaceTexture_getTimestamp;
+ ASurfaceTexture_getTransformMatrix;
+ ASurfaceTexture_updateTexImage;
} LIBNATIVEDISPLAY;
diff --git a/libs/gui/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
similarity index 99%
rename from libs/gui/surfacetexture/EGLConsumer.cpp
rename to libs/nativedisplay/surfacetexture/EGLConsumer.cpp
index 1f0f52c..2f31888 100644
--- a/libs/gui/surfacetexture/EGLConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
@@ -24,8 +24,8 @@
#include <cutils/compiler.h>
#include <gui/BufferItem.h>
#include <gui/BufferQueue.h>
-#include <gui/surfacetexture/EGLConsumer.h>
-#include <gui/surfacetexture/SurfaceTexture.h>
+#include <surfacetexture/EGLConsumer.h>
+#include <surfacetexture/SurfaceTexture.h>
#include <inttypes.h>
#include <private/gui/SyncFeatures.h>
#include <utils/Log.h>
diff --git a/libs/gui/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
similarity index 97%
rename from libs/gui/surfacetexture/ImageConsumer.cpp
rename to libs/nativedisplay/surfacetexture/ImageConsumer.cpp
index 4bc4a7b..16afc68 100644
--- a/libs/gui/surfacetexture/ImageConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
@@ -15,8 +15,8 @@
*/
#include <gui/BufferQueue.h>
-#include <gui/surfacetexture/ImageConsumer.h>
-#include <gui/surfacetexture/SurfaceTexture.h>
+#include <surfacetexture/ImageConsumer.h>
+#include <surfacetexture/SurfaceTexture.h>
// Macro for including the SurfaceTexture name in log messages
#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
diff --git a/libs/gui/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
similarity index 90%
rename from libs/gui/surfacetexture/SurfaceTexture.cpp
rename to libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
index 25e5618..62db6d0 100644
--- a/libs/gui/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -16,9 +16,9 @@
#include <cutils/compiler.h>
#include <gui/BufferQueue.h>
-#include <gui/surfacetexture/ImageConsumer.h>
-#include <gui/surfacetexture/SurfaceTexture.h>
-#include <gui/surfacetexture/surface_texture_platform.h>
+#include <surfacetexture/ImageConsumer.h>
+#include <surfacetexture/SurfaceTexture.h>
+#include <surfacetexture/surface_texture_platform.h>
#include <math/mat4.h>
#include <system/window.h>
#include <utils/Trace.h>
@@ -487,42 +487,4 @@
return buffer;
}
-unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st) {
- return st->consumer->getCurrentTextureTarget();
-}
-
-void ASurfaceTexture_takeConsumerOwnership(ASurfaceTexture* texture) {
- texture->consumer->takeConsumerOwnership();
-}
-
-void ASurfaceTexture_releaseConsumerOwnership(ASurfaceTexture* texture) {
- texture->consumer->releaseConsumerOwnership();
-}
-
-AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
- android_dataspace* outDataspace,
- float* outTransformMatrix, bool* outNewContent,
- ASurfaceTexture_createReleaseFence createFence,
- ASurfaceTexture_fenceWait fenceWait, void* handle) {
- sp<GraphicBuffer> buffer;
- *outNewContent = false;
- bool queueEmpty;
- do {
- buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, outTransformMatrix,
- &queueEmpty, createFence, fenceWait, handle);
- if (!queueEmpty) {
- *outNewContent = true;
- }
- } while (buffer.get() && (!queueEmpty));
- return reinterpret_cast<AHardwareBuffer*>(buffer.get());
-}
-
-ASurfaceTexture* ASurfaceTexture_create(sp<SurfaceTexture> consumer,
- sp<IGraphicBufferProducer> producer) {
- ASurfaceTexture* ast = new ASurfaceTexture;
- ast->consumer = consumer;
- ast->producer = producer;
- return ast;
-}
-
} // namespace android
diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp
new file mode 100644
index 0000000..1670fbb
--- /dev/null
+++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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/surface_texture.h>
+#include <android/surface_texture_jni.h>
+
+#define LOG_TAG "ASurfaceTexture"
+
+#include <utils/Log.h>
+
+#include <gui/Surface.h>
+
+#include <surfacetexture/surface_texture_platform.h>
+#include <surfacetexture/SurfaceTexture.h>
+
+#include <mutex>
+
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+
+struct ASurfaceTexture {
+ android::sp<android::SurfaceTexture> consumer;
+ android::sp<android::IGraphicBufferProducer> producer;
+};
+
+using namespace android;
+
+const char* const kSurfaceTextureClassPathName = "android/graphics/SurfaceTexture";
+
+struct fields_t {
+ jfieldID surfaceTexture;
+ jfieldID producer;
+};
+static fields_t fields;
+static std::once_flag sInitFieldsOnce;
+
+#define ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID "mSurfaceTexture"
+#define ANDROID_GRAPHICS_PRODUCER_JNI_ID "mProducer"
+
+static void SurfaceTexture_classInit(JNIEnv* env, jclass clazz)
+{
+ fields.surfaceTexture = env->GetFieldID(clazz,
+ ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID, "J");
+ if (fields.surfaceTexture == NULL) {
+ ALOGE("can't find android/graphics/SurfaceTexture.%s",
+ ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID);
+ }
+ fields.producer = env->GetFieldID(clazz,
+ ANDROID_GRAPHICS_PRODUCER_JNI_ID, "J");
+ if (fields.producer == NULL) {
+ ALOGE("can't find android/graphics/SurfaceTexture.%s",
+ ANDROID_GRAPHICS_PRODUCER_JNI_ID);
+ }
+}
+
+static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) {
+ jclass clazz = env->FindClass(class_name);
+ LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name);
+ return clazz;
+}
+
+static void register_android_graphics_SurfaceTexture(JNIEnv* env)
+{
+ // Cache some fields.
+ ScopedLocalRef<jclass> klass(env, FindClassOrDie(env, kSurfaceTextureClassPathName));
+ SurfaceTexture_classInit(env, klass.get());
+}
+
+static bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) {
+ std::call_once(sInitFieldsOnce, [=]() {
+ register_android_graphics_SurfaceTexture(env);
+ });
+
+ jclass surfaceTextureClass = env->FindClass(kSurfaceTextureClassPathName);
+ return env->IsInstanceOf(thiz, surfaceTextureClass);
+}
+
+static sp<SurfaceTexture> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) {
+ std::call_once(sInitFieldsOnce, [=]() {
+ register_android_graphics_SurfaceTexture(env);
+ });
+
+ return (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture);
+}
+
+static sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) {
+ std::call_once(sInitFieldsOnce, [=]() {
+ register_android_graphics_SurfaceTexture(env);
+ });
+
+ return (IGraphicBufferProducer*)env->GetLongField(thiz, fields.producer);
+}
+
+// The following functions implement NDK API.
+ASurfaceTexture* ASurfaceTexture_fromSurfaceTexture(JNIEnv* env, jobject surfacetexture) {
+ if (!surfacetexture || !android_SurfaceTexture_isInstanceOf(env, surfacetexture)) {
+ return nullptr;
+ }
+ ASurfaceTexture* ast = new ASurfaceTexture;
+ ast->consumer = SurfaceTexture_getSurfaceTexture(env, surfacetexture);
+ ast->producer = SurfaceTexture_getProducer(env, surfacetexture);
+ return ast;
+}
+
+ANativeWindow* ASurfaceTexture_acquireANativeWindow(ASurfaceTexture* st) {
+ sp<Surface> surface = new Surface(st->producer);
+ ANativeWindow* win(surface.get());
+ ANativeWindow_acquire(win);
+ return win;
+}
+
+void ASurfaceTexture_release(ASurfaceTexture* st) {
+ delete st;
+}
+
+int ASurfaceTexture_attachToGLContext(ASurfaceTexture* st, uint32_t tex) {
+ return st->consumer->attachToContext(tex);
+}
+
+int ASurfaceTexture_detachFromGLContext(ASurfaceTexture* st) {
+ return st->consumer->detachFromContext();
+}
+
+int ASurfaceTexture_updateTexImage(ASurfaceTexture* st) {
+ return st->consumer->updateTexImage();
+}
+
+void ASurfaceTexture_getTransformMatrix(ASurfaceTexture* st, float mtx[16]) {
+ st->consumer->getTransformMatrix(mtx);
+}
+
+int64_t ASurfaceTexture_getTimestamp(ASurfaceTexture* st) {
+ return st->consumer->getTimestamp();
+}
+
+// The following functions are private/unstable API.
+namespace android {
+
+unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st) {
+ return st->consumer->getCurrentTextureTarget();
+}
+
+void ASurfaceTexture_takeConsumerOwnership(ASurfaceTexture* texture) {
+ texture->consumer->takeConsumerOwnership();
+}
+
+void ASurfaceTexture_releaseConsumerOwnership(ASurfaceTexture* texture) {
+ texture->consumer->releaseConsumerOwnership();
+}
+
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
+ android_dataspace* outDataspace,
+ float* outTransformMatrix, bool* outNewContent,
+ ASurfaceTexture_createReleaseFence createFence,
+ ASurfaceTexture_fenceWait fenceWait, void* handle) {
+ sp<GraphicBuffer> buffer;
+ *outNewContent = false;
+ bool queueEmpty;
+ do {
+ buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, outTransformMatrix,
+ &queueEmpty, createFence, fenceWait, handle);
+ if (!queueEmpty) {
+ *outNewContent = true;
+ }
+ } while (buffer.get() && (!queueEmpty));
+ return reinterpret_cast<AHardwareBuffer*>(buffer.get());
+}
+
+} // namespace android
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index ce7399e..30fdf90 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -256,6 +256,75 @@
return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
}
+static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
+ EventEntry* eventEntry,
+ int32_t inputTargetFlags) {
+ if (inputTarget.useDefaultPointerInfo()) {
+ const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo();
+ return std::make_unique<DispatchEntry>(eventEntry, // increments ref
+ inputTargetFlags, pointerInfo.xOffset,
+ pointerInfo.yOffset, inputTarget.globalScaleFactor,
+ pointerInfo.windowXScale, pointerInfo.windowYScale);
+ }
+
+ ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
+
+ PointerCoords pointerCoords[motionEntry.pointerCount];
+
+ // Use the first pointer information to normalize all other pointers. This could be any pointer
+ // as long as all other pointers are normalized to the same value and the final DispatchEntry
+ // uses the offset and scale for the normalized pointer.
+ const PointerInfo& firstPointerInfo =
+ inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()];
+
+ // Iterate through all pointers in the event to normalize against the first.
+ for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) {
+ const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex];
+ uint32_t pointerId = uint32_t(pointerProperties.id);
+ const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId];
+
+ // The scale factor is the ratio of the current pointers scale to the normalized scale.
+ float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale;
+ float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale;
+
+ pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]);
+ // First apply the current pointers offset to set the window at 0,0
+ pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset);
+ // Next scale the coordinates.
+ pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff);
+ // Lastly, offset the coordinates so they're in the normalized pointer's frame.
+ pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset,
+ -firstPointerInfo.yOffset);
+ }
+
+ MotionEntry* combinedMotionEntry =
+ new MotionEntry(motionEntry.sequenceNum, motionEntry.eventTime, motionEntry.deviceId,
+ motionEntry.source, motionEntry.displayId, motionEntry.policyFlags,
+ motionEntry.action, motionEntry.actionButton, motionEntry.flags,
+ motionEntry.metaState, motionEntry.buttonState,
+ motionEntry.classification, motionEntry.edgeFlags,
+ motionEntry.xPrecision, motionEntry.yPrecision,
+ motionEntry.xCursorPosition, motionEntry.yCursorPosition,
+ motionEntry.downTime, motionEntry.pointerCount,
+ motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */,
+ 0 /* yOffset */);
+
+ if (motionEntry.injectionState) {
+ combinedMotionEntry->injectionState = motionEntry.injectionState;
+ combinedMotionEntry->injectionState->refCount += 1;
+ }
+
+ std::unique_ptr<DispatchEntry> dispatchEntry =
+ std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref
+ inputTargetFlags, firstPointerInfo.xOffset,
+ firstPointerInfo.yOffset, inputTarget.globalScaleFactor,
+ firstPointerInfo.windowXScale,
+ firstPointerInfo.windowYScale);
+ combinedMotionEntry->release();
+ return dispatchEntry;
+}
+
// --- InputDispatcherThread ---
class InputDispatcher::InputDispatcherThread : public Thread {
@@ -1815,23 +1884,34 @@
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
std::vector<InputTarget>& inputTargets) {
- sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
- if (inputChannel == nullptr) {
- ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
- return;
- }
+ std::vector<InputTarget>::iterator it =
+ std::find_if(inputTargets.begin(), inputTargets.end(),
+ [&windowHandle](const InputTarget& inputTarget) {
+ return inputTarget.inputChannel->getConnectionToken() ==
+ windowHandle->getToken();
+ });
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- InputTarget target;
- target.inputChannel = inputChannel;
- target.flags = targetFlags;
- target.xOffset = -windowInfo->frameLeft;
- target.yOffset = -windowInfo->frameTop;
- target.globalScaleFactor = windowInfo->globalScaleFactor;
- target.windowXScale = windowInfo->windowXScale;
- target.windowYScale = windowInfo->windowYScale;
- target.pointerIds = pointerIds;
- inputTargets.push_back(target);
+
+ if (it == inputTargets.end()) {
+ InputTarget inputTarget;
+ sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
+ if (inputChannel == nullptr) {
+ ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
+ return;
+ }
+ inputTarget.inputChannel = inputChannel;
+ inputTarget.flags = targetFlags;
+ inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
+ inputTargets.push_back(inputTarget);
+ it = inputTargets.end() - 1;
+ }
+
+ ALOG_ASSERT(it->flags == targetFlags);
+ ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);
+
+ it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop,
+ windowInfo->windowXScale, windowInfo->windowYScale);
}
void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
@@ -1854,10 +1934,7 @@
InputTarget target;
target.inputChannel = monitor.inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- target.xOffset = xOffset;
- target.yOffset = yOffset;
- target.pointerIds.clear();
- target.globalScaleFactor = 1.0f;
+ target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */);
inputTargets.push_back(target);
}
@@ -2089,11 +2166,10 @@
}
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
- "xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
- "windowScaleFactor=(%f, %f), pointerIds=0x%x",
- connection->getInputChannelName().c_str(), inputTarget.flags, inputTarget.xOffset,
- inputTarget.yOffset, inputTarget.globalScaleFactor, inputTarget.windowXScale,
- inputTarget.windowYScale, inputTarget.pointerIds.value);
+ "globalScaleFactor=%f, pointerIds=0x%x %s",
+ connection->getInputChannelName().c_str(), inputTarget.flags,
+ inputTarget.globalScaleFactor, inputTarget.pointerIds.value,
+ inputTarget.getPointerInfoString().c_str());
#endif
// Skip this event if the connection status is not normal.
@@ -2187,15 +2263,15 @@
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
std::unique_ptr<DispatchEntry> dispatchEntry =
- std::make_unique<DispatchEntry>(eventEntry, // increments ref
- inputTargetFlags, inputTarget.xOffset,
- inputTarget.yOffset, inputTarget.globalScaleFactor,
- inputTarget.windowXScale, inputTarget.windowYScale);
+ createDispatchEntry(inputTarget, eventEntry, inputTargetFlags);
+ // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
+ // different EventEntry than what was passed in.
+ EventEntry* newEntry = dispatchEntry->eventEntry;
// Apply target flags and update the connection's input state.
- switch (eventEntry->type) {
+ switch (newEntry->type) {
case EventEntry::Type::KEY: {
- const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*eventEntry);
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*newEntry);
dispatchEntry->resolvedAction = keyEntry.action;
dispatchEntry->resolvedFlags = keyEntry.flags;
@@ -2211,7 +2287,7 @@
}
case EventEntry::Type::MOTION: {
- const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*newEntry);
if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
@@ -2265,14 +2341,14 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("%s events should not go to apps",
- EventEntry::typeToString(eventEntry->type));
+ EventEntry::typeToString(newEntry->type));
break;
}
}
// Remember that we are waiting for this dispatch to complete.
if (dispatchEntry->hasForegroundTarget()) {
- incrementPendingForegroundDispatches(eventEntry);
+ incrementPendingForegroundDispatches(newEntry);
}
// Enqueue the dispatch entry.
@@ -2661,15 +2737,9 @@
getWindowHandleLocked(connection->inputChannel->getConnectionToken());
if (windowHandle != nullptr) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- target.xOffset = -windowInfo->frameLeft;
- target.yOffset = -windowInfo->frameTop;
+ target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
+ windowInfo->windowXScale, windowInfo->windowYScale);
target.globalScaleFactor = windowInfo->globalScaleFactor;
- target.windowXScale = windowInfo->windowXScale;
- target.windowYScale = windowInfo->windowYScale;
- } else {
- target.xOffset = 0;
- target.yOffset = 0;
- target.globalScaleFactor = 1.0f;
}
target.inputChannel = connection->inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 80fa2cb..0588374 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -42,4 +42,63 @@
return StringPrintf("%" PRId32, dispatchMode);
}
+void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset,
+ float windowXScale, float windowYScale) {
+ // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors
+ // and non splittable windows since we will just use all the pointers from the input event.
+ if (newPointerIds.isEmpty()) {
+ setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale);
+ return;
+ }
+
+ // Ensure that the new set of pointers doesn't overlap with the current set of pointers.
+ ALOG_ASSERT((pointerIds & newPointerIds) == 0);
+
+ pointerIds |= newPointerIds;
+ while (!newPointerIds.isEmpty()) {
+ int32_t pointerId = newPointerIds.clearFirstMarkedBit();
+ pointerInfos[pointerId].xOffset = xOffset;
+ pointerInfos[pointerId].yOffset = yOffset;
+ pointerInfos[pointerId].windowXScale = windowXScale;
+ pointerInfos[pointerId].windowYScale = windowYScale;
+ }
+}
+
+void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
+ float windowYScale) {
+ pointerIds.clear();
+ pointerInfos[0].xOffset = xOffset;
+ pointerInfos[0].yOffset = yOffset;
+ pointerInfos[0].windowXScale = windowXScale;
+ pointerInfos[0].windowYScale = windowYScale;
+}
+
+bool InputTarget::useDefaultPointerInfo() const {
+ return pointerIds.isEmpty();
+}
+
+const PointerInfo& InputTarget::getDefaultPointerInfo() const {
+ return pointerInfos[0];
+}
+
+std::string InputTarget::getPointerInfoString() const {
+ if (useDefaultPointerInfo()) {
+ const PointerInfo& pointerInfo = getDefaultPointerInfo();
+ return StringPrintf("xOffset=%.1f, yOffset=%.1f windowScaleFactor=(%.1f, %.1f)",
+ pointerInfo.xOffset, pointerInfo.yOffset, pointerInfo.windowXScale,
+ pointerInfo.windowYScale);
+ }
+
+ std::string out;
+ for (uint32_t i = pointerIds.firstMarkedBit(); i <= pointerIds.lastMarkedBit(); i++) {
+ if (!pointerIds.hasBit(i)) {
+ continue;
+ }
+ out += StringPrintf("\n pointerId %d: xOffset=%.1f, yOffset=%.1f "
+ "windowScaleFactor=(%.1f, %.1f)",
+ i, pointerInfos[i].xOffset, pointerInfos[i].yOffset,
+ pointerInfos[i].windowXScale, pointerInfos[i].windowYScale);
+ }
+ return out;
+}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 1ba5eff..499a75f 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -24,6 +24,22 @@
namespace android::inputdispatcher {
/*
+ * Information about each pointer for an InputTarget. This includes offset and scale so
+ * all pointers can be normalized to a single offset and scale.
+ *
+ * These values are ignored for KeyEvents
+ */
+struct PointerInfo {
+ // The x and y offset to add to a MotionEvent as it is delivered.
+ float xOffset = 0.0f;
+ float yOffset = 0.0f;
+
+ // Scaling factor to apply to MotionEvent as it is delivered.
+ float windowXScale = 1.0f;
+ float windowYScale = 1.0f;
+};
+
+/*
* An input target specifies how an input event is to be dispatched to a particular window
* including the window's input channel, control flags, a timeout, and an X / Y offset to
* be added to input event coordinates to compensate for the absolute position of the
@@ -95,20 +111,37 @@
// Flags for the input target.
int32_t flags = 0;
- // The x and y offset to add to a MotionEvent as it is delivered.
- // (ignored for KeyEvents)
- float xOffset = 0.0f;
- float yOffset = 0.0f;
-
// Scaling factor to apply to MotionEvent as it is delivered.
// (ignored for KeyEvents)
float globalScaleFactor = 1.0f;
- float windowXScale = 1.0f;
- float windowYScale = 1.0f;
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
BitSet32 pointerIds;
+ // The data is stored by the pointerId. Use the bit position of pointerIds to look up
+ // PointerInfo per pointerId.
+ PointerInfo pointerInfos[MAX_POINTERS];
+
+ void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale,
+ float windowYScale);
+ void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
+ float windowYScale);
+
+ /**
+ * Returns whether the default pointer information should be used. This will be true when the
+ * InputTarget doesn't have any bits set in the pointerIds bitset. This can happen for monitors
+ * and non splittable windows since we want all pointers for the EventEntry to go to this
+ * target.
+ */
+ bool useDefaultPointerInfo() const;
+
+ /**
+ * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is
+ * true.
+ */
+ const PointerInfo& getDefaultPointerInfo() const;
+
+ std::string getPointerInfoString() const;
};
std::string dispatchModeToString(int32_t dispatchMode);
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index c567c8b..f42ddcf 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -209,6 +209,8 @@
return AMOTION_EVENT_TOOL_TYPE_FINGER;
case MT_TOOL_PEN:
return AMOTION_EVENT_TOOL_TYPE_STYLUS;
+ case MT_TOOL_PALM:
+ return AMOTION_EVENT_TOOL_TYPE_PALM;
}
}
return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
@@ -247,6 +249,14 @@
continue;
}
+ if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
+ if (!mCurrentMotionAborted) {
+ ALOGI("Canceling touch gesture from device %s because the palm event was detected",
+ getDeviceName().c_str());
+ cancelTouch(when);
+ }
+ }
+
if (outCount >= MAX_POINTERS) {
#if DEBUG_POINTERS
ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index c262abb..98ebf50 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1571,4 +1571,107 @@
consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
}
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) {
+ mWindow2->setWindowScale(0.5f, 0.5f);
+
+ // Touch Window 1
+ std::vector<PointF> touchedPoints = {PointF{10, 10}};
+ std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+ // Touch Window 2
+ int32_t actionPointerDown =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchedPoints.emplace_back(PointF{150, 150});
+ expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+ motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Consuming from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) {
+ mWindow2->setWindowScale(0.5f, 0.5f);
+
+ // Touch Window 1
+ std::vector<PointF> touchedPoints = {PointF{10, 10}};
+ std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+ // Touch Window 2
+ int32_t actionPointerDown =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchedPoints.emplace_back(PointF{150, 150});
+ expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+ motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Consuming from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+
+ // Move both windows
+ touchedPoints = {{20, 20}, {175, 175}};
+ expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
+ getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) {
+ mWindow1->setWindowScale(0.5f, 0.5f);
+
+ // Touch Window 1
+ std::vector<PointF> touchedPoints = {PointF{10, 10}};
+ std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+ // Touch Window 2
+ int32_t actionPointerDown =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchedPoints.emplace_back(PointF{150, 150});
+ expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+ motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Consuming from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+
+ // Move both windows
+ touchedPoints = {{20, 20}, {175, 175}};
+ expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
+ getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 1fc8217..a9d7793 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -6798,4 +6798,95 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
}
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
+ addMapperAndConfigure(mapper);
+
+ NotifyMotionArgs motionArgs;
+
+ constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
+ // finger down
+ processId(mapper, 1);
+ processPosition(mapper, x1, y1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+ // finger move
+ processId(mapper, 1);
+ processPosition(mapper, x2, y2);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+ // finger up.
+ processId(mapper, -1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+ // new finger down
+ processId(mapper, 1);
+ processPosition(mapper, x3, y3);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+}
+
+/**
+ * Test touch should be canceled when received the MT_TOOL_PALM event, and the following MOVE and
+ * UP events should be ignored.
+ */
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
+ addMapperAndConfigure(mapper);
+
+ NotifyMotionArgs motionArgs;
+
+ // default tool type is finger
+ constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
+ processId(mapper, 1);
+ processPosition(mapper, x1, y1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+ // Tool changed to MT_TOOL_PALM expect sending the cancel event.
+ processToolType(mapper, MT_TOOL_PALM);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
+
+ // Ignore the following MOVE and UP events if had detect a palm event.
+ processId(mapper, 1);
+ processPosition(mapper, x2, y2);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // finger up.
+ processId(mapper, -1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // new finger down
+ processToolType(mapper, MT_TOOL_FINGER);
+ processId(mapper, 1);
+ processPosition(mapper, x3, y3);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 9154191..e8c47a5 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -42,7 +42,7 @@
kMinimumSamplesForPrediction(minimumSamplesForPrediction),
kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
mIdealPeriod(idealPeriod) {
- mRateMap[mIdealPeriod] = {idealPeriod, 0};
+ resetModel();
}
inline size_t VSyncPredictor::next(int i) const {
@@ -203,6 +203,10 @@
mRateMap[mIdealPeriod] = {period, 0};
}
+ clearTimestamps();
+}
+
+void VSyncPredictor::clearTimestamps() {
if (!timestamps.empty()) {
mKnownTimestamp = *std::max_element(timestamps.begin(), timestamps.end());
timestamps.clear();
@@ -227,6 +231,12 @@
return needsMoreSamples;
}
+void VSyncPredictor::resetModel() {
+ std::lock_guard<std::mutex> lk(mMutex);
+ mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
+ clearTimestamps();
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 4210b3c..41e5469 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -40,6 +40,7 @@
void addVsyncTimestamp(nsecs_t timestamp) final;
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
nsecs_t currentPeriod() const final;
+ void resetModel() final;
/*
* Inform the model that the period is anticipated to change to a new value.
@@ -48,7 +49,7 @@
*
* \param [in] period The new period that should be used.
*/
- void setPeriod(nsecs_t period);
+ 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.
@@ -61,6 +62,7 @@
private:
VSyncPredictor(VSyncPredictor const&) = delete;
VSyncPredictor& operator=(VSyncPredictor const&) = delete;
+ void clearTimestamps() REQUIRES(mMutex);
size_t const kHistorySize;
size_t const kMinimumSamplesForPrediction;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 20b6238..98cec71 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -199,7 +199,9 @@
return mTracker->currentPeriod();
}
-void VSyncReactor::beginResync() {}
+void VSyncReactor::beginResync() {
+ mTracker->resetModel();
+}
void VSyncReactor::endResync() {}
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 6be63fe..2b27884 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -61,6 +61,9 @@
*/
virtual void setPeriod(nsecs_t period) = 0;
+ /* Inform the tracker that the samples it has are not accurate for prediction. */
+ virtual void resetModel() = 0;
+
protected:
VSyncTracker(VSyncTracker const&) = delete;
VSyncTracker& operator=(VSyncTracker const&) = delete;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1796476..06a9b1f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -494,6 +494,7 @@
ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
mFrameTracer->initialize();
+ mTimeStats->onBootFinished();
// wait patiently for the window manager death
const String16 name("window");
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index 20c2218..7ff2594 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -8,12 +8,18 @@
"libcutils",
"liblog",
"libprotobuf-cpp-lite",
+ "libstatslog",
+ "libstatspull",
+ "libstatssocket",
"libtimestats_proto",
"libui",
"libutils",
],
export_include_dirs: ["."],
export_shared_lib_headers: [
+ "libstatslog",
+ "libstatspull",
+ "libstatssocket",
"libtimestats_proto",
],
cppflags: [
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 0939fa4..44a59fd 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -36,11 +36,50 @@
namespace impl {
-TimeStats::TimeStats() {
+status_pull_atom_return_t TimeStats::pullGlobalAtomCallback(int32_t atom_tag,
+ pulled_stats_event_list* data,
+ void* cookie) {
+ impl::TimeStats* timeStats = reinterpret_cast<impl::TimeStats*>(cookie);
+ if (atom_tag != android::util::SURFACEFLINGER_STATS_GLOBAL_INFO) {
+ return STATS_PULL_SKIP;
+ }
+
+ std::lock_guard<std::mutex> lock(timeStats->mMutex);
+
+ const auto& stats = timeStats->mTimeStats;
+ if (stats.statsStart == 0) {
+ return STATS_PULL_SKIP;
+ }
+ timeStats->flushPowerTimeLocked();
+
+ struct stats_event* event = timeStats->mStatsDelegate->addStatsEventToPullData(data);
+ timeStats->mStatsDelegate->statsEventSetAtomId(event,
+ android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+ timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.totalFrames);
+ timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.missedFrames);
+ timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.clientCompositionFrames);
+ timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.displayOnTime);
+ timeStats->mStatsDelegate->statsEventWriteInt64(event, stats.presentToPresent.totalTime());
+ timeStats->mStatsDelegate->statsEventBuild(event);
+ timeStats->clearGlobalLocked();
+
+ return STATS_PULL_SUCCESS;
+}
+
+TimeStats::TimeStats() : TimeStats(nullptr) {}
+
+TimeStats::TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate) {
+ if (statsDelegate != nullptr) {
+ mStatsDelegate = std::move(statsDelegate);
+ }
+}
+
+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.
- // TODO: remove this.
+ // during testing without enabling by default.
+ // TODO: remove this, as we should only be paying this overhead on devices
+ // where statsd exists.
enable();
}
@@ -69,7 +108,7 @@
}
if (argsMap.count("-clear")) {
- clear();
+ clearAll();
}
if (argsMap.count("-enable")) {
@@ -594,6 +633,8 @@
mEnabled.store(true);
mTimeStats.statsStart = static_cast<int64_t>(std::time(0));
mPowerTime.prevTime = systemTime();
+ mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ TimeStats::pullGlobalAtomCallback, nullptr, this);
ALOGD("Enabled");
}
@@ -606,15 +647,20 @@
flushPowerTimeLocked();
mEnabled.store(false);
mTimeStats.statsEnd = static_cast<int64_t>(std::time(0));
+ mStatsDelegate->unregisterStatsPullAtomCallback(
+ android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
ALOGD("Disabled");
}
-void TimeStats::clear() {
+void TimeStats::clearAll() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ clearGlobalLocked();
+ clearLayersLocked();
+}
+
+void TimeStats::clearGlobalLocked() {
ATRACE_CALL();
- std::lock_guard<std::mutex> lock(mMutex);
- mTimeStatsTracker.clear();
- mTimeStats.stats.clear();
mTimeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
mTimeStats.statsEnd = 0;
mTimeStats.totalFrames = 0;
@@ -628,7 +674,15 @@
mPowerTime.prevTime = systemTime();
mGlobalRecord.prevPresentTime = 0;
mGlobalRecord.presentFences.clear();
- ALOGD("Cleared");
+ ALOGD("Cleared global stats");
+}
+
+void TimeStats::clearLayersLocked() {
+ ATRACE_CALL();
+
+ mTimeStatsTracker.clear();
+ mTimeStats.stats.clear();
+ ALOGD("Cleared layer stats");
}
bool TimeStats::isEnabled() {
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 65e5cf4..5cd421c 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -17,6 +17,9 @@
#pragma once
#include <hardware/hwcomposer_defs.h>
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
+#include <statslog.h>
#include <timestatsproto/TimeStatsHelper.h>
#include <timestatsproto/TimeStatsProtoHeader.h>
#include <ui/FenceTime.h>
@@ -37,6 +40,10 @@
public:
virtual ~TimeStats() = default;
+ // Called once boot has been finished to perform additional capabilities,
+ // e.g. registration to statsd.
+ virtual void onBootFinished() = 0;
+
virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
virtual bool isEnabled() = 0;
virtual std::string miniDump() = 0;
@@ -131,6 +138,39 @@
public:
TimeStats();
+ // Delegate to the statsd service and associated APIs.
+ // Production code may use this class directly, whereas unit test may define
+ // a subclass for ease of testing.
+ class StatsEventDelegate {
+ public:
+ virtual ~StatsEventDelegate() = default;
+ virtual struct stats_event* addStatsEventToPullData(pulled_stats_event_list* data) {
+ return add_stats_event_to_pull_data(data);
+ }
+ virtual void registerStatsPullAtomCallback(int32_t atom_tag,
+ stats_pull_atom_callback_t callback,
+ pull_atom_metadata* metadata, void* cookie) {
+ return register_stats_pull_atom_callback(atom_tag, callback, metadata, cookie);
+ }
+
+ virtual void unregisterStatsPullAtomCallback(int32_t atom_tag) {
+ return unregister_stats_pull_atom_callback(atom_tag);
+ }
+
+ virtual void statsEventSetAtomId(struct stats_event* event, uint32_t atom_id) {
+ return stats_event_set_atom_id(event, atom_id);
+ }
+
+ virtual void statsEventWriteInt64(struct stats_event* event, int64_t field) {
+ return stats_event_write_int64(event, field);
+ }
+
+ virtual void statsEventBuild(struct stats_event* event) { return stats_event_build(event); }
+ };
+ // For testing only for injecting custom dependencies.
+ TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate);
+
+ void onBootFinished() override;
void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
bool isEnabled() override;
std::string miniDump() override;
@@ -167,6 +207,9 @@
static const size_t MAX_NUM_TIME_RECORDS = 64;
private:
+ static status_pull_atom_return_t pullGlobalAtomCallback(int32_t atom_tag,
+ pulled_stats_event_list* data,
+ void* cookie);
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerId);
void flushPowerTimeLocked();
@@ -174,7 +217,9 @@
void enable();
void disable();
- void clear();
+ void clearAll();
+ void clearGlobalLocked();
+ void clearLayersLocked();
void dump(bool asProto, std::optional<uint32_t> maxLayers, std::string& result);
std::atomic<bool> mEnabled = false;
@@ -187,6 +232,7 @@
static const size_t MAX_NUM_LAYER_RECORDS = 200;
static const size_t MAX_NUM_LAYER_STATS = 200;
+ std::unique_ptr<StatsEventDelegate> mStatsDelegate = std::make_unique<StatsEventDelegate>();
};
} // namespace impl
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index becf484..e500672 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -83,6 +83,7 @@
"perfetto_trace_protos",
],
shared_libs: [
+ "libstatssocket",
"libsurfaceflinger",
"libtimestats",
"libtimestats_proto",
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index bcf3ba8..c667080 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -40,7 +40,9 @@
namespace android {
namespace {
+using testing::_;
using testing::Contains;
+using testing::InSequence;
using testing::SizeIs;
using testing::UnorderedElementsAre;
@@ -136,7 +138,40 @@
}
std::mt19937 mRandomEngine = std::mt19937(std::random_device()());
- std::unique_ptr<TimeStats> mTimeStats = std::make_unique<impl::TimeStats>();
+
+ class FakeStatsEventDelegate : public impl::TimeStats::StatsEventDelegate {
+ public:
+ FakeStatsEventDelegate() = default;
+ ~FakeStatsEventDelegate() override = default;
+
+ struct stats_event* addStatsEventToPullData(pulled_stats_event_list*) override {
+ return mEvent;
+ }
+ void registerStatsPullAtomCallback(int32_t atom_tag, stats_pull_atom_callback_t callback,
+ pull_atom_metadata*, void* cookie) override {
+ mAtomTag = atom_tag;
+ mCallback = callback;
+ mCookie = cookie;
+ }
+
+ status_pull_atom_return_t makePullAtomCallback(int32_t atom_tag, void* cookie) {
+ return (*mCallback)(atom_tag, nullptr, cookie);
+ }
+
+ MOCK_METHOD1(unregisterStatsPullAtomCallback, void(int32_t));
+ MOCK_METHOD2(statsEventSetAtomId, void(struct stats_event*, uint32_t));
+ MOCK_METHOD2(statsEventWriteInt64, void(struct stats_event*, int64_t));
+ MOCK_METHOD1(statsEventBuild, void(struct stats_event*));
+
+ struct stats_event* mEvent = stats_event_obtain();
+ int32_t mAtomTag = 0;
+ stats_pull_atom_callback_t mCallback = nullptr;
+ void* mCookie = nullptr;
+ };
+
+ FakeStatsEventDelegate* mDelegate = new FakeStatsEventDelegate;
+ std::unique_ptr<TimeStats> mTimeStats =
+ std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate));
};
std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
@@ -213,14 +248,22 @@
return distr(mRandomEngine);
}
-TEST_F(TimeStatsTest, enabledByDefault) {
+TEST_F(TimeStatsTest, disabledByDefault) {
+ ASSERT_FALSE(mTimeStats->isEnabled());
+}
+
+TEST_F(TimeStatsTest, enabledAfterBoot) {
+ mTimeStats->onBootFinished();
ASSERT_TRUE(mTimeStats->isEnabled());
}
TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
ASSERT_TRUE(mTimeStats->isEnabled());
+ EXPECT_EQ(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, mDelegate->mAtomTag);
+ EXPECT_CALL(*mDelegate,
+ unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
EXPECT_TRUE(inputCommand(InputCommand::DISABLE, FMT_STRING).empty());
ASSERT_FALSE(mTimeStats->isEnabled());
}
@@ -631,6 +674,56 @@
ASSERT_EQ(0, globalProto.stats_size());
}
+TEST_F(TimeStatsTest, globalStatsCallback) {
+ constexpr size_t TOTAL_FRAMES = 5;
+ constexpr size_t MISSED_FRAMES = 4;
+ constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
+
+ mTimeStats->onBootFinished();
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ for (size_t i = 0; i < TOTAL_FRAMES; i++) {
+ mTimeStats->incrementTotalFrames();
+ }
+ for (size_t i = 0; i < MISSED_FRAMES; i++) {
+ mTimeStats->incrementMissedFrames();
+ }
+ for (size_t i = 0; i < CLIENT_COMPOSITION_FRAMES; i++) {
+ mTimeStats->incrementClientCompositionFrames();
+ }
+
+ mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
+
+ EXPECT_EQ(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, mDelegate->mAtomTag);
+ EXPECT_NE(nullptr, mDelegate->mCallback);
+ EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+ {
+ InSequence seq;
+ EXPECT_CALL(*mDelegate,
+ statsEventSetAtomId(mDelegate->mEvent,
+ android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, TOTAL_FRAMES));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, MISSED_FRAMES));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, CLIENT_COMPOSITION_FRAMES));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, _));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 2));
+ EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
+ }
+ EXPECT_EQ(STATS_PULL_SUCCESS,
+ mDelegate->makePullAtomCallback(mDelegate->mAtomTag, mDelegate->mCookie));
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ EXPECT_EQ(0, globalProto.total_frames());
+ EXPECT_EQ(0, globalProto.missed_frames());
+ EXPECT_EQ(0, globalProto.client_composition_frames());
+ EXPECT_EQ(0, globalProto.present_to_present_size());
+}
+
TEST_F(TimeStatsTest, canSurviveMonkey) {
if (g_noSlowTests) {
GTEST_SKIP();
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index b51a025..acf852d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -50,6 +50,7 @@
nsecs_t currentPeriod() const final { return mPeriod; }
void setPeriod(nsecs_t) final {}
+ void resetModel() final {}
private:
nsecs_t const mPeriod;
@@ -83,6 +84,7 @@
}
void setPeriod(nsecs_t) final {}
+ void resetModel() final {}
private:
std::mutex mutable mMutex;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index f5649ee..70c9225 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -45,6 +45,7 @@
MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
+ MOCK_METHOD0(resetModel, void());
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
if (timePoint % mPeriod == 0) {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 00d3cc6..6ec3844 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -355,6 +355,21 @@
EXPECT_THAT(prediction, Ge(timePoint));
}
+TEST_F(VSyncPredictorTest, resetsWhenInstructed) {
+ auto const idealPeriod = 10000;
+ auto const realPeriod = 10500;
+ tracker.setPeriod(idealPeriod);
+ for (auto i = 0; i < kMinimumSamplesForPrediction; i++) {
+ tracker.addVsyncTimestamp(i * realPeriod);
+ }
+
+ EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
+ IsCloseTo(realPeriod, mMaxRoundingError));
+ tracker.resetModel();
+ EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
+ IsCloseTo(idealPeriod, mMaxRoundingError));
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 188adea..ce1fafe 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -39,6 +39,7 @@
MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
+ MOCK_METHOD0(resetModel, void());
};
class VSyncTrackerWrapper : public VSyncTracker {
@@ -50,7 +51,8 @@
return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
}
nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
- void setPeriod(nsecs_t period) { mTracker->setPeriod(period); }
+ void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); }
+ void resetModel() final { mTracker->resetModel(); }
private:
std::shared_ptr<VSyncTracker> const mTracker;
@@ -559,6 +561,11 @@
mReactor.addEventListener(mName, negativePhase, &outerCb, lastCallbackTime);
}
+TEST_F(VSyncReactorTest, beginResyncResetsModel) {
+ EXPECT_CALL(*mMockTracker, resetModel());
+ mReactor.beginResync();
+}
+
using VSyncReactorDeathTest = VSyncReactorTest;
TEST_F(VSyncReactorDeathTest, invalidRemoval) {
mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index ec74a42..9ada5ef 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -28,6 +28,7 @@
TimeStats();
~TimeStats() override;
+ MOCK_METHOD0(onBootFinished, void());
MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
MOCK_METHOD0(isEnabled, bool());
MOCK_METHOD0(miniDump, std::string());