Merge "Do not modify vector after getting references" into sc-dev
diff --git a/Android.bp b/Android.bp
index 1393f61..dec6716 100644
--- a/Android.bp
+++ b/Android.bp
@@ -86,3 +86,9 @@
name: "libandroid_headers_private",
export_include_dirs: ["include/private"],
}
+
+filegroup {
+ name: "deviceproductinfoconstants_aidl",
+ srcs: ["aidl/android/hardware/display/IDeviceProductInfoConstants.aidl"],
+ path: "aidl",
+}
diff --git a/aidl/android/hardware/display/IDeviceProductInfoConstants.aidl b/aidl/android/hardware/display/IDeviceProductInfoConstants.aidl
new file mode 100644
index 0000000..7cc272a
--- /dev/null
+++ b/aidl/android/hardware/display/IDeviceProductInfoConstants.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2021 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.hardware.display;
+
+/** @hide */
+interface IDeviceProductInfoConstants {
+ /** The device connection to the display sink is unknown. */
+ const int CONNECTION_TO_SINK_UNKNOWN = 0;
+
+ /** The device is built-in in the display sink. */
+ const int CONNECTION_TO_SINK_BUILT_IN = 1;
+
+ /** The device is directly connected to the display sink. */
+ const int CONNECTION_TO_SINK_DIRECT = 2;
+
+ /** The device is transitively connected to the display sink. */
+ const int CONNECTION_TO_SINK_TRANSITIVE = 3;
+}
\ No newline at end of file
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index a7a6563..c20d9f6 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -113,5 +113,14 @@
* Returns the debug flag for the given package.
* Unknown packages will cause the call to fail.
*/
- boolean isPackageDebuggable(in String packageName);
+ boolean isPackageDebuggable(in String packageName);
+
+ /**
+ * Check whether the given feature name and version is one of the available
+ * features as returned by {@link PackageManager#getSystemAvailableFeatures()}. Since
+ * features are defined to always be backwards compatible, this returns true
+ * if the available feature version is greater than or equal to the
+ * requested version.
+ */
+ boolean hasSystemFeature(in String featureName, in int version);
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index e027c5e..51f6f5d 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -898,6 +898,8 @@
mInfo.touchOcclusionMode = mode;
}
+ void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; }
+
void setFrame(const Rect& frame) {
mInfo.frameLeft = frame.left;
mInfo.frameTop = frame.top;
@@ -4266,6 +4268,19 @@
class InputDispatcherUntrustedTouchesTest : public InputDispatcherTest {
protected:
constexpr static const float MAXIMUM_OBSCURING_OPACITY = 0.8;
+
+ constexpr static const float OPACITY_ABOVE_THRESHOLD = 0.9;
+ static_assert(OPACITY_ABOVE_THRESHOLD > MAXIMUM_OBSCURING_OPACITY);
+
+ constexpr static const float OPACITY_BELOW_THRESHOLD = 0.7;
+ static_assert(OPACITY_BELOW_THRESHOLD < MAXIMUM_OBSCURING_OPACITY);
+
+ // When combined twice, ie 1 - (1 - 0.5)*(1 - 0.5) = 0.75 < 8, is still below the threshold
+ constexpr static const float OPACITY_FAR_BELOW_THRESHOLD = 0.5;
+ static_assert(OPACITY_FAR_BELOW_THRESHOLD < MAXIMUM_OBSCURING_OPACITY);
+ static_assert(1 - (1 - OPACITY_FAR_BELOW_THRESHOLD) * (1 - OPACITY_FAR_BELOW_THRESHOLD) <
+ MAXIMUM_OBSCURING_OPACITY);
+
static const int32_t TOUCHED_APP_UID = 10001;
static const int32_t APP_B_UID = 10002;
static const int32_t APP_C_UID = 10003;
@@ -4321,6 +4336,17 @@
}
TEST_F(InputDispatcherUntrustedTouchesTest,
+ WindowWithBlockUntrustedOcclusionModeWithOpacityBelowThreshold_BlocksTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.7f);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest,
WindowWithBlockUntrustedOcclusionMode_DoesNotReceiveTouch) {
const sp<FakeWindowHandle>& w =
getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
@@ -4415,7 +4441,8 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityBelowThreshold_AllowsTouch) {
const sp<FakeWindowHandle>& w =
- getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.7f);
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
touch();
@@ -4436,7 +4463,8 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityAboveThreshold_BlocksTouch) {
const sp<FakeWindowHandle>& w =
- getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.9f);
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
touch();
@@ -4447,9 +4475,11 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowsWithCombinedOpacityAboveThreshold_BlocksTouch) {
// Resulting opacity = 1 - (1 - 0.7)*(1 - 0.7) = .91
const sp<FakeWindowHandle>& w1 =
- getOccludingWindow(APP_B_UID, "B1", TouchOcclusionMode::USE_OPACITY, 0.7f);
+ getOccludingWindow(APP_B_UID, "B1", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
const sp<FakeWindowHandle>& w2 =
- getOccludingWindow(APP_B_UID, "B2", TouchOcclusionMode::USE_OPACITY, 0.7f);
+ getOccludingWindow(APP_B_UID, "B2", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w1, w2, mTouchWindow}}});
touch();
@@ -4460,9 +4490,11 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowsWithCombinedOpacityBelowThreshold_AllowsTouch) {
// Resulting opacity = 1 - (1 - 0.5)*(1 - 0.5) = .75
const sp<FakeWindowHandle>& w1 =
- getOccludingWindow(APP_B_UID, "B1", TouchOcclusionMode::USE_OPACITY, 0.5f);
+ getOccludingWindow(APP_B_UID, "B1", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_FAR_BELOW_THRESHOLD);
const sp<FakeWindowHandle>& w2 =
- getOccludingWindow(APP_B_UID, "B2", TouchOcclusionMode::USE_OPACITY, 0.5f);
+ getOccludingWindow(APP_B_UID, "B2", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_FAR_BELOW_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w1, w2, mTouchWindow}}});
touch();
@@ -4473,9 +4505,11 @@
TEST_F(InputDispatcherUntrustedTouchesTest,
WindowsFromDifferentAppsEachBelowThreshold_AllowsTouch) {
const sp<FakeWindowHandle>& wB =
- getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.7f);
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
const sp<FakeWindowHandle>& wC =
- getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY, 0.7f);
+ getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wB, wC, mTouchWindow}}});
touch();
@@ -4485,9 +4519,11 @@
TEST_F(InputDispatcherUntrustedTouchesTest, WindowsFromDifferentAppsOneAboveThreshold_BlocksTouch) {
const sp<FakeWindowHandle>& wB =
- getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.7f);
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
const sp<FakeWindowHandle>& wC =
- getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY, 0.9f);
+ getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wB, wC, mTouchWindow}}});
touch();
@@ -4498,9 +4534,11 @@
TEST_F(InputDispatcherUntrustedTouchesTest,
WindowWithOpacityAboveThresholdAndSelfWindow_BlocksTouch) {
const sp<FakeWindowHandle>& wA =
- getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY, 0.7f);
+ getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
const sp<FakeWindowHandle>& wB =
- getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.9f);
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wA, wB, mTouchWindow}}});
touch();
@@ -4511,9 +4549,11 @@
TEST_F(InputDispatcherUntrustedTouchesTest,
WindowWithOpacityBelowThresholdAndSelfWindow_AllowsTouch) {
const sp<FakeWindowHandle>& wA =
- getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY, 0.9f);
+ getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
const sp<FakeWindowHandle>& wB =
- getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.7f);
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wA, wB, mTouchWindow}}});
touch();
@@ -4523,7 +4563,8 @@
TEST_F(InputDispatcherUntrustedTouchesTest, SelfWindowWithOpacityAboveThreshold_AllowsTouch) {
const sp<FakeWindowHandle>& w =
- getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY, 0.9f);
+ getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
touch();
@@ -4568,7 +4609,59 @@
OpacityThresholdIs1AndWindowBelowThreshold_AllowsTouch) {
mDispatcher->setMaximumObscuringOpacityForTouch(1.0f);
const sp<FakeWindowHandle>& w =
- getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.9f);
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_ABOVE_THRESHOLD);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->consumeAnyMotionDown();
+}
+
+TEST_F(InputDispatcherUntrustedTouchesTest,
+ WindowWithBlockUntrustedModeAndWindowWithOpacityBelowFromSameApp_BlocksTouch) {
+ const sp<FakeWindowHandle>& w1 =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED,
+ OPACITY_BELOW_THRESHOLD);
+ const sp<FakeWindowHandle>& w2 =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w1, w2, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->assertNoEvents();
+}
+
+/**
+ * Window B of BLOCK_UNTRUSTED occlusion mode is enough to block the touch, we're testing that the
+ * addition of another window (C) of USE_OPACITY occlusion mode and opacity below the threshold
+ * (which alone would result in allowing touches) does not affect the blocking behavior.
+ */
+TEST_F(InputDispatcherUntrustedTouchesTest,
+ WindowWithBlockUntrustedModeAndWindowWithOpacityBelowFromDifferentApps_BlocksTouch) {
+ const sp<FakeWindowHandle>& wB =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED,
+ OPACITY_BELOW_THRESHOLD);
+ const sp<FakeWindowHandle>& wC =
+ getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY,
+ OPACITY_BELOW_THRESHOLD);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wB, wC, mTouchWindow}}});
+
+ touch();
+
+ mTouchWindow->assertNoEvents();
+}
+
+/**
+ * This test is testing that a window from a different UID but with same application token doesn't
+ * block the touch. Apps can share the application token for close UI collaboration for example.
+ */
+TEST_F(InputDispatcherUntrustedTouchesTest,
+ WindowWithSameApplicationTokenFromDifferentApp_AllowsTouch) {
+ const sp<FakeWindowHandle>& w =
+ getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED);
+ w->setApplicationToken(mTouchWindow->getApplicationToken());
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}});
touch();
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index f30e1eb..296085a 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -370,9 +370,7 @@
addFrameEvent(acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime);
- if (info.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
- setFrameTimelineVsyncForBufferTransaction(info, postTime);
- }
+ setFrameTimelineVsyncForBufferTransaction(info, postTime);
if (dequeueTime && *dequeueTime != 0) {
const uint64_t bufferId = buffer->getId();
@@ -871,18 +869,13 @@
return layerSize.width() != bufferWidth || layerSize.height() != bufferHeight;
}
-void BufferStateLayer::incrementPendingBufferCount() {
- mPendingBufferTransactions++;
- tracePendingBufferCount();
-}
-
void BufferStateLayer::decrementPendingBufferCount() {
- mPendingBufferTransactions--;
- tracePendingBufferCount();
+ int32_t pendingBuffers = --mPendingBufferTransactions;
+ tracePendingBufferCount(pendingBuffers);
}
-void BufferStateLayer::tracePendingBufferCount() {
- ATRACE_INT(mBlastTransactionName.c_str(), mPendingBufferTransactions);
+void BufferStateLayer::tracePendingBufferCount(int32_t pendingBuffers) {
+ ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers);
}
uint32_t BufferStateLayer::doTransaction(uint32_t flags) {
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 175a40b..6b422ea 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -113,9 +113,10 @@
uint32_t getEffectiveScalingMode() const override;
// See mPendingBufferTransactions
- void incrementPendingBufferCount() override;
void decrementPendingBufferCount();
uint32_t doTransaction(uint32_t flags) override;
+ std::atomic<int32_t>* getPendingBufferCounter() override { return &mPendingBufferTransactions; }
+ std::string getPendingBufferCounterName() override { return mBlastTransactionName; }
protected:
void gatherBufferInfo() override;
@@ -127,7 +128,7 @@
friend class TransactionFrameTracerTest;
friend class TransactionSurfaceFrameTest;
- inline void tracePendingBufferCount();
+ inline void tracePendingBufferCount(int32_t pendingBuffers);
bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
nsecs_t requestedPresentTime);
@@ -184,7 +185,7 @@
// - If the integer increases, a buffer arrived at the server.
// - If the integer decreases in latchBuffer, that buffer was latched
// - If the integer decreases in setBuffer or doTransaction, a buffer was dropped
- uint64_t mPendingBufferTransactions{0};
+ std::atomic<int32_t> mPendingBufferTransactions{0};
// TODO(marissaw): support sticky transform for LEGACY camera mode
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 36c4c4d..f4a2a3f 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -145,9 +145,9 @@
}
void DisplayDevice::setActiveMode(DisplayModeId id) {
- LOG_FATAL_IF(id.value() >= mSupportedModes.size(),
- "Cannot set active mode which is not supported.");
- mActiveModeId = id;
+ const auto mode = getMode(id);
+ LOG_FATAL_IF(!mode, "Cannot set active mode which is not supported.");
+ mActiveMode = mode;
}
status_t DisplayDevice::initiateModeChange(DisplayModeId modeId,
@@ -164,7 +164,7 @@
}
const DisplayModePtr& DisplayDevice::getActiveMode() const {
- return mSupportedModes[mActiveModeId.value()];
+ return mActiveMode;
}
const DisplayModes& DisplayDevice::getSupportedModes() const {
@@ -172,9 +172,10 @@
}
DisplayModePtr DisplayDevice::getMode(DisplayModeId modeId) const {
- const auto id = modeId.value();
- if (static_cast<size_t>(id) < mSupportedModes.size()) {
- return mSupportedModes[id];
+ const auto it = std::find_if(mSupportedModes.begin(), mSupportedModes.end(),
+ [&](DisplayModePtr mode) { return mode->getId() == modeId; });
+ if (it != mSupportedModes.end()) {
+ return *it;
}
return nullptr;
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index a94bfa2..7156613 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -208,7 +208,7 @@
hardware::graphics::composer::hal::PowerMode mPowerMode =
hardware::graphics::composer::hal::PowerMode::OFF;
- DisplayModeId mActiveModeId;
+ DisplayModePtr mActiveMode;
const DisplayModes mSupportedModes;
std::atomic<nsecs_t> mLastHwVsync = 0;
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 853c05b..85cc993 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -125,6 +125,12 @@
// without visual interruptions such as a black screen.
int32_t getGroup() const { return mGroup; }
+ bool equalsExceptDisplayModeId(const DisplayModePtr& other) const {
+ return mHwcId == other->mHwcId && mWidth == other->mWidth && mHeight == other->mHeight &&
+ getVsyncPeriod() == other->getVsyncPeriod() && mDpiX == other->mDpiX &&
+ mDpiY == other->mDpiY && mGroup == other->mGroup;
+ }
+
private:
explicit DisplayMode(hal::HWConfigId id) : mHwcId(id) {}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index cf6bc68..f532e50 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -91,6 +91,12 @@
int32_t dpiX = -1;
int32_t dpiY = -1;
int32_t configGroup = -1;
+
+ friend std::ostream& operator<<(std::ostream& os, const HWCDisplayMode& mode) {
+ return os << "id=" << mode.hwcId << " res=" << mode.width << "x" << mode.height
+ << " vsyncPeriod=" << mode.vsyncPeriod << " dpi=" << mode.dpiX << "x"
+ << mode.dpiY << " group=" << mode.configGroup;
+ }
};
virtual ~HWComposer();
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 75229e9..7515872 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -974,7 +974,7 @@
FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
auto packet = ctx.NewTracePacket();
packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
- packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.endTime));
+ packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.presentTime));
auto* event = packet->set_frame_timeline_event();
auto* actualDisplayFrameEndEvent = event->set_frame_end();
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 937868a..061ad0e 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1339,7 +1339,6 @@
bool Layer::setMetadata(const LayerMetadata& data) {
if (!mCurrentState.metadata.merge(data, true /* eraseEmpty */)) return false;
- mCurrentState.sequence++;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index a072554..ce97155 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -503,8 +503,6 @@
virtual void useEmptyDamage() {}
Region getVisibleRegion(const DisplayDevice*) const;
- virtual void incrementPendingBufferCount() {}
-
/*
* isOpaque - true if this surface is opaque
*
@@ -948,6 +946,9 @@
bool setStretchEffect(const StretchEffect& effect);
StretchEffect getStretchEffect() const;
+ virtual std::atomic<int32_t>* getPendingBufferCounter() { return nullptr; }
+ virtual std::string getPendingBufferCounterName() { return ""; }
+
protected:
class SyncPoint {
public:
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index de11c16..91e8043 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -610,15 +610,16 @@
void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes,
DisplayModeId currentModeId) {
std::lock_guard lock(mLock);
- LOG_ALWAYS_FATAL_IF(modes.empty());
- LOG_ALWAYS_FATAL_IF(currentModeId.value() >= modes.size());
+ // The current mode should be supported
+ LOG_ALWAYS_FATAL_IF(std::none_of(modes.begin(), modes.end(), [&](DisplayModePtr mode) {
+ return mode->getId() == currentModeId;
+ }));
mRefreshRates.clear();
for (const auto& mode : modes) {
const auto modeId = mode->getId();
- const auto fps = Fps::fromPeriodNsecs(mode->getVsyncPeriod());
mRefreshRates.emplace(modeId,
- std::make_unique<RefreshRate>(modeId, mode, fps,
+ std::make_unique<RefreshRate>(modeId, mode, mode->getFps(),
RefreshRate::ConstructorTag(0)));
if (modeId == currentModeId) {
mCurrentRefreshRate = mRefreshRates.at(modeId).get();
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 8007dd3..ad91183 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -104,6 +104,7 @@
#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/FramebufferSurface.h"
#include "DisplayHardware/HWComposer.h"
+#include "DisplayHardware/Hal.h"
#include "DisplayHardware/VirtualDisplaySurface.h"
#include "DisplayRenderArea.h"
#include "EffectLayer.h"
@@ -131,6 +132,7 @@
#include "TimeStats/TimeStats.h"
#include "android-base/parseint.h"
#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
#define MAIN_THREAD ACQUIRE(mStateLock) RELEASE(mStateLock)
@@ -496,6 +498,9 @@
// the window manager died on us. prepare its eulogy.
mBootFinished = false;
+ // Sever the link to inputflinger since its gone as well.
+ static_cast<void>(schedule([=] { mInputFlinger = nullptr; }));
+
// restore initial conditions (default device unblank, etc)
initializeDisplays();
@@ -2303,22 +2308,74 @@
// here the transaction has been committed
}
-DisplayModes SurfaceFlinger::loadSupportedDisplayModes(PhysicalDisplayId displayId) const {
- const auto hwcModes = getHwComposer().getModes(displayId);
- DisplayModes modes;
- int32_t nextModeId = 0;
- for (const auto& hwcMode : hwcModes) {
- modes.push_back(DisplayMode::Builder(hwcMode.hwcId)
- .setId(DisplayModeId{nextModeId++})
- .setWidth(hwcMode.width)
- .setHeight(hwcMode.height)
- .setVsyncPeriod(hwcMode.vsyncPeriod)
- .setDpiX(hwcMode.dpiX)
- .setDpiY(hwcMode.dpiY)
- .setGroup(hwcMode.configGroup)
- .build());
+void SurfaceFlinger::loadDisplayModes(PhysicalDisplayId displayId, DisplayModes& outModes,
+ DisplayModePtr& outActiveMode) const {
+ std::vector<HWComposer::HWCDisplayMode> hwcModes;
+ std::optional<hal::HWDisplayId> activeModeHwcId;
+ bool activeModeIsSupported;
+ int attempt = 0;
+ constexpr int kMaxAttempts = 3;
+ do {
+ hwcModes = getHwComposer().getModes(displayId);
+ activeModeHwcId = getHwComposer().getActiveMode(displayId);
+ LOG_ALWAYS_FATAL_IF(!activeModeHwcId, "HWC returned no active mode");
+
+ activeModeIsSupported =
+ std::any_of(hwcModes.begin(), hwcModes.end(),
+ [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) {
+ return mode.hwcId == *activeModeHwcId;
+ });
+ } while (!activeModeIsSupported && ++attempt < kMaxAttempts);
+ LOG_ALWAYS_FATAL_IF(!activeModeIsSupported,
+ "After %d attempts HWC still returns an active mode which is not"
+ " supported. Active mode ID = %" PRIu64 " . Supported modes = %s",
+ kMaxAttempts, *activeModeHwcId, base::Join(hwcModes, ", ").c_str());
+
+ DisplayModes oldModes;
+
+ if (const auto token = getPhysicalDisplayTokenLocked(displayId)) {
+ oldModes = getDisplayDeviceLocked(token)->getSupportedModes();
}
- return modes;
+
+ int largestUsedModeId = -1; // Use int instead of DisplayModeId for signedness
+ for (const auto& mode : oldModes) {
+ const auto id = static_cast<int>(mode->getId().value());
+ if (id > largestUsedModeId) {
+ largestUsedModeId = id;
+ }
+ }
+
+ DisplayModes newModes;
+ int32_t nextModeId = largestUsedModeId + 1;
+ for (const auto& hwcMode : hwcModes) {
+ newModes.push_back(DisplayMode::Builder(hwcMode.hwcId)
+ .setId(DisplayModeId{nextModeId++})
+ .setWidth(hwcMode.width)
+ .setHeight(hwcMode.height)
+ .setVsyncPeriod(hwcMode.vsyncPeriod)
+ .setDpiX(hwcMode.dpiX)
+ .setDpiY(hwcMode.dpiY)
+ .setGroup(hwcMode.configGroup)
+ .build());
+ }
+
+ const bool modesAreSame =
+ std::equal(newModes.begin(), newModes.end(), oldModes.begin(), oldModes.end(),
+ [](DisplayModePtr left, DisplayModePtr right) {
+ return left->equalsExceptDisplayModeId(right);
+ });
+
+ if (modesAreSame) {
+ // The supported modes have not changed, keep the old IDs.
+ outModes = oldModes;
+ } else {
+ outModes = newModes;
+ }
+
+ outActiveMode = *std::find_if(outModes.begin(), outModes.end(),
+ [activeModeHwcId](const DisplayModePtr& mode) {
+ return mode->getHwcId() == *activeModeHwcId;
+ });
}
void SurfaceFlinger::processDisplayHotplugEventsLocked() {
@@ -2334,15 +2391,9 @@
const auto it = mPhysicalDisplayTokens.find(displayId);
if (event.connection == hal::Connection::CONNECTED) {
- auto supportedModes = loadSupportedDisplayModes(displayId);
- const auto activeModeHwcId = getHwComposer().getActiveMode(displayId);
- LOG_ALWAYS_FATAL_IF(!activeModeHwcId, "HWC returned no active mode");
-
- const auto activeMode = *std::find_if(supportedModes.begin(), supportedModes.end(),
- [activeModeHwcId](const DisplayModePtr& mode) {
- return mode->getHwcId() == *activeModeHwcId;
- });
- // TODO(b/175678215) Handle the case when activeMode is not in supportedModes
+ DisplayModes supportedModes;
+ DisplayModePtr activeMode;
+ loadDisplayModes(displayId, supportedModes, activeMode);
if (it == mPhysicalDisplayTokens.end()) {
ALOGV("Creating display %s", to_string(displayId).c_str());
@@ -2411,7 +2462,6 @@
const sp<IGraphicBufferProducer>& producer) {
DisplayDeviceCreationArgs creationArgs(this, getHwComposer(), displayToken, compositionDisplay);
creationArgs.sequenceId = state.sequenceId;
- creationArgs.hwComposer = getHwComposer();
creationArgs.isSecure = state.isSecure;
creationArgs.displaySurface = displaySurface;
creationArgs.hasWideColorGamut = false;
@@ -3268,7 +3318,6 @@
if (!transactionIsReadyToBeApplied(transaction.isAutoTimestamp,
transaction.desiredPresentTime,
transaction.states,
- false /* updateTransactionCounters*/,
pendingBuffers)) {
setTransactionFlags(eTransactionFlushNeeded);
break;
@@ -3293,13 +3342,9 @@
const auto& transaction = mTransactionQueue.front();
bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
mPendingTransactionQueues.end();
- // Call transactionIsReadyToBeApplied first in case we need to
- // incrementPendingBufferCount and keep track of pending buffers
- // if the transaction contains a buffer.
if (!transactionIsReadyToBeApplied(transaction.isAutoTimestamp,
transaction.desiredPresentTime,
transaction.states,
- true /* updateTransactionCounters */,
pendingBuffers) ||
pendingTransactions) {
mPendingTransactionQueues[transaction.applyToken].push(transaction);
@@ -3331,7 +3376,6 @@
bool SurfaceFlinger::transactionIsReadyToBeApplied(
bool isAutoTimestamp, int64_t desiredPresentTime, const Vector<ComposerState>& states,
- bool updateTransactionCounters,
std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers) {
const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
bool ready = true;
@@ -3370,10 +3414,6 @@
ATRACE_NAME("!isVsyncValidForUid");
ready = false;
}
- if (updateTransactionCounters) {
- // See BufferStateLayer::mPendingBufferTransactions
- layer->incrementPendingBufferCount();
- }
// If backpressure is enabled and we already have a buffer to commit, keep the transaction
// in the queue.
@@ -3464,6 +3504,13 @@
const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
ATRACE_CALL();
+ // Check for incoming buffer updates and increment the pending buffer count.
+ for (const auto& state : states) {
+ if ((state.state.what & layer_state_t::eAcquireFenceChanged) && (state.state.surface)) {
+ mBufferCountTracker.increment(state.state.surface->localBinder());
+ }
+ }
+
uint32_t permissions =
callingThreadHasUnscopedSurfaceFlingerAccess() ? Permission::ACCESS_SURFACE_FLINGER : 0;
// Avoid checking for rotation permissions if the caller already has ACCESS_SURFACE_FLINGER
@@ -4041,10 +4088,16 @@
std::move(metadata), format, handle, gbp, &layer);
break;
- case ISurfaceComposerClient::eFXSurfaceBufferState:
+ case ISurfaceComposerClient::eFXSurfaceBufferState: {
result = createBufferStateLayer(client, std::move(uniqueName), w, h, flags,
std::move(metadata), handle, &layer);
- break;
+ std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
+ if (pendingBufferCounter) {
+ std::string counterName = layer->getPendingBufferCounterName();
+ mBufferCountTracker.add((*handle)->localBinder(), counterName,
+ pendingBufferCounter);
+ }
+ } break;
case ISurfaceComposerClient::eFXSurfaceEffect:
// check if buffer size is set for color layer.
if (w > 0 || h > 0) {
@@ -4211,6 +4264,7 @@
auto it = mLayersByLocalBinderToken.begin();
while (it != mLayersByLocalBinderToken.end()) {
if (it->second == layer) {
+ mBufferCountTracker.remove(it->first->localBinder());
it = mLayersByLocalBinderToken.erase(it);
} else {
it++;
@@ -4949,8 +5003,6 @@
case GET_DISPLAYED_CONTENT_SAMPLE:
case NOTIFY_POWER_BOOST:
case SET_GLOBAL_SHADOW_SETTINGS:
- case ADD_FPS_LISTENER:
- case REMOVE_FPS_LISTENER:
case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
// ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the
// necessary permission dynamically. Don't use the permission cache for this check.
@@ -5013,6 +5065,8 @@
// This is not sensitive information, so should not require permission control.
return OK;
}
+ case ADD_FPS_LISTENER:
+ case REMOVE_FPS_LISTENER:
case ADD_REGION_SAMPLING_LISTENER:
case REMOVE_REGION_SAMPLING_LISTENER: {
// codes that require permission check
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 1615ab6..6434ca2 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -416,6 +416,43 @@
void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
};
+ // Keeps track of pending buffers per layer handle in the transaction queue or current/drawing
+ // state before the buffers are latched. The layer owns the atomic counters and decrements the
+ // count in the main thread when dropping or latching a buffer.
+ //
+ // The binder threads increment the same counter when a new transaction containing a buffer is
+ // added to the transaction queue. The map is updated with the layer handle lifecycle updates.
+ // This is done to avoid lock contention with the main thread.
+ class BufferCountTracker {
+ public:
+ void increment(BBinder* layerHandle) {
+ std::lock_guard<std::mutex> lock(mLock);
+ auto it = mCounterByLayerHandle.find(layerHandle);
+ if (it != mCounterByLayerHandle.end()) {
+ auto [name, pendingBuffers] = it->second;
+ int32_t count = ++(*pendingBuffers);
+ ATRACE_INT(name.c_str(), count);
+ } else {
+ ALOGW("Handle not found! %p", layerHandle);
+ }
+ }
+
+ void add(BBinder* layerHandle, const std::string& name, std::atomic<int32_t>* counter) {
+ std::lock_guard<std::mutex> lock(mLock);
+ mCounterByLayerHandle[layerHandle] = std::make_pair(name, counter);
+ }
+
+ void remove(BBinder* layerHandle) {
+ std::lock_guard<std::mutex> lock(mLock);
+ mCounterByLayerHandle.erase(layerHandle);
+ }
+
+ private:
+ std::mutex mLock;
+ std::unordered_map<BBinder*, std::pair<std::string, std::atomic<int32_t>*>>
+ mCounterByLayerHandle GUARDED_BY(mLock);
+ };
+
struct ActiveModeInfo {
DisplayModeId modeId;
Scheduler::ModeEvent event = Scheduler::ModeEvent::None;
@@ -764,7 +801,6 @@
void commitOffscreenLayers();
bool transactionIsReadyToBeApplied(
bool isAutoTimestamp, int64_t desiredPresentTime, const Vector<ComposerState>& states,
- bool updateTransactionCounters,
std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers)
REQUIRES(mStateLock);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
@@ -906,7 +942,8 @@
/*
* Display management
*/
- DisplayModes loadSupportedDisplayModes(PhysicalDisplayId) const;
+ void loadDisplayModes(PhysicalDisplayId displayId, DisplayModes& outModes,
+ DisplayModePtr& outActiveMode) const REQUIRES(mStateLock);
sp<DisplayDevice> setupNewDisplayDeviceInternal(
const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay,
@@ -1314,6 +1351,8 @@
int mFrameRateFlexibilityTokenCount = 0;
sp<IBinder> mDebugFrameRateFlexibilityToken;
+
+ BufferCountTracker mBufferCountTracker;
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 7e6141e..6150b22 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -915,7 +915,7 @@
// Packet - 3 : FrameEnd (ActualDisplayFrame)
const auto& packet3 = packets[3];
ASSERT_TRUE(packet3.has_timestamp());
- EXPECT_EQ(packet3.timestamp(), 26u);
+ EXPECT_EQ(packet3.timestamp(), 31u);
ASSERT_TRUE(packet3.has_frame_timeline_event());
const auto& event3 = packet3.frame_timeline_event();
@@ -969,7 +969,7 @@
// Packet - 1 : FrameEnd (ActualDisplayFrame)
const auto& packet1 = packets[1];
ASSERT_TRUE(packet1.has_timestamp());
- EXPECT_EQ(packet1.timestamp(), 26u);
+ EXPECT_EQ(packet1.timestamp(), 31u);
ASSERT_TRUE(packet1.has_frame_timeline_event());
const auto& event1 = packet1.frame_timeline_event();
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index bf9ec39..363bd80 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -368,6 +368,62 @@
EXPECT_EQ(0u, layer->mPendingJankClassifications.size());
}
+
+ void BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer() {
+ sp<BufferStateLayer> layer = createBufferStateLayer();
+
+ sp<Fence> fence1(new Fence());
+ auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
+ sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+ layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0});
+ EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ const auto droppedSurfaceFrame1 = layer->mCurrentState.bufferSurfaceFrameTX;
+
+ sp<Fence> fence2(new Fence());
+ auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
+ sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+ auto dropStartTime1 = systemTime();
+ layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0});
+ auto dropEndTime1 = systemTime();
+ EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ const auto droppedSurfaceFrame2 = layer->mCurrentState.bufferSurfaceFrameTX;
+
+ sp<Fence> fence3(new Fence());
+ auto acquireFence3 = fenceFactory.createFenceTimeForTest(fence3);
+ sp<GraphicBuffer> buffer3{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+ auto dropStartTime2 = systemTime();
+ layer->setBuffer(buffer3, fence3, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ 2, /*inputEventId*/ 0});
+ auto dropEndTime2 = systemTime();
+ acquireFence3->signalForTest(12);
+
+ EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ const auto& presentedSurfaceFrame = layer->mCurrentState.bufferSurfaceFrameTX;
+
+ commitTransaction(layer.get());
+ bool computeVisisbleRegions;
+ layer->updateTexImage(computeVisisbleRegions, 15, 0);
+
+ EXPECT_EQ(1, droppedSurfaceFrame1->getToken());
+ EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame1->getPresentState());
+ EXPECT_EQ(0u, droppedSurfaceFrame1->getActuals().endTime);
+ auto dropTime1 = droppedSurfaceFrame1->getDropTime();
+ EXPECT_TRUE(dropTime1 > dropStartTime1 && dropTime1 < dropEndTime1);
+
+ EXPECT_EQ(FrameTimelineInfo::INVALID_VSYNC_ID, droppedSurfaceFrame2->getToken());
+ EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame2->getPresentState());
+ EXPECT_EQ(0u, droppedSurfaceFrame2->getActuals().endTime);
+ auto dropTime2 = droppedSurfaceFrame2->getDropTime();
+ EXPECT_TRUE(dropTime2 > dropStartTime2 && dropTime2 < dropEndTime2);
+
+ EXPECT_EQ(2, presentedSurfaceFrame->getToken());
+ EXPECT_EQ(PresentState::Presented, presentedSurfaceFrame->getPresentState());
+ }
};
TEST_F(TransactionSurfaceFrameTest, PresentedBufferlessSurfaceFrame) {
@@ -407,4 +463,10 @@
TEST_F(TransactionSurfaceFrameTest, PendingSurfaceFramesRemovedAfterClassification) {
PendingSurfaceFramesRemovedAfterClassification();
}
+
+TEST_F(TransactionSurfaceFrameTest,
+ BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer) {
+ BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer();
+}
+
} // namespace android
\ No newline at end of file
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index bfc240e..a513239 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -1196,10 +1196,10 @@
std::string* errors) {
*t = T();
Json::Value object(Json::objectValue);
- Json::Reader reader;
- reader.parse(json, object, false);
- if (!object) {
- if (errors) errors->assign(reader.getFormatedErrorMessages());
+ Json::CharReaderBuilder builder;
+ builder["collectComments"] = false;
+ std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
+ if (!reader->parse(json.data(), json.data() + json.size(), &object, errors)) {
return false;
}
return AsValue(&object, t);