Merge "Fix Process_LockedKeysShouldToggleAfterReattach test failure"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 5db44c7..307e21c 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -53,6 +53,9 @@
},
{
"include-filter": "*RelativeZTest.*"
+ },
+ {
+ "include-filter": "*RefreshRateOverlayTest.*"
}
]
},
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index c8277fe..91d5524 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1350,7 +1350,7 @@
static void DumpHals(int out_fd = STDOUT_FILENO) {
if (!ds.IsZipping()) {
RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"},
- CommandOptions::WithTimeout(10).AsRootIfAvailable().Build(),
+ CommandOptions::WithTimeout(60).AsRootIfAvailable().Build(),
false, out_fd);
return;
}
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index c8355e2..7aac7da 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -373,7 +373,6 @@
outReturn->clear();
for (const std::string& instance : allInstances) {
- // TODO(b/169275998): allow checking policy only once for the interface
if (mAccess->canFind(ctx, interface + "/" + instance)) {
outReturn->push_back(instance);
}
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 7372022..36097d6 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -17,6 +17,7 @@
#ifndef _UI_INPUT_WINDOW_H
#define _UI_INPUT_WINDOW_H
+#include <android/os/TouchOcclusionMode.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <input/Flags.h>
@@ -30,6 +31,8 @@
#include "InputApplication.h"
+using android::os::TouchOcclusionMode;
+
namespace android {
/*
@@ -158,6 +161,10 @@
// in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis.
float globalScaleFactor = 1.0f;
+ // The opacity of this window, from 0.0 to 1.0 (inclusive).
+ // An alpha of 1.0 means fully opaque and 0.0 means fully transparent.
+ float alpha;
+
// Transform applied to individual windows.
ui::Transform transform;
@@ -176,8 +183,10 @@
* motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED.
*/
bool trustedOverlay = false;
+ TouchOcclusionMode touchOcclusionMode = TouchOcclusionMode::BLOCK_UNTRUSTED;
int32_t ownerPid = -1;
int32_t ownerUid = -1;
+ std::string packageName;
Flags<Feature> inputFeatures;
int32_t displayId = ADISPLAY_ID_NONE;
int32_t portalToDisplayId = ADISPLAY_ID_NONE;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 06f6249..ddd9f9b 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -520,14 +520,19 @@
// Write RPC headers. (previously just the interface token)
status_t Parcel::writeInterfaceToken(const String16& interface)
{
+ return writeInterfaceToken(interface.string(), interface.size());
+}
+
+status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) {
const IPCThreadState* threadState = IPCThreadState::self();
writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
updateWorkSourceRequestHeaderPosition();
writeInt32(threadState->shouldPropagateWorkSource() ?
threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource);
writeInt32(kHeader);
+
// currently the interface identification token is just its name as a string
- return writeString16(interface);
+ return writeString16(str, len);
}
bool Parcel::replaceCallingWorkSourceUid(uid_t uid)
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index b6cfb8e..fbfd6c5 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -86,6 +86,7 @@
// Writes the RPC header.
status_t writeInterfaceToken(const String16& interface);
+ status_t writeInterfaceToken(const char16_t* str, size_t len);
// Parses the RPC header, returning true if the interface name
// in the header matches the expected interface from the caller.
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index ca03397..9508de0 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -99,6 +99,7 @@
if (set) {
// if this is a local object, it's not one known to libbinder_ndk
mClazz = clazz;
+ return true;
}
return {};
@@ -127,7 +128,7 @@
return false;
}
- return associateClassInternal(clazz, newDescriptor, true).value_or(true);
+ return associateClassInternal(clazz, newDescriptor, true).value();
}
ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData)
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index cce434a..87f7972 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -18,7 +18,7 @@
#define LOG_TAG "BLASTBufferQueue"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-// #define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#include <gui/BLASTBufferQueue.h>
#include <gui/BufferItemConsumer.h>
@@ -252,8 +252,8 @@
}
if (rejectBuffer(bufferItem)) {
- BQA_LOGE("rejecting buffer: configured width=%d, height=%d, buffer{w=%d, h=%d}", mWidth,
- mHeight, buffer->getWidth(), buffer->getHeight());
+ BQA_LOGE("rejecting buffer:configured size=%dx%d, buffer{size=%dx%d transform=%d}", mWidth,
+ mHeight, buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform);
mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
return;
}
@@ -330,7 +330,15 @@
return false;
}
+ uint32_t bufWidth = item.mGraphicBuffer->getWidth();
+ uint32_t bufHeight = item.mGraphicBuffer->getHeight();
+
+ // Take the buffer's orientation into account
+ if (item.mTransform & ui::Transform::ROT_90) {
+ std::swap(bufWidth, bufHeight);
+ }
+
// reject buffers if the buffer size doesn't match.
- return item.mGraphicBuffer->getWidth() != mWidth || item.mGraphicBuffer->getHeight() != mHeight;
+ return bufWidth != mWidth || bufHeight != mHeight;
}
} // namespace android
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 35fff0a..9299721 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -490,8 +490,8 @@
IGraphicBufferProducer::QueueBufferOutput qbOutput;
IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
Rect(bufWidth, bufHeight),
- NATIVE_WINDOW_SCALING_MODE_FREEZE, tr,
- Fence::NO_FENCE);
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
+ tr, Fence::NO_FENCE);
igbProducer->queueBuffer(slot, input, &qbOutput);
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 8f575a8..b442700 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -18,6 +18,8 @@
name: "inputconstants_aidl",
srcs: [
"android/os/IInputConstants.aidl",
+ "android/os/TouchOcclusionMode.aidl",
+ "android/os/BlockUntrustedTouchesMode.aidl",
],
}
@@ -71,10 +73,14 @@
"android/FocusRequest.aidl",
"android/InputApplicationInfo.aidl",
"android/os/IInputConstants.aidl",
+ "android/os/TouchOcclusionMode.aidl",
+ "android/os/BlockUntrustedTouchesMode.aidl",
"android/os/IInputFlinger.aidl",
"android/os/ISetInputWindowsListener.aidl",
],
+ export_shared_lib_headers: ["libbinder"],
+
shared_libs: [
"libutils",
"libbinder",
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 885dc9b..8546bbb 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -59,10 +59,11 @@
info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
info.visible == visible && info.trustedOverlay == trustedOverlay &&
- info.focusable == focusable && info.hasWallpaper == hasWallpaper &&
- info.paused == paused && info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
- info.inputFeatures == inputFeatures && info.displayId == displayId &&
- info.portalToDisplayId == portalToDisplayId &&
+ info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode &&
+ info.hasWallpaper == hasWallpaper && info.paused == paused &&
+ info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
+ info.packageName == packageName && info.inputFeatures == inputFeatures &&
+ info.displayId == displayId && info.portalToDisplayId == portalToDisplayId &&
info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
info.applicationInfo == applicationInfo;
}
@@ -91,6 +92,7 @@
parcel->writeInt32(frameBottom) ?:
parcel->writeInt32(surfaceInset) ?:
parcel->writeFloat(globalScaleFactor) ?:
+ parcel->writeFloat(alpha) ?:
parcel->writeFloat(transform.dsdx()) ?:
parcel->writeFloat(transform.dtdx()) ?:
parcel->writeFloat(transform.tx()) ?:
@@ -102,8 +104,10 @@
parcel->writeBool(hasWallpaper) ?:
parcel->writeBool(paused) ?:
parcel->writeBool(trustedOverlay) ?:
+ parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?:
parcel->writeInt32(ownerPid) ?:
parcel->writeInt32(ownerUid) ?:
+ parcel->writeUtf8AsUtf16(packageName) ?:
parcel->writeInt32(inputFeatures.get()) ?:
parcel->writeInt32(displayId) ?:
parcel->writeInt32(portalToDisplayId) ?:
@@ -134,6 +138,7 @@
flags = Flags<Flag>(parcel->readInt32());
type = static_cast<Type>(parcel->readInt32());
float dsdx, dtdx, tx, dtdy, dsdy, ty;
+ int32_t touchOcclusionModeInt;
// clang-format off
status = parcel->readInt32(&frameLeft) ?:
parcel->readInt32(&frameTop) ?:
@@ -141,6 +146,7 @@
parcel->readInt32(&frameBottom) ?:
parcel->readInt32(&surfaceInset) ?:
parcel->readFloat(&globalScaleFactor) ?:
+ parcel->readFloat(&alpha) ?:
parcel->readFloat(&dsdx) ?:
parcel->readFloat(&dtdx) ?:
parcel->readFloat(&tx) ?:
@@ -152,14 +158,18 @@
parcel->readBool(&hasWallpaper) ?:
parcel->readBool(&paused) ?:
parcel->readBool(&trustedOverlay) ?:
+ parcel->readInt32(&touchOcclusionModeInt) ?:
parcel->readInt32(&ownerPid) ?:
- parcel->readInt32(&ownerUid);
+ parcel->readInt32(&ownerUid) ?:
+ parcel->readUtf8FromUtf16(&packageName);
// clang-format on
if (status != OK) {
return status;
}
+ touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt);
+
inputFeatures = Flags<Feature>(parcel->readInt32());
status = parcel->readInt32(&displayId) ?:
parcel->readInt32(&portalToDisplayId) ?:
diff --git a/libs/input/android/os/BlockUntrustedTouchesMode.aidl b/libs/input/android/os/BlockUntrustedTouchesMode.aidl
new file mode 100644
index 0000000..9504e99
--- /dev/null
+++ b/libs/input/android/os/BlockUntrustedTouchesMode.aidl
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+
+/**
+ * Block untrusted touches feature mode.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum BlockUntrustedTouchesMode {
+ /** Feature is off. */
+ DISABLED,
+
+ /** Untrusted touches are flagged but not blocked. */
+ PERMISSIVE,
+
+ /** Untrusted touches are blocked. */
+ BLOCK
+}
diff --git a/libs/input/android/os/TouchOcclusionMode.aidl b/libs/input/android/os/TouchOcclusionMode.aidl
new file mode 100644
index 0000000..106f159
--- /dev/null
+++ b/libs/input/android/os/TouchOcclusionMode.aidl
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+
+/**
+ * Touch occlusion modes: These modes represent how windows are taken into
+ * consideration in order to decide whether to block obscured touches or
+ * not.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum TouchOcclusionMode {
+ /**
+ * Touches that pass through this window will be blocked if they are
+ * consumed by a different UID and this window is not trusted.
+ */
+ BLOCK_UNTRUSTED,
+
+ /**
+ * The window's opacity will be taken into consideration for touch
+ * occlusion rules if the touch passes through it and the window is not
+ * trusted.
+ */
+ USE_OPACITY,
+
+ /**
+ * The window won't count for touch occlusion rules if the touch passes
+ * through it.
+ */
+ ALLOW
+}
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index 65a7761..c18a17f 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -53,13 +53,16 @@
i.frameBottom = 19;
i.surfaceInset = 17;
i.globalScaleFactor = 0.3;
+ i.alpha = 0.7;
i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
i.visible = false;
i.focusable = false;
i.hasWallpaper = false;
i.paused = false;
+ i.touchOcclusionMode = TouchOcclusionMode::ALLOW;
i.ownerPid = 19;
i.ownerUid = 24;
+ i.packageName = "com.example.package";
i.inputFeatures = InputWindowInfo::Feature::DISABLE_USER_ACTIVITY;
i.displayId = 34;
i.portalToDisplayId = 2;
@@ -86,13 +89,16 @@
ASSERT_EQ(i.frameBottom, i2.frameBottom);
ASSERT_EQ(i.surfaceInset, i2.surfaceInset);
ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
+ ASSERT_EQ(i.alpha, i2.alpha);
ASSERT_EQ(i.transform, i2.transform);
ASSERT_EQ(i.visible, i2.visible);
ASSERT_EQ(i.focusable, i2.focusable);
ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
ASSERT_EQ(i.paused, i2.paused);
+ ASSERT_EQ(i.touchOcclusionMode, i2.touchOcclusionMode);
ASSERT_EQ(i.ownerPid, i2.ownerPid);
ASSERT_EQ(i.ownerUid, i2.ownerUid);
+ ASSERT_EQ(i.packageName, i2.packageName);
ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
ASSERT_EQ(i.displayId, i2.displayId);
ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId);
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index ca16d2c..a637796 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -47,8 +47,8 @@
// DataSpace::UNKNOWN otherwise.
ui::Dataspace outputDataspace = ui::Dataspace::UNKNOWN;
- // Additional color transform to apply in linear space after transforming
- // to the output dataspace.
+ // Additional color transform to apply after transforming to the output
+ // dataspace, in non-linear space.
mat4 colorTransform = mat4();
// Region that will be cleared to (0, 0, 0, 1) prior to rendering.
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 1719cd8..69ad189 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -20,13 +20,13 @@
#define LOG_TAG "RenderEngine"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include "SkiaGLRenderEngine.h"
-
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GrContextOptions.h>
#include <SkCanvas.h>
+#include <SkColorFilter.h>
+#include <SkColorMatrix.h>
#include <SkColorSpace.h>
#include <SkImage.h>
#include <SkImageFilters.h>
@@ -40,6 +40,7 @@
#include <cmath>
#include "../gl/GLExtensions.h"
+#include "SkiaGLRenderEngine.h"
#include "filters/BlurFilter.h"
extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
@@ -395,6 +396,13 @@
}
}
+static SkColorMatrix toSkColorMatrix(const mat4& matrix) {
+ return SkColorMatrix(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], 0, matrix[0][1],
+ matrix[1][1], matrix[2][1], matrix[3][1], 0, matrix[0][2], matrix[1][2],
+ matrix[2][2], matrix[3][2], 0, matrix[0][3], matrix[1][3], matrix[2][3],
+ matrix[3][3], 0);
+}
+
void SkiaGLRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
std::lock_guard<std::mutex> lock(mRenderingMutex);
mImageCache.erase(bufferId);
@@ -566,6 +574,8 @@
paint.setColor(SkColor4f{.fR = color.r, .fG = color.g, .fB = color.b, layer->alpha});
}
+ paint.setColorFilter(SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)));
+
// Layers have a local transform matrix that should be applied to them.
canvas->save();
canvas->concat(getSkM44(layer->geometry.positionTransform));
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 3ccb0c9..8b4ae03 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -402,6 +402,7 @@
// To avoid leaking stack in case that call never comes, and for tests,
// initialize it here anyways.
mInTouchMode(true),
+ mMaximumObscuringOpacityForTouch(1.0f),
mFocusedDisplayId(ADISPLAY_ID_DEFAULT) {
mLooper = new Looper(false);
mReporter = createInputReporter();
@@ -481,6 +482,33 @@
}
/**
+ * Raise ANR if there is no focused window.
+ * Before the ANR is raised, do a final state check:
+ * 1. The currently focused application must be the same one we are waiting for.
+ * 2. Ensure we still don't have a focused window.
+ */
+void InputDispatcher::processNoFocusedWindowAnrLocked() {
+ // Check if the application that we are waiting for is still focused.
+ std::shared_ptr<InputApplicationHandle> focusedApplication =
+ getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId);
+ if (focusedApplication == nullptr ||
+ focusedApplication->getApplicationToken() !=
+ mAwaitedFocusedApplication->getApplicationToken()) {
+ // Unexpected because we should have reset the ANR timer when focused application changed
+ ALOGE("Waited for a focused window, but focused application has already changed to %s",
+ focusedApplication->getName().c_str());
+ return; // The focused application has changed.
+ }
+
+ const sp<InputWindowHandle>& focusedWindowHandle =
+ getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId);
+ if (focusedWindowHandle != nullptr) {
+ return; // We now have a focused window. No need for ANR.
+ }
+ onAnrLocked(mAwaitedFocusedApplication);
+}
+
+/**
* Check if any of the connections' wait queues have events that are too old.
* If we waited for events to be ack'ed for more than the window timeout, raise an ANR.
* Return the time at which we should wake up next.
@@ -491,8 +519,9 @@
// Check if we are waiting for a focused window to appear. Raise ANR if waited too long
if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
if (currentTime >= *mNoFocusedWindowTimeoutTime) {
- onAnrLocked(mAwaitedFocusedApplication);
+ processNoFocusedWindowAnrLocked();
mAwaitedFocusedApplication.reset();
+ mNoFocusedWindowTimeoutTime = std::nullopt;
return LONG_LONG_MIN;
} else {
// Keep waiting
@@ -1494,6 +1523,7 @@
DEFAULT_INPUT_DISPATCHING_TIMEOUT);
mNoFocusedWindowTimeoutTime = currentTime + timeout.count();
mAwaitedFocusedApplication = focusedApplicationHandle;
+ mAwaitedApplicationDisplayId = displayId;
ALOGW("Waiting because no window has focus but %s may eventually add a "
"window when it finishes starting up. Will wait for %" PRId64 "ms",
mAwaitedFocusedApplication->getName().c_str(), millis(timeout));
@@ -1708,6 +1738,22 @@
}
}
+ // Drop events that can't be trusted due to occlusion
+ if (newTouchedWindowHandle != nullptr &&
+ mBlockUntrustedTouchesMode != BlockUntrustedTouchesMode::DISABLED) {
+ TouchOcclusionInfo occlusionInfo =
+ computeTouchOcclusionInfoLocked(newTouchedWindowHandle, x, y);
+ // The order of the operands in the 'if' below is important because even if the feature
+ // is not BLOCK we want isTouchTrustedLocked() to execute in order to log details to
+ // logcat.
+ if (!isTouchTrustedLocked(occlusionInfo) &&
+ mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {
+ ALOGW("Dropping untrusted touch event due to %s/%d",
+ occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);
+ newTouchedWindowHandle = nullptr;
+ }
+ }
+
// Also don't send the new touch event to unresponsive gesture monitors
newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors);
@@ -2109,9 +2155,9 @@
auto otherInfo = otherHandle->getInfo();
if (!otherInfo->visible) {
return false;
- } else if (info->ownerPid == otherInfo->ownerPid) {
- // If ownerPid is the same we don't generate occlusion events as there
- // is no in-process security boundary.
+ } else if (info->ownerUid == otherInfo->ownerUid) {
+ // If ownerUid is the same we don't generate occlusion events as there
+ // is no security boundary within an uid.
return false;
} else if (otherInfo->trustedOverlay) {
return false;
@@ -2121,6 +2167,84 @@
return true;
}
+/**
+ * Returns touch occlusion information in the form of TouchOcclusionInfo. To check if the touch is
+ * untrusted, one should check:
+ *
+ * 1. If result.hasBlockingOcclusion is true.
+ * If it's, it means the touch should be blocked due to a window with occlusion mode of
+ * BLOCK_UNTRUSTED.
+ *
+ * 2. If result.obscuringOpacity > mMaximumObscuringOpacityForTouch.
+ * If it is (and 1 is false), then the touch should be blocked because a stack of windows
+ * (possibly only one) with occlusion mode of USE_OPACITY from one UID resulted in a composed
+ * obscuring opacity above the threshold. Note that if there was no window of occlusion mode
+ * USE_OPACITY, result.obscuringOpacity would've been 0 and since
+ * mMaximumObscuringOpacityForTouch >= 0, the condition above would never be true.
+ *
+ * If neither of those is true, then it means the touch can be allowed.
+ */
+InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked(
+ const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const {
+ int32_t displayId = windowHandle->getInfo()->displayId;
+ const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+ TouchOcclusionInfo info;
+ info.hasBlockingOcclusion = false;
+ info.obscuringOpacity = 0;
+ info.obscuringUid = -1;
+ std::map<int32_t, float> opacityByUid;
+ for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
+ if (windowHandle == otherHandle) {
+ break; // All future windows are below us. Exit early.
+ }
+ const InputWindowInfo* otherInfo = otherHandle->getInfo();
+ if (canBeObscuredBy(windowHandle, otherHandle) &&
+ windowHandle->getInfo()->ownerUid != otherInfo->ownerUid &&
+ otherInfo->frameContainsPoint(x, y)) {
+ // canBeObscuredBy() has returned true above, which means this window is untrusted, so
+ // we perform the checks below to see if the touch can be propagated or not based on the
+ // window's touch occlusion mode
+ if (otherInfo->touchOcclusionMode == TouchOcclusionMode::BLOCK_UNTRUSTED) {
+ info.hasBlockingOcclusion = true;
+ info.obscuringUid = otherInfo->ownerUid;
+ info.obscuringPackage = otherInfo->packageName;
+ break;
+ }
+ if (otherInfo->touchOcclusionMode == TouchOcclusionMode::USE_OPACITY) {
+ uint32_t uid = otherInfo->ownerUid;
+ float opacity =
+ (opacityByUid.find(uid) == opacityByUid.end()) ? 0 : opacityByUid[uid];
+ // Given windows A and B:
+ // opacity(A, B) = 1 - [1 - opacity(A)] * [1 - opacity(B)]
+ opacity = 1 - (1 - opacity) * (1 - otherInfo->alpha);
+ opacityByUid[uid] = opacity;
+ if (opacity > info.obscuringOpacity) {
+ info.obscuringOpacity = opacity;
+ info.obscuringUid = uid;
+ info.obscuringPackage = otherInfo->packageName;
+ }
+ }
+ }
+ }
+ return info;
+}
+
+bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const {
+ if (occlusionInfo.hasBlockingOcclusion) {
+ ALOGW("Untrusted touch due to occlusion by %s/%d", occlusionInfo.obscuringPackage.c_str(),
+ occlusionInfo.obscuringUid);
+ return false;
+ }
+ if (occlusionInfo.obscuringOpacity > mMaximumObscuringOpacityForTouch) {
+ ALOGW("Untrusted touch due to occlusion by %s/%d (obscuring opacity = "
+ "%.2f, maximum allowed = %.2f)",
+ occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid,
+ occlusionInfo.obscuringOpacity, mMaximumObscuringOpacityForTouch);
+ return false;
+ }
+ return true;
+}
+
bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
int32_t x, int32_t y) const {
int32_t displayId = windowHandle->getInfo()->displayId;
@@ -4060,6 +4184,21 @@
mInTouchMode = inTouchMode;
}
+void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) {
+ if (opacity < 0 || opacity > 1) {
+ LOG_ALWAYS_FATAL("Maximum obscuring opacity for touch should be >= 0 and <= 1");
+ return;
+ }
+
+ std::scoped_lock lock(mLock);
+ mMaximumObscuringOpacityForTouch = opacity;
+}
+
+void InputDispatcher::setBlockUntrustedTouchesMode(BlockUntrustedTouchesMode mode) {
+ std::scoped_lock lock(mLock);
+ mBlockUntrustedTouchesMode = mode;
+}
+
bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
if (fromToken == toToken) {
if (DEBUG_FOCUS) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 4fcdcc2..1772ad8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -112,6 +112,8 @@
virtual void setInputDispatchMode(bool enabled, bool frozen) override;
virtual void setInputFilterEnabled(bool enabled) override;
virtual void setInTouchMode(bool inTouchMode) override;
+ virtual void setMaximumObscuringOpacityForTouch(float opacity) override;
+ virtual void setBlockUntrustedTouchesMode(BlockUntrustedTouchesMode mode) override;
virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
const sp<IBinder>& toToken) override;
@@ -296,6 +298,8 @@
bool mDispatchFrozen GUARDED_BY(mLock);
bool mInputFilterEnabled GUARDED_BY(mLock);
bool mInTouchMode GUARDED_BY(mLock);
+ float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock);
+ BlockUntrustedTouchesMode mBlockUntrustedTouchesMode GUARDED_BY(mLock);
std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
GUARDED_BY(mLock);
@@ -390,6 +394,11 @@
* Used to raise an ANR when we have no focused window.
*/
std::shared_ptr<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock);
+ /**
+ * The displayId that the focused application is associated with.
+ */
+ int32_t mAwaitedApplicationDisplayId GUARDED_BY(mLock);
+ void processNoFocusedWindowAnrLocked() REQUIRES(mLock);
/**
* This map will store the pending focus requests that cannot be currently processed. This can
@@ -446,6 +455,17 @@
void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
const InjectionState* injectionState);
+
+ struct TouchOcclusionInfo {
+ bool hasBlockingOcclusion;
+ float obscuringOpacity;
+ std::string obscuringPackage;
+ int32_t obscuringUid;
+ };
+
+ TouchOcclusionInfo computeTouchOcclusionInfoLocked(const sp<InputWindowHandle>& windowHandle,
+ int32_t x, int32_t y) const REQUIRES(mLock);
+ bool isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const REQUIRES(mLock);
bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x,
int32_t y) const REQUIRES(mLock);
bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 67d9a06..65687c4 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -20,12 +20,15 @@
#include <InputListener.h>
#include <android-base/result.h>
#include <android/FocusRequest.h>
+#include <android/os/BlockUntrustedTouchesMode.h>
#include <android/os/ISetInputWindowsListener.h>
#include <input/InputApplication.h>
#include <input/InputTransport.h>
#include <input/InputWindow.h>
#include <unordered_map>
+using android::os::BlockUntrustedTouchesMode;
+
namespace android {
/*
@@ -145,6 +148,21 @@
*/
virtual void setInTouchMode(bool inTouchMode) = 0;
+ /**
+ * Sets the maximum allowed obscuring opacity by UID to propagate touches.
+ * For certain window types (eg. SAWs), the decision of honoring
+ * FLAG_NOT_TOUCHABLE or not depends on the combined obscuring opacity of
+ * the windows above the touch-consuming window.
+ */
+ virtual void setMaximumObscuringOpacityForTouch(float opacity) = 0;
+
+ /**
+ * Sets the mode of the block untrusted touches feature.
+ *
+ * TODO(b/169067926): Clean-up feature modes.
+ */
+ virtual void setBlockUntrustedTouchesMode(BlockUntrustedTouchesMode mode) = 0;
+
/* Transfers touch focus from one window to another window.
*
* Returns true on success. False if the window did not actually have touch focus.
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 70f872a..766dc90 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -3483,6 +3483,68 @@
mFocusedWindow->assertNoEvents();
}
+/**
+ * If we have no focused window, and a key comes in, we start the ANR timer.
+ * The focused application should add a focused window before the timer runs out to prevent ANR.
+ *
+ * If the user touches another application during this time, the key should be dropped.
+ * Next, if a new focused window comes in, without toggling the focused application,
+ * then no ANR should occur.
+ *
+ * Normally, we would expect the new focused window to be accompanied by 'setFocusedApplication',
+ * but in some cases the policy may not update the focused application.
+ */
+TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) {
+ std::shared_ptr<FakeApplicationHandle> focusedApplication =
+ std::make_shared<FakeApplicationHandle>();
+ focusedApplication->setDispatchingTimeout(60ms);
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication);
+ // The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused.
+ mFocusedWindow->setFocusable(false);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ mFocusedWindow->consumeFocusEvent(false);
+
+ // Send a key. The ANR timer should start because there is no focused window.
+ // 'focusedApplication' will get blamed if this timer completes.
+ // Key will not be sent anywhere because we have no focused window. It will remain pending.
+ int32_t result =
+ injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
+ INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/);
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result);
+
+ // Wait until dispatcher starts the "no focused window" timer. If we don't wait here,
+ // then the injected touches won't cause the focused event to get dropped.
+ // The dispatcher only checks for whether the queue should be pruned upon queueing.
+ // If we inject the touch right away and the ANR timer hasn't started, the touch event would
+ // simply be added to the queue without 'shouldPruneInboundQueueLocked' returning 'true'.
+ // For this test, it means that the key would get delivered to the window once it becomes
+ // focused.
+ std::this_thread::sleep_for(10ms);
+
+ // Touch unfocused window. This should force the pending key to get dropped.
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {UNFOCUSED_WINDOW_LOCATION});
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // We do not consume the motion right away, because that would require dispatcher to first
+ // process (== drop) the key event, and by that time, ANR will be raised.
+ // Set the focused window first.
+ mFocusedWindow->setFocusable(true);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+ setFocusedWindow(mFocusedWindow);
+ mFocusedWindow->consumeFocusEvent(true);
+ // We do not call "setFocusedApplication" here, even though the newly focused window belongs
+ // to another application. This could be a bug / behaviour in the policy.
+
+ mUnfocusedWindow->consumeMotionDown();
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ // Should not ANR because we actually have a focused window. It was just added too slowly.
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyAnrWasNotCalled());
+}
+
// These tests ensure we cannot send touch events to a window that's positioned behind a window
// that has feature NO_INPUT_CHANNEL.
// Layout:
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index a1ccaad..3b7cfb9 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -168,22 +168,16 @@
sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation);
}
- const Rect& displayBounds = getCompositionDisplay()->getState().displaySpace.bounds;
- const int displayWidth = displayBounds.width();
- const int displayHeight = displayBounds.height();
-
if (!orientedDisplaySpaceRect.isValid()) {
- // the destination frame can be invalid if it has never been set,
- // in that case we assume the whole display frame.
- orientedDisplaySpaceRect = Rect(displayWidth, displayHeight);
+ // The destination frame can be invalid if it has never been set,
+ // in that case we assume the whole display size.
+ orientedDisplaySpaceRect = getCompositionDisplay()->getState().displaySpace.bounds;
}
if (layerStackSpaceRect.isEmpty()) {
- // layerStackSpaceRect can be invalid if it has never been set, in that case
- // we assume the whole display size.
- // It's also invalid to have an empty layerStackSpaceRect, so we handle that
- // case in the same way.
- layerStackSpaceRect = Rect(displayWidth, displayHeight);
+ // The layerStackSpaceRect can be invalid if it has never been set, in that case
+ // we assume the whole framebuffer size.
+ layerStackSpaceRect = getCompositionDisplay()->getState().framebufferSpace.bounds;
if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
std::swap(layerStackSpaceRect.right, layerStackSpaceRect.bottom);
}
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 9985253..43176a3 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -22,12 +22,127 @@
#include <android-base/stringprintf.h>
#include <utils/Log.h>
#include <utils/Trace.h>
+#include <chrono>
#include <cinttypes>
+#include <numeric>
namespace android::frametimeline::impl {
using base::StringAppendF;
+void dumpTable(std::string& result, TimelineItem predictions, TimelineItem actuals,
+ const std::string& indent, PredictionState predictionState, nsecs_t baseTime) {
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "\t\t");
+ StringAppendF(&result, " Start time\t\t|");
+ StringAppendF(&result, " End time\t\t|");
+ StringAppendF(&result, " Present time\n");
+ if (predictionState == PredictionState::Valid) {
+ // Dump the Predictions only if they are valid
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Expected\t|");
+ std::chrono::nanoseconds startTime(predictions.startTime - baseTime);
+ std::chrono::nanoseconds endTime(predictions.endTime - baseTime);
+ std::chrono::nanoseconds presentTime(predictions.presentTime - baseTime);
+ StringAppendF(&result, "\t%10.2f\t|\t%10.2f\t|\t%10.2f\n",
+ std::chrono::duration<double, std::milli>(startTime).count(),
+ std::chrono::duration<double, std::milli>(endTime).count(),
+ std::chrono::duration<double, std::milli>(presentTime).count());
+ }
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Actual \t|");
+
+ if (actuals.startTime == 0) {
+ StringAppendF(&result, "\t\tN/A\t|");
+ } else {
+ std::chrono::nanoseconds startTime(std::max<nsecs_t>(0, actuals.startTime - baseTime));
+ StringAppendF(&result, "\t%10.2f\t|",
+ std::chrono::duration<double, std::milli>(startTime).count());
+ }
+ if (actuals.endTime == 0) {
+ StringAppendF(&result, "\t\tN/A\t|");
+ } else {
+ std::chrono::nanoseconds endTime(actuals.endTime - baseTime);
+ StringAppendF(&result, "\t%10.2f\t|",
+ std::chrono::duration<double, std::milli>(endTime).count());
+ }
+ if (actuals.presentTime == 0) {
+ StringAppendF(&result, "\t\tN/A\n");
+ } else {
+ std::chrono::nanoseconds presentTime(std::max<nsecs_t>(0, actuals.presentTime - baseTime));
+ StringAppendF(&result, "\t%10.2f\n",
+ std::chrono::duration<double, std::milli>(presentTime).count());
+ }
+
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "----------------------");
+ StringAppendF(&result, "----------------------");
+ StringAppendF(&result, "----------------------");
+ StringAppendF(&result, "----------------------\n");
+}
+
+std::string toString(PredictionState predictionState) {
+ switch (predictionState) {
+ case PredictionState::Valid:
+ return "Valid";
+ case PredictionState::Expired:
+ return "Expired";
+ case PredictionState::None:
+ default:
+ return "None";
+ }
+}
+
+std::string toString(JankType jankType) {
+ switch (jankType) {
+ case JankType::None:
+ return "None";
+ case JankType::Display:
+ return "Composer/Display - outside SF and App";
+ case JankType::SurfaceFlingerDeadlineMissed:
+ return "SurfaceFlinger Deadline Missed";
+ case JankType::AppDeadlineMissed:
+ return "App Deadline Missed";
+ case JankType::PredictionExpired:
+ return "Prediction Expired";
+ case JankType::SurfaceFlingerEarlyLatch:
+ return "SurfaceFlinger Early Latch";
+ default:
+ return "Unclassified";
+ }
+}
+
+std::string jankMetadataBitmaskToString(int32_t jankMetadata) {
+ std::vector<std::string> jankInfo;
+
+ if (jankMetadata & EarlyStart) {
+ jankInfo.emplace_back("Early Start");
+ } else if (jankMetadata & LateStart) {
+ jankInfo.emplace_back("Late Start");
+ }
+
+ if (jankMetadata & EarlyFinish) {
+ jankInfo.emplace_back("Early Finish");
+ } else if (jankMetadata & LateFinish) {
+ jankInfo.emplace_back("Late Finish");
+ }
+
+ if (jankMetadata & EarlyPresent) {
+ jankInfo.emplace_back("Early Present");
+ } else if (jankMetadata & LatePresent) {
+ jankInfo.emplace_back("Late Present");
+ }
+ // TODO(b/169876734): add GPU composition metadata here
+
+ if (jankInfo.empty()) {
+ return "None";
+ }
+ return std::accumulate(jankInfo.begin(), jankInfo.end(), std::string(),
+ [](const std::string& l, const std::string& r) {
+ return l.empty() ? r : l + ", " + r;
+ });
+}
+
int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
@@ -69,29 +184,26 @@
mPredictionState(predictionState),
mPredictions(predictions),
mActuals({0, 0, 0}),
- mActualQueueTime(0) {}
+ mActualQueueTime(0),
+ mJankType(JankType::None),
+ mJankMetadata(0) {}
void SurfaceFrame::setPresentState(PresentState state) {
std::lock_guard<std::mutex> lock(mMutex);
mPresentState = state;
}
-PredictionState SurfaceFrame::getPredictionState() {
- std::lock_guard<std::mutex> lock(mMutex);
- return mPredictionState;
-}
-
-SurfaceFrame::PresentState SurfaceFrame::getPresentState() {
+SurfaceFrame::PresentState SurfaceFrame::getPresentState() const {
std::lock_guard<std::mutex> lock(mMutex);
return mPresentState;
}
-TimelineItem SurfaceFrame::getActuals() {
+TimelineItem SurfaceFrame::getActuals() const {
std::lock_guard<std::mutex> lock(mMutex);
return mActuals;
}
-nsecs_t SurfaceFrame::getActualQueueTime() {
+nsecs_t SurfaceFrame::getActualQueueTime() const {
std::lock_guard<std::mutex> lock(mMutex);
return mActualQueueTime;
}
@@ -115,25 +227,74 @@
mActuals.presentTime = presentTime;
}
-void SurfaceFrame::dump(std::string& result) {
+void SurfaceFrame::setJankInfo(JankType jankType, int32_t jankMetadata) {
std::lock_guard<std::mutex> lock(mMutex);
- StringAppendF(&result, "Present State : %d\n", static_cast<int>(mPresentState));
- StringAppendF(&result, "Prediction State : %d\n", static_cast<int>(mPredictionState));
- StringAppendF(&result, "Predicted Start Time : %" PRId64 "\n", mPredictions.startTime);
- StringAppendF(&result, "Actual Start Time : %" PRId64 "\n", mActuals.startTime);
- StringAppendF(&result, "Actual Queue Time : %" PRId64 "\n", mActualQueueTime);
- StringAppendF(&result, "Predicted Render Complete Time : %" PRId64 "\n", mPredictions.endTime);
- StringAppendF(&result, "Actual Render Complete Time : %" PRId64 "\n", mActuals.endTime);
- StringAppendF(&result, "Predicted Present Time : %" PRId64 "\n", mPredictions.presentTime);
- StringAppendF(&result, "Actual Present Time : %" PRId64 "\n", mActuals.presentTime);
+ mJankType = jankType;
+ mJankMetadata = jankMetadata;
}
-FrameTimeline::FrameTimeline() : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()) {}
+JankType SurfaceFrame::getJankType() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mJankType;
+}
+
+nsecs_t SurfaceFrame::getBaseTime() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
+ if (mPredictionState == PredictionState::Valid) {
+ baseTime = std::min(baseTime, mPredictions.startTime);
+ }
+ if (mActuals.startTime != 0) {
+ baseTime = std::min(baseTime, mActuals.startTime);
+ }
+ baseTime = std::min(baseTime, mActuals.endTime);
+ return baseTime;
+}
+
+std::string presentStateToString(SurfaceFrame::PresentState presentState) {
+ using PresentState = SurfaceFrame::PresentState;
+ switch (presentState) {
+ case PresentState::Presented:
+ return "Presented";
+ case PresentState::Dropped:
+ return "Dropped";
+ case PresentState::Unknown:
+ default:
+ return "Unknown";
+ }
+}
+
+void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Layer - %s", mLayerName.c_str());
+ if (mJankType != JankType::None) {
+ // Easily identify a janky Surface Frame in the dump
+ StringAppendF(&result, " [*] ");
+ }
+ StringAppendF(&result, "\n");
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Present State : %s\n", presentStateToString(mPresentState).c_str());
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Jank Type : %s\n", toString(mJankType).c_str());
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Jank Metadata: %s\n",
+ jankMetadataBitmaskToString(mJankMetadata).c_str());
+ dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
+}
+
+FrameTimeline::FrameTimeline()
+ : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()),
+ mMaxDisplayFrames(kDefaultMaxDisplayFrames) {}
FrameTimeline::DisplayFrame::DisplayFrame()
: surfaceFlingerPredictions(TimelineItem()),
surfaceFlingerActuals(TimelineItem()),
- predictionState(PredictionState::None) {
+ predictionState(PredictionState::None),
+ jankType(JankType::None),
+ jankMetadata(0) {
this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
}
@@ -200,10 +361,75 @@
if (signalTime != Fence::SIGNAL_TIME_INVALID) {
auto& displayFrame = pendingPresentFence.second;
displayFrame->surfaceFlingerActuals.presentTime = signalTime;
+
+ // Jank Analysis for DisplayFrame
+ const auto& sfActuals = displayFrame->surfaceFlingerActuals;
+ const auto& sfPredictions = displayFrame->surfaceFlingerPredictions;
+ if (std::abs(sfActuals.presentTime - sfPredictions.presentTime) > kPresentThreshold) {
+ displayFrame->jankMetadata |= sfActuals.presentTime > sfPredictions.presentTime
+ ? LatePresent
+ : EarlyPresent;
+ }
+ if (std::abs(sfActuals.endTime - sfPredictions.endTime) > kDeadlineThreshold) {
+ if (sfActuals.endTime > sfPredictions.endTime) {
+ displayFrame->jankMetadata |= LateFinish;
+ } else {
+ displayFrame->jankMetadata |= EarlyFinish;
+ }
+
+ if (displayFrame->jankMetadata & EarlyFinish & EarlyPresent) {
+ displayFrame->jankType = JankType::SurfaceFlingerEarlyLatch;
+ } else if (displayFrame->jankMetadata & LateFinish & LatePresent) {
+ displayFrame->jankType = JankType::SurfaceFlingerDeadlineMissed;
+ } else if (displayFrame->jankMetadata & EarlyPresent ||
+ displayFrame->jankMetadata & LatePresent) {
+ // Cases where SF finished early but frame was presented late and vice versa
+ displayFrame->jankType = JankType::Display;
+ }
+ }
+ if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) {
+ displayFrame->jankMetadata |=
+ sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart;
+ }
+
for (auto& surfaceFrame : displayFrame->surfaceFrames) {
if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
// Only presented SurfaceFrames need to be updated
surfaceFrame->setActualPresentTime(signalTime);
+
+ // Jank Analysis for SurfaceFrame
+ const auto& predictionState = surfaceFrame->getPredictionState();
+ if (predictionState == PredictionState::Expired) {
+ // Jank analysis cannot be done on apps that don't use predictions
+ surfaceFrame->setJankInfo(JankType::PredictionExpired, 0);
+ continue;
+ } else if (predictionState == PredictionState::Valid) {
+ const auto& actuals = surfaceFrame->getActuals();
+ const auto& predictions = surfaceFrame->getPredictions();
+ int32_t jankMetadata = 0;
+ JankType jankType = JankType::None;
+ if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) {
+ jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish
+ : EarlyFinish;
+ }
+ if (std::abs(actuals.presentTime - predictions.presentTime) >
+ kPresentThreshold) {
+ jankMetadata |= actuals.presentTime > predictions.presentTime
+ ? LatePresent
+ : EarlyPresent;
+ }
+ if (jankMetadata & EarlyPresent) {
+ jankType = JankType::SurfaceFlingerEarlyLatch;
+ } else if (jankMetadata & LatePresent) {
+ if (jankMetadata & EarlyFinish) {
+ // TODO(b/169890654): Classify this properly
+ jankType = JankType::Display;
+ } else {
+ jankType = JankType::AppDeadlineMissed;
+ }
+ }
+ surfaceFrame->setJankInfo(jankType, jankMetadata);
+ }
}
}
}
@@ -214,7 +440,7 @@
}
void FrameTimeline::finalizeCurrentDisplayFrame() {
- while (mDisplayFrames.size() >= kMaxDisplayFrames) {
+ while (mDisplayFrames.size() >= mMaxDisplayFrames) {
// We maintain only a fixed number of frames' data. Pop older frames
mDisplayFrames.pop_front();
}
@@ -223,30 +449,100 @@
mCurrentDisplayFrame = std::make_shared<DisplayFrame>();
}
-void FrameTimeline::dump(std::string& result) {
- std::lock_guard<std::mutex> lock(mMutex);
- StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size());
- for (const auto& displayFrame : mDisplayFrames) {
- StringAppendF(&result, "---Display Frame---\n");
- StringAppendF(&result, "Prediction State : %d\n",
- static_cast<int>(displayFrame->predictionState));
- StringAppendF(&result, "Predicted SF wake time : %" PRId64 "\n",
- displayFrame->surfaceFlingerPredictions.startTime);
- StringAppendF(&result, "Actual SF wake time : %" PRId64 "\n",
- displayFrame->surfaceFlingerActuals.startTime);
- StringAppendF(&result, "Predicted SF Complete time : %" PRId64 "\n",
- displayFrame->surfaceFlingerPredictions.endTime);
- StringAppendF(&result, "Actual SF Complete time : %" PRId64 "\n",
- displayFrame->surfaceFlingerActuals.endTime);
- StringAppendF(&result, "Predicted Present time : %" PRId64 "\n",
- displayFrame->surfaceFlingerPredictions.presentTime);
- StringAppendF(&result, "Actual Present time : %" PRId64 "\n",
- displayFrame->surfaceFlingerActuals.presentTime);
- for (size_t i = 0; i < displayFrame->surfaceFrames.size(); i++) {
- StringAppendF(&result, "Surface frame - %" PRId32 "\n", (int)i);
- displayFrame->surfaceFrames[i]->dump(result);
+nsecs_t FrameTimeline::findBaseTime(const std::shared_ptr<DisplayFrame>& displayFrame) {
+ nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
+ if (displayFrame->predictionState == PredictionState::Valid) {
+ baseTime = std::min(baseTime, displayFrame->surfaceFlingerPredictions.startTime);
+ }
+ baseTime = std::min(baseTime, displayFrame->surfaceFlingerActuals.startTime);
+ for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+ nsecs_t surfaceFrameBaseTime = surfaceFrame->getBaseTime();
+ if (surfaceFrameBaseTime != 0) {
+ baseTime = std::min(baseTime, surfaceFrameBaseTime);
}
}
+ return baseTime;
+}
+
+void FrameTimeline::dumpDisplayFrame(std::string& result,
+ const std::shared_ptr<DisplayFrame>& displayFrame,
+ nsecs_t baseTime) {
+ if (displayFrame->jankType != JankType::None) {
+ // Easily identify a janky Display Frame in the dump
+ StringAppendF(&result, " [*] ");
+ }
+ StringAppendF(&result, "\n");
+ StringAppendF(&result, "Prediction State : %s\n",
+ toString(displayFrame->predictionState).c_str());
+ StringAppendF(&result, "Jank Type : %s\n", toString(displayFrame->jankType).c_str());
+ StringAppendF(&result, "Jank Metadata: %s\n",
+ jankMetadataBitmaskToString(displayFrame->jankMetadata).c_str());
+ dumpTable(result, displayFrame->surfaceFlingerPredictions, displayFrame->surfaceFlingerActuals,
+ "", displayFrame->predictionState, baseTime);
+ StringAppendF(&result, "\n");
+ std::string indent = " "; // 4 spaces
+ for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+ surfaceFrame->dump(result, indent, baseTime);
+ }
+ StringAppendF(&result, "\n");
+}
+void FrameTimeline::dumpAll(std::string& result) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size());
+ nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
+ for (size_t i = 0; i < mDisplayFrames.size(); i++) {
+ StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
+ dumpDisplayFrame(result, mDisplayFrames[i], baseTime);
+ }
+}
+
+void FrameTimeline::dumpJank(std::string& result) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
+ for (size_t i = 0; i < mDisplayFrames.size(); i++) {
+ const auto& displayFrame = mDisplayFrames[i];
+ if (displayFrame->jankType == JankType::None) {
+ // Check if any Surface Frame has been janky
+ bool isJanky = false;
+ for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+ if (surfaceFrame->getJankType() != JankType::None) {
+ isJanky = true;
+ break;
+ }
+ }
+ if (!isJanky) {
+ continue;
+ }
+ }
+ StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
+ dumpDisplayFrame(result, displayFrame, baseTime);
+ }
+}
+void FrameTimeline::parseArgs(const Vector<String16>& args, std::string& result) {
+ ATRACE_CALL();
+ std::unordered_map<std::string, bool> argsMap;
+ for (size_t i = 0; i < args.size(); i++) {
+ argsMap[std::string(String8(args[i]).c_str())] = true;
+ }
+ if (argsMap.count("-jank")) {
+ dumpJank(result);
+ }
+ if (argsMap.count("-all")) {
+ dumpAll(result);
+ }
+}
+
+void FrameTimeline::setMaxDisplayFrames(uint32_t size) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ // The size can either increase or decrease, clear everything, to be consistent
+ mDisplayFrames.clear();
+ mPendingPresentFences.clear();
+ mMaxDisplayFrames = size;
+}
+
+void FrameTimeline::reset() {
+ setMaxDisplayFrames(kDefaultMaxDisplayFrames);
}
} // namespace android::frametimeline::impl
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index cfe8170..bd637df 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -22,10 +22,48 @@
#include <gui/ISurfaceComposer.h>
#include <ui/FenceTime.h>
#include <utils/RefBase.h>
+#include <utils/String16.h>
#include <utils/Timers.h>
+#include <utils/Vector.h>
namespace android::frametimeline {
+/*
+ * The type of jank that is associated with a Display/Surface frame
+ */
+enum class JankType {
+ // No Jank
+ None,
+ // Jank not related to SurfaceFlinger or the App
+ Display,
+ // SF took too long on the CPU
+ SurfaceFlingerDeadlineMissed,
+ // Either App or GPU took too long on the frame
+ AppDeadlineMissed,
+ // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a jank
+ // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
+ PredictionExpired,
+ // Latching a buffer early might cause an early present of the frame
+ SurfaceFlingerEarlyLatch,
+};
+
+enum JankMetadata {
+ // Frame was presented earlier than expected
+ EarlyPresent = 0x1,
+ // Frame was presented later than expected
+ LatePresent = 0x2,
+ // App/SF started earlier than expected
+ EarlyStart = 0x4,
+ // App/SF started later than expected
+ LateStart = 0x8,
+ // App/SF finished work earlier than the deadline
+ EarlyFinish = 0x10,
+ // App/SF finished work later than the deadline
+ LateFinish = 0x20,
+ // SF was in GPU composition
+ GpuComposition = 0x40,
+};
+
class FrameTimelineTest;
/*
@@ -39,6 +77,13 @@
nsecs_t startTime;
nsecs_t endTime;
nsecs_t presentTime;
+
+ bool operator==(const TimelineItem& other) const {
+ return startTime == other.startTime && endTime == other.endTime &&
+ presentTime == other.presentTime;
+ }
+
+ bool operator!=(const TimelineItem& other) const { return !(*this == other); }
};
/*
@@ -75,11 +120,11 @@
virtual ~SurfaceFrame() = default;
- virtual TimelineItem getPredictions() = 0;
- virtual TimelineItem getActuals() = 0;
- virtual nsecs_t getActualQueueTime() = 0;
- virtual PresentState getPresentState() = 0;
- virtual PredictionState getPredictionState() = 0;
+ virtual TimelineItem getPredictions() const = 0;
+ virtual TimelineItem getActuals() const = 0;
+ virtual nsecs_t getActualQueueTime() const = 0;
+ virtual PresentState getPresentState() const = 0;
+ virtual PredictionState getPredictionState() const = 0;
virtual void setPresentState(PresentState state) = 0;
@@ -120,7 +165,17 @@
virtual void setSfPresent(nsecs_t sfPresentTime,
const std::shared_ptr<FenceTime>& presentFence) = 0;
- virtual void dump(std::string& result) = 0;
+ // Args:
+ // -jank : Dumps only the Display Frames that are either janky themselves
+ // or contain janky Surface Frames.
+ // -all : Dumps the entire list of DisplayFrames and the SurfaceFrames contained within
+ virtual void parseArgs(const Vector<String16>& args, std::string& result) = 0;
+
+ // Sets the max number of display frames that can be stored. Called by SF backdoor.
+ virtual void setMaxDisplayFrames(uint32_t size);
+
+ // Restores the max number of display frames to default. Called by SF backdoor.
+ virtual void reset() = 0;
};
namespace impl {
@@ -155,27 +210,33 @@
TimelineItem&& predictions);
~SurfaceFrame() = default;
- TimelineItem getPredictions() override { return mPredictions; };
- TimelineItem getActuals() override;
- nsecs_t getActualQueueTime() override;
- PresentState getPresentState() override;
- PredictionState getPredictionState() override;
+ TimelineItem getPredictions() const override { return mPredictions; };
+ TimelineItem getActuals() const override;
+ nsecs_t getActualQueueTime() const override;
+ PresentState getPresentState() const override;
+ PredictionState getPredictionState() const override { return mPredictionState; };
void setActualStartTime(nsecs_t actualStartTime) override;
void setActualQueueTime(nsecs_t actualQueueTime) override;
void setAcquireFenceTime(nsecs_t acquireFenceTime) override;
void setPresentState(PresentState state) override;
void setActualPresentTime(nsecs_t presentTime);
- void dump(std::string& result);
+ void setJankInfo(JankType jankType, int32_t jankMetadata);
+ JankType getJankType() const;
+ nsecs_t getBaseTime() const;
+ // All the timestamps are dumped relative to the baseTime
+ void dump(std::string& result, const std::string& indent, nsecs_t baseTime);
private:
const std::string mLayerName;
PresentState mPresentState GUARDED_BY(mMutex);
- PredictionState mPredictionState GUARDED_BY(mMutex);
+ const PredictionState mPredictionState;
const TimelineItem mPredictions;
TimelineItem mActuals GUARDED_BY(mMutex);
- nsecs_t mActualQueueTime;
- std::mutex mMutex;
+ nsecs_t mActualQueueTime GUARDED_BY(mMutex);
+ mutable std::mutex mMutex;
+ JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank
+ int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank
};
class FrameTimeline : public android::frametimeline::FrameTimeline {
@@ -191,7 +252,9 @@
void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override;
void setSfPresent(nsecs_t sfPresentTime,
const std::shared_ptr<FenceTime>& presentFence) override;
- void dump(std::string& result) override;
+ void parseArgs(const Vector<String16>& args, std::string& result) override;
+ void setMaxDisplayFrames(uint32_t size) override;
+ void reset() override;
private:
// Friend class for testing
@@ -215,10 +278,19 @@
std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames;
PredictionState predictionState;
+ JankType jankType = JankType::None; // Enum for the type of jank
+ int32_t jankMetadata = 0x0; // Additional details about the jank
};
void flushPendingPresentFences() REQUIRES(mMutex);
void finalizeCurrentDisplayFrame() REQUIRES(mMutex);
+ // BaseTime is the smallest timestamp in a DisplayFrame.
+ // Used for dumping all timestamps relative to the oldest, making it easy to read.
+ nsecs_t findBaseTime(const std::shared_ptr<DisplayFrame>&) REQUIRES(mMutex);
+ void dumpDisplayFrame(std::string& result, const std::shared_ptr<DisplayFrame>&,
+ nsecs_t baseTime) REQUIRES(mMutex);
+ void dumpAll(std::string& result);
+ void dumpJank(std::string& result);
// Sliding window of display frames. TODO(b/168072834): compare perf with fixed size array
std::deque<std::shared_ptr<DisplayFrame>> mDisplayFrames GUARDED_BY(mMutex);
@@ -227,12 +299,21 @@
std::shared_ptr<DisplayFrame> mCurrentDisplayFrame GUARDED_BY(mMutex);
TokenManager mTokenManager;
std::mutex mMutex;
- static constexpr uint32_t kMaxDisplayFrames = 64;
+ uint32_t mMaxDisplayFrames;
+ static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
// The initial container size for the vector<SurfaceFrames> inside display frame. Although this
// number doesn't represent any bounds on the number of surface frames that can go in a display
// frame, this is a good starting size for the vector so that we can avoid the internal vector
// resizing that happens with push_back.
static constexpr uint32_t kNumSurfaceFramesInitial = 10;
+ // The various thresholds for App and SF. If the actual timestamp falls within the threshold
+ // compared to prediction, we don't treat it as a jank.
+ static constexpr nsecs_t kPresentThreshold =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+ static constexpr nsecs_t kDeadlineThreshold =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+ static constexpr nsecs_t kSFStartThreshold =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count();
};
} // namespace impl
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 08801c9..a79cbe4 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2511,6 +2511,7 @@
// InputDispatcher, and obviously if they aren't visible they can't occlude
// anything.
info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
+ info.alpha = getAlpha();
auto cropLayer = mDrawingState.touchableRegionCrop.promote();
if (info.replaceTouchableRegionWithCrop) {
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 641a0a3..1343375 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <binder/IPCThreadState.h>
@@ -28,6 +26,7 @@
#include <gui/IDisplayEventConnection.h>
#include "EventThread.h"
+#include "FrameTimeline.h"
#include "MessageQueue.h"
#include "SurfaceFlinger.h"
@@ -68,15 +67,53 @@
mHandler = new Handler(*this);
}
+// TODO(b/169865816): refactor VSyncInjections to use MessageQueue directly
+// and remove the EventThread from MessageQueue
void MessageQueue::setEventConnection(const sp<EventThreadConnection>& connection) {
if (mEventTube.getFd() >= 0) {
mLooper->removeFd(mEventTube.getFd());
}
mEvents = connection;
- mEvents->stealReceiveChannel(&mEventTube);
- mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
- this);
+ if (mEvents) {
+ mEvents->stealReceiveChannel(&mEventTube);
+ mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
+ this);
+ }
+}
+
+void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
+ ATRACE_CALL();
+ // Trace VSYNC-sf
+ mVsync.value = (mVsync.value + 1) % 2;
+
+ {
+ std::lock_guard lock(mVsync.mutex);
+ mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
+ }
+ mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions(
+ {targetWakeupTime, readyTime, vsyncTime}),
+ vsyncTime);
+}
+
+void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch,
+ frametimeline::TokenManager& tokenManager,
+ std::chrono::nanoseconds workDuration) {
+ setDuration(workDuration);
+ mVsync.tokenManager = &tokenManager;
+ mVsync.registration = std::make_unique<
+ scheduler::VSyncCallbackRegistration>(dispatch,
+ std::bind(&MessageQueue::vsyncCallback, this,
+ std::placeholders::_1,
+ std::placeholders::_2,
+ std::placeholders::_3),
+ "sf");
+}
+
+void MessageQueue::setDuration(std::chrono::nanoseconds workDuration) {
+ ATRACE_CALL();
+ std::lock_guard lock(mVsync.mutex);
+ mVsync.workDuration = workDuration;
}
void MessageQueue::waitMessage() {
@@ -106,7 +143,18 @@
}
void MessageQueue::invalidate() {
- mEvents->requestNextVsync();
+ ATRACE_CALL();
+ if (mEvents) {
+ mEvents->requestNextVsync();
+ } else {
+ const auto [workDuration, lastVsyncCallback] = [&] {
+ std::lock_guard lock(mVsync.mutex);
+ std::chrono::nanoseconds mWorkDurationNanos = mVsync.workDuration;
+ return std::make_pair(mWorkDurationNanos.count(), mVsync.lastCallbackTime.count());
+ }();
+
+ mVsync.registration->schedule({workDuration, /*readyDuration=*/0, lastVsyncCallback});
+ }
}
void MessageQueue::refresh() {
@@ -135,5 +183,3 @@
} // namespace android::impl
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index e263b2f..139b38e 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -29,6 +29,8 @@
#include <private/gui/BitTube.h>
#include "EventThread.h"
+#include "TracedOrdinal.h"
+#include "VSyncDispatch.h"
namespace android {
@@ -63,6 +65,9 @@
virtual ~MessageQueue() = default;
virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
+ virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+ std::chrono::nanoseconds workDuration) = 0;
+ virtual void setDuration(std::chrono::nanoseconds workDuration) = 0;
virtual void setEventConnection(const sp<EventThreadConnection>& connection) = 0;
virtual void waitMessage() = 0;
virtual void postMessage(sp<MessageHandler>&&) = 0;
@@ -74,7 +79,8 @@
namespace impl {
-class MessageQueue final : public android::MessageQueue {
+class MessageQueue : public android::MessageQueue {
+protected:
class Handler : public MessageHandler {
enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 };
MessageQueue& mQueue;
@@ -84,9 +90,9 @@
public:
explicit Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) {}
- virtual void handleMessage(const Message& message);
- void dispatchRefresh();
- void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp);
+ void handleMessage(const Message& message) override;
+ virtual void dispatchRefresh();
+ virtual void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp);
};
friend class Handler;
@@ -94,15 +100,33 @@
sp<SurfaceFlinger> mFlinger;
sp<Looper> mLooper;
sp<EventThreadConnection> mEvents;
+
+ struct Vsync {
+ frametimeline::TokenManager* tokenManager = nullptr;
+ std::unique_ptr<scheduler::VSyncCallbackRegistration> registration;
+
+ std::mutex mutex;
+ TracedOrdinal<std::chrono::nanoseconds> workDuration
+ GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
+ std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0};
+ TracedOrdinal<int> value = {"VSYNC-sf", 0};
+ };
+
+ Vsync mVsync;
+
gui::BitTube mEventTube;
sp<Handler> mHandler;
static int cb_eventReceiver(int fd, int events, void* data);
int eventReceiver(int fd, int events);
+ void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
public:
~MessageQueue() override = default;
void init(const sp<SurfaceFlinger>& flinger) override;
+ void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+ std::chrono::nanoseconds workDuration) override;
+ void setDuration(std::chrono::nanoseconds workDuration) override;
void setEventConnection(const sp<EventThreadConnection>& connection) override;
void waitMessage() override;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 3ecda58..47ce4a4 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -137,6 +137,8 @@
void setDisplayPowerState(bool normal);
+ scheduler::VSyncDispatch& getVsyncDispatch() { return *mVsyncSchedule.dispatch; }
+
void dump(std::string&) const;
void dump(ConnectionHandle, std::string&) const;
void dumpVsync(std::string&) const;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 0407900..9d71103 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -94,6 +94,13 @@
nsecs_t workDuration = 0;
nsecs_t readyDuration = 0;
nsecs_t earliestVsync = 0;
+
+ bool operator==(const ScheduleTiming& other) const {
+ return workDuration == other.workDuration && readyDuration == other.readyDuration &&
+ earliestVsync == other.earliestVsync;
+ }
+
+ bool operator!=(const ScheduleTiming& other) const { return !(*this == other); }
};
/*
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index df87e3b..bf33e5e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1414,8 +1414,8 @@
Mutex::Autolock lock(mStateLock);
if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
- mEventQueue->setEventConnection(
- mScheduler->getEventConnection(enable ? handle : mSfConnectionHandle));
+ mEventQueue->setEventConnection(enable ? mScheduler->getEventConnection(handle)
+ : nullptr);
}
}).wait();
@@ -2867,19 +2867,23 @@
// start the EventThread
mScheduler = getFactory().createScheduler(*mRefreshRateConfigs, *this);
const auto configs = mVsyncConfiguration->getCurrentConfigs();
+ const nsecs_t vsyncPeriod =
+ mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod();
mAppConnectionHandle =
mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),
/*workDuration=*/configs.late.appWorkDuration,
/*readyDuration=*/configs.late.sfWorkDuration,
impl::EventThread::InterceptVSyncsCallback());
mSfConnectionHandle =
- mScheduler->createConnection("sf", mFrameTimeline->getTokenManager(),
- /*workDuration=*/configs.late.sfWorkDuration,
- /*readyDuration=*/0ns, [this](nsecs_t timestamp) {
+ mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(),
+ /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
+ /*readyDuration=*/configs.late.sfWorkDuration,
+ [this](nsecs_t timestamp) {
mInterceptor->saveVSyncEvent(timestamp);
});
- mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
+ mEventQueue->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
+ configs.late.sfWorkDuration);
mRegionSamplingThread =
new RegionSamplingThread(*this, *mScheduler,
@@ -2891,8 +2895,6 @@
// This is a bit hacky, but this avoids a back-pointer into the main SF
// classes from EventThread, and there should be no run-time binder cost
// anyway since there are no connected apps at this point.
- const nsecs_t vsyncPeriod =
- mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod();
mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, primaryDisplayId, currentConfig,
vsyncPeriod);
static auto ignorePresentFences =
@@ -2904,16 +2906,19 @@
void SurfaceFlinger::updatePhaseConfiguration(const RefreshRate& refreshRate) {
mVsyncConfiguration->setRefreshRateFps(refreshRate.getFps());
- setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()));
+ setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
+ refreshRate.getVsyncPeriod());
}
-void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config) {
+void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config,
+ nsecs_t vsyncPeriod) {
mScheduler->setDuration(mAppConnectionHandle,
/*workDuration=*/config.appWorkDuration,
/*readyDuration=*/config.sfWorkDuration);
mScheduler->setDuration(mSfConnectionHandle,
- /*workDuration=*/config.sfWorkDuration,
- /*readyDuration=*/0ns);
+ /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
+ /*readyDuration=*/config.sfWorkDuration);
+ mEventQueue->setDuration(config.sfWorkDuration);
}
void SurfaceFlinger::commitTransaction() {
@@ -3942,7 +3947,9 @@
mInterceptor->saveSurfaceCreation(layer);
setTransactionFlags(eTransactionNeeded);
- *outId = layer->sequence;
+ if (outId) {
+ *outId = layer->sequence;
+ }
return result;
}
@@ -4232,7 +4239,7 @@
{"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
{"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)},
{"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
- {"--frametimeline"s, dumper([this](std::string& s) { mFrameTimeline->dump(s); })},
+ {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
};
const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
@@ -4315,6 +4322,10 @@
mTimeStats->parseArgs(asProto, args, result);
}
+void SurfaceFlinger::dumpFrameTimeline(const DumpArgs& args, std::string& result) const {
+ mFrameTimeline->parseArgs(args, result);
+}
+
// This should only be called from the main thread. Otherwise it would need
// the lock and should use mCurrentState rather than mDrawingState.
void SurfaceFlinger::logFrameStats() {
@@ -4897,9 +4908,9 @@
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1037 are currently used for backdoors. The code
+ // Numbers from 1000 to 1038 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1037) {
+ if (code >= 1000 && code <= 1038) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -5255,6 +5266,21 @@
return NO_ERROR;
}
+ // Modify the max number of display frames stored within FrameTimeline
+ case 1038: {
+ n = data.readInt32();
+ if (n < 0 || n > MAX_ALLOWED_DISPLAY_FRAMES) {
+ ALOGW("Invalid max size. Maximum allowed is %d", MAX_ALLOWED_DISPLAY_FRAMES);
+ return BAD_VALUE;
+ }
+ if (n == 0) {
+ // restore to default
+ mFrameTimeline->reset();
+ return NO_ERROR;
+ }
+ mFrameTimeline->setMaxDisplayFrames(n);
+ return NO_ERROR;
+ }
}
}
return err;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 9a552d8..e50ecf0 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -494,11 +494,14 @@
typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
void modulateVsync(Handler handler, Args... args) {
if (const auto config = (*mVsyncModulator.*handler)(args...)) {
- setVsyncConfig(*config);
+ const auto vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
+ setVsyncConfig(*config, vsyncPeriod);
}
}
static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
+ // Maximum allowed number of display frames that can be set through backdoor
+ static const int MAX_ALLOWED_DISPLAY_FRAMES = 2048;
// Implements IBinder.
status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
@@ -701,7 +704,7 @@
void initScheduler(PhysicalDisplayId primaryDisplayId);
void updatePhaseConfiguration(const RefreshRate&);
- void setVsyncConfig(const VsyncModulator::VsyncConfig&);
+ void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod);
/* handlePageFlip - latch a new buffer if available and compute the dirty
* region. Returns whether a new buffer has been latched, i.e., whether it
@@ -978,6 +981,7 @@
void dumpStatsLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
void clearStatsLocked(const DumpArgs& args, std::string& result);
void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const;
+ void dumpFrameTimeline(const DumpArgs& args, std::string& result) const;
void logFrameStats();
void dumpVSync(std::string& result) const REQUIRES(mStateLock);
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 719c46e..b7edb84 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -34,6 +34,7 @@
"LayerUpdate_test.cpp",
"MirrorLayer_test.cpp",
"MultiDisplayLayerBounds_test.cpp",
+ "RefreshRateOverlay_test.cpp",
"RelativeZ_test.cpp",
"ScreenCapture_test.cpp",
"SetFrameRate_test.cpp",
diff --git a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
new file mode 100644
index 0000000..05858bf
--- /dev/null
+++ b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <thread>
+
+#include <gtest/gtest.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+
+static constexpr int kRefreshRateOverlayCode = 1034;
+static constexpr int kRefreshRateOverlayEnable = 1;
+static constexpr int kRefreshRateOverlayDisable = 0;
+static constexpr int kRefreshRateOverlayQuery = 2;
+
+// These values must match the ones we used for developer options in
+// com.android.settings.development.ShowRefreshRatePreferenceController
+static_assert(kRefreshRateOverlayCode == 1034);
+static_assert(kRefreshRateOverlayEnable == 1);
+static_assert(kRefreshRateOverlayDisable == 0);
+static_assert(kRefreshRateOverlayQuery == 2);
+
+namespace android {
+
+namespace {
+void sendCommandToSf(int command, Parcel& reply) {
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ Parcel request;
+ request.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
+ request.writeInt32(command);
+ ASSERT_EQ(NO_ERROR,
+ IInterface::asBinder(sf)->transact(kRefreshRateOverlayCode, request, &reply));
+}
+
+bool isOverlayEnabled() {
+ Parcel reply;
+ sendCommandToSf(kRefreshRateOverlayQuery, reply);
+ return reply.readBool();
+}
+
+void waitForOverlay(bool enabled) {
+ static constexpr auto kTimeout = std::chrono::nanoseconds(1s);
+ static constexpr auto kIterations = 10;
+ for (int i = 0; i < kIterations; i++) {
+ if (enabled == isOverlayEnabled()) {
+ return;
+ }
+ std::this_thread::sleep_for(kTimeout / kIterations);
+ }
+}
+
+void toggleOverlay(bool enabled) {
+ if (enabled == isOverlayEnabled()) {
+ return;
+ }
+
+ Parcel reply;
+ const auto command = enabled ? kRefreshRateOverlayEnable : kRefreshRateOverlayDisable;
+ sendCommandToSf(command, reply);
+ waitForOverlay(enabled);
+ ASSERT_EQ(enabled, isOverlayEnabled());
+}
+
+} // namespace
+
+TEST(RefreshRateOverlayTest, enableOverlay) {
+ toggleOverlay(true);
+}
+
+TEST(RefreshRateOverlayTest, disableOverlay) {
+ toggleOverlay(false);
+}
+
+TEST(RefreshRateOverlayTest, enableAndDisableOverlay) {
+ toggleOverlay(true);
+ toggleOverlay(false);
+
+ toggleOverlay(true);
+ toggleOverlay(false);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 6f1f1f2..cf3a4c2 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -47,6 +47,7 @@
"LayerHistoryTest.cpp",
"LayerHistoryTestV2.cpp",
"LayerMetadataTest.cpp",
+ "MessageQueueTest.cpp",
"PromiseTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 617f95a..69efd7f 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -43,7 +43,7 @@
void SetUp() override {
mFrameTimeline = std::make_unique<impl::FrameTimeline>();
mTokenManager = &mFrameTimeline->mTokenManager;
- maxDisplayFrames = mFrameTimeline->kMaxDisplayFrames;
+ maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
maxTokenRetentionTime = mTokenManager->kMaxRetentionTime;
}
@@ -71,10 +71,15 @@
return mTokenManager->mPredictions;
}
+ uint32_t getNumberOfDisplayFrames() {
+ std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
+ return static_cast<uint32_t>(mFrameTimeline->mDisplayFrames.size());
+ }
+
std::unique_ptr<impl::FrameTimeline> mFrameTimeline;
impl::TokenManager* mTokenManager;
FenceToFenceTimeMap fenceFactory;
- uint64_t maxDisplayFrames;
+ uint32_t* maxDisplayFrames;
nsecs_t maxTokenRetentionTime;
};
@@ -178,7 +183,7 @@
TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) {
// Insert kMaxDisplayFrames' count of DisplayFrames to fill the deque
int frameTimeFactor = 0;
- for (size_t i = 0; i < maxDisplayFrames; i++) {
+ for (size_t i = 0; i < *maxDisplayFrames; i++) {
auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions(
{10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor});
@@ -233,4 +238,47 @@
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
}
+TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) {
+ auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ presentFence->signalForTest(2);
+
+ // Size shouldn't exceed maxDisplayFrames - 64
+ for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+ int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
+ mFrameTimeline->setSfWakeUp(sfToken, 22);
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+ SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfPresent(27, presentFence);
+ }
+ EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
+
+ // Increase the size to 256
+ mFrameTimeline->setMaxDisplayFrames(256);
+ EXPECT_EQ(*maxDisplayFrames, 256);
+
+ for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+ int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
+ mFrameTimeline->setSfWakeUp(sfToken, 22);
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+ SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfPresent(27, presentFence);
+ }
+ EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
+
+ // Shrink the size to 128
+ mFrameTimeline->setMaxDisplayFrames(128);
+ EXPECT_EQ(*maxDisplayFrames, 128);
+
+ for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+ int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
+ mFrameTimeline->setSfWakeUp(sfToken, 22);
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
+ SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfPresent(27, presentFence);
+ }
+ EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
+}
} // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
new file mode 100644
index 0000000..53dfe3f
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "FrameTimeline.h"
+#include "Scheduler/MessageQueue.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+
+using namespace std::chrono_literals;
+using namespace testing;
+
+using CallbackToken = scheduler::VSyncDispatch::CallbackToken;
+
+class TestableMessageQueue : public impl::MessageQueue {
+public:
+ class MockHandler : public MessageQueue::Handler {
+ public:
+ explicit MockHandler(MessageQueue& queue) : MessageQueue::Handler(queue) {}
+ ~MockHandler() override = default;
+ MOCK_METHOD2(dispatchInvalidate, void(int64_t vsyncId, nsecs_t expectedVSyncTimestamp));
+ };
+
+ TestableMessageQueue() = default;
+ ~TestableMessageQueue() override = default;
+
+ void initHandler(const sp<MockHandler>& handler) { mHandler = handler; }
+
+ void triggerVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
+ vsyncCallback(vsyncTime, targetWakeupTime, readyTime);
+ }
+};
+
+class MockVSyncDispatch : public scheduler::VSyncDispatch {
+public:
+ MockVSyncDispatch() = default;
+ ~MockVSyncDispatch() override = default;
+
+ MOCK_METHOD2(registerCallback,
+ CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string));
+ MOCK_METHOD1(unregisterCallback, void(CallbackToken));
+ MOCK_METHOD2(schedule, scheduler::ScheduleResult(CallbackToken, ScheduleTiming));
+ MOCK_METHOD1(cancel, scheduler::CancelResult(CallbackToken token));
+ MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+class MockTokenManager : public frametimeline::TokenManager {
+public:
+ MockTokenManager() = default;
+ ~MockTokenManager() override = default;
+
+ MOCK_METHOD1(generateTokenForPredictions, int64_t(frametimeline::TimelineItem&& prediction));
+};
+
+class MessageQueueTest : public testing::Test {
+public:
+ MessageQueueTest() = default;
+ ~MessageQueueTest() override = default;
+
+ void SetUp() override {
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.initHandler(mHandler));
+
+ EXPECT_CALL(mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken));
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, mDuration));
+ EXPECT_CALL(mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+ }
+
+ sp<TestableMessageQueue::MockHandler> mHandler =
+ new TestableMessageQueue::MockHandler(mEventQueue);
+ MockVSyncDispatch mVSyncDispatch;
+ MockTokenManager mTokenManager;
+ TestableMessageQueue mEventQueue;
+
+ const CallbackToken mCallbackToken{5};
+ constexpr static auto mDuration = std::chrono::nanoseconds(100ms);
+ constexpr static auto mDifferentDuration = std::chrono::nanoseconds(250ms);
+};
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(MessageQueueTest, invalidate) {
+ const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+ .readyDuration = 0,
+ .earliestVsync = 0};
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+TEST_F(MessageQueueTest, invalidateTwice) {
+ InSequence s;
+ const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+ .readyDuration = 0,
+ .earliestVsync = 0};
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+TEST_F(MessageQueueTest, invalidateTwiceWithCallback) {
+ InSequence s;
+ const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+ .readyDuration = 0,
+ .earliestVsync = 0};
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+
+ const auto startTime = 100;
+ const auto endTime = startTime + mDuration.count();
+ const auto presentTime = 500;
+ const auto vsyncId = 42;
+ EXPECT_CALL(mTokenManager,
+ generateTokenForPredictions(
+ frametimeline::TimelineItem(startTime, endTime, presentTime)))
+ .WillOnce(Return(vsyncId));
+ EXPECT_CALL(*mHandler, dispatchInvalidate(vsyncId, presentTime)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.triggerVsyncCallback(presentTime, startTime, endTime));
+
+ const auto timingAfterCallback =
+ scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+ .readyDuration = 0,
+ .earliestVsync = presentTime};
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+TEST_F(MessageQueueTest, invalidateWithDurationChange) {
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.setDuration(mDifferentDuration));
+
+ const auto timing =
+ scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDifferentDuration.count(),
+ .readyDuration = 0,
+ .earliestVsync = 0};
+
+ EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index a82b583..efaa9fa 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -18,6 +18,7 @@
#include <gmock/gmock.h>
+#include "FrameTimeline.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/MessageQueue.h"
@@ -34,6 +35,10 @@
MOCK_METHOD1(postMessage, void(sp<MessageHandler>&&));
MOCK_METHOD0(invalidate, void());
MOCK_METHOD0(refresh, void());
+ MOCK_METHOD3(initVsync,
+ void(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+ std::chrono::nanoseconds));
+ MOCK_METHOD1(setDuration, void(std::chrono::nanoseconds workDuration));
};
} // namespace android::mock