Merge "LayerStats: some changes for referencings" into pi-dev
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 21d9ace..f65f4f8 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -105,7 +105,6 @@
{ "video", "Video", ATRACE_TAG_VIDEO, { } },
{ "camera", "Camera", ATRACE_TAG_CAMERA, { } },
{ "hal", "Hardware Modules", ATRACE_TAG_HAL, { } },
- { "app", "Application", ATRACE_TAG_APP, { } },
{ "res", "Resource Loading", ATRACE_TAG_RESOURCES, { } },
{ "dalvik", "Dalvik VM", ATRACE_TAG_DALVIK, { } },
{ "rs", "RenderScript", ATRACE_TAG_RS, { } },
diff --git a/data/etc/go_handheld_core_hardware.xml b/data/etc/go_handheld_core_hardware.xml
new file mode 100644
index 0000000..ffebc9f
--- /dev/null
+++ b/data/etc/go_handheld_core_hardware.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- These are the hardware components that all Android Go handheld devices
+ must include. Devices with optional hardware must also include extra
+ hardware files, per the comments below.
+-->
+<permissions>
+ <!-- This is Android and fully CTS compatible. Basically this is for CTS tests to use. -->
+ <feature name="android.software.cts" />
+
+ <feature name="android.hardware.audio.output" />
+ <feature name="android.hardware.camera" />
+ <feature name="android.hardware.location" />
+ <feature name="android.hardware.location.network" />
+ <feature name="android.hardware.sensor.compass" />
+ <feature name="android.hardware.sensor.accelerometer" />
+ <feature name="android.hardware.bluetooth" />
+ <feature name="android.hardware.touchscreen" />
+ <feature name="android.hardware.microphone" />
+ <feature name="android.hardware.screen.portrait" />
+ <feature name="android.hardware.screen.landscape" />
+
+ <!-- basic system services -->
+ <feature name="android.software.connectionservice" />
+ <feature name="android.software.backup" />
+ <feature name="android.software.home_screen" />
+ <feature name="android.software.input_methods" />
+ <feature name="android.software.print" />
+ <feature name="android.software.companion_device_setup" />
+ <feature name="android.software.autofill" />
+
+ <!-- Feature to specify if the device supports adding device admins. -->
+ <feature name="android.software.device_admin" />
+
+ <!-- Devices with all optimizations required to support VR Mode and
+ pass all CDD requirements for this feature may include
+ android.hardware.vr.high_performance -->
+ <!-- Devices that support VR headtracking features and pass all CDD
+ requirements may include
+ android.hardware.vr.headtracking -->
+
+ <!-- devices with GPS must include android.hardware.location.gps.xml -->
+ <!-- devices with an autofocus camera and/or flash must include either
+ android.hardware.camera.autofocus.xml or
+ android.hardware.camera.autofocus-flash.xml -->
+ <!-- devices with a front facing camera must include
+ android.hardware.camera.front.xml -->
+ <!-- devices with WiFi must also include android.hardware.wifi.xml -->
+ <!-- devices that support multitouch must include the most appropriate one
+ of these files:
+
+ If only partial (non-independent) pointers are supported:
+ android.hardware.touchscreen.multitouch.xml
+
+ If up to 4 independently tracked pointers are supported:
+ include android.hardware.touchscreen.multitouch.distinct.xml
+
+ If 5 or more independently tracked pointers are supported:
+ include android.hardware.touchscreen.multitouch.jazzhand.xml
+
+ ONLY ONE of the above should be included. -->
+ <!-- devices with an ambient light sensor must also include
+ android.hardware.sensor.light.xml -->
+ <!-- devices with a proximity sensor must also include
+ android.hardware.sensor.proximity.xml -->
+ <!-- GSM phones must also include android.hardware.telephony.gsm.xml -->
+ <!-- CDMA phones must also include android.hardware.telephony.cdma.xml -->
+ <!-- Devices that have low-latency audio stacks suitable for apps like
+ VoIP may include android.hardware.audio.low_latency.xml. ONLY apps
+ that meet the requirements specified in the CDD may include this. -->
+</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 6d739a1..c76e611 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -14,9 +14,9 @@
limitations under the License.
-->
-<!-- These are the hardware components that all handheld devices
- must include. Devices with optional hardware must also include extra
- hardware files, per the comments below.
+<!-- These are the hardware components that all handheld devices except Android Go
+ must include, for Android Go devices include go_handheld_core_hardware.xml.
+ Devices with optional hardware must also include extra hardware files, per the comments below.
Handheld devices include phones, mobile Internet devices (MIDs),
Personal Media Players (PMPs), small tablets (7" or less), and similar
diff --git a/headers/media_plugin/media/cas/CasAPI.h b/headers/media_plugin/media/cas/CasAPI.h
index 67f4511..4de314d 100644
--- a/headers/media_plugin/media/cas/CasAPI.h
+++ b/headers/media_plugin/media/cas/CasAPI.h
@@ -63,7 +63,7 @@
// Construct a new instance of a CasPlugin given a CA_system_id
virtual status_t createPlugin(
int32_t CA_system_id,
- uint64_t appData,
+ void *appData,
CasPluginCallback callback,
CasPlugin **plugin) = 0;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index f739f07..2d196c1 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1276,7 +1276,7 @@
if (err) return err;
// payload
- void* const buf = this->writeInplace(pad_size(len));
+ void* const buf = this->writeInplace(len);
if (buf == NULL)
return BAD_VALUE;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index bbf681e..63560c4 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -101,7 +101,8 @@
SurfaceComposerClient::Transaction::Transaction(const Transaction& other) :
mForceSynchronous(other.mForceSynchronous),
mTransactionNestCount(other.mTransactionNestCount),
- mAnimation(other.mAnimation) {
+ mAnimation(other.mAnimation),
+ mEarlyWakeup(other.mEarlyWakeup) {
mDisplayStates = other.mDisplayStates;
mComposerStates = other.mComposerStates;
}
@@ -157,9 +158,13 @@
if (mAnimation) {
flags |= ISurfaceComposer::eAnimation;
}
+ if (mEarlyWakeup) {
+ flags |= ISurfaceComposer::eEarlyWakeup;
+ }
mForceSynchronous = false;
mAnimation = false;
+ mEarlyWakeup = false;
sf->setTransactionState(composerStates, displayStates, flags);
mStatus = NO_ERROR;
@@ -185,6 +190,10 @@
mAnimation = true;
}
+void SurfaceComposerClient::Transaction::setEarlyWakeup() {
+ mEarlyWakeup = true;
+}
+
layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
if (mComposerStates.count(sc) == 0) {
// we don't have it, add an initialized layer_state to our list
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 3591090..e401572 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -61,6 +61,11 @@
enum {
eSynchronous = 0x01,
eAnimation = 0x02,
+
+ // Indicates that this transaction will likely result in a lot of layers being composed, and
+ // thus, SurfaceFlinger should wake-up earlier to avoid missing frame deadlines. In this
+ // case SurfaceFlinger will wake up at (sf vsync offset - debug.sf.early_phase_offset_ns)
+ eEarlyWakeup = 0x04
};
enum {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index ffc22f6..377fe68 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -154,6 +154,7 @@
uint32_t mForceSynchronous = 0;
uint32_t mTransactionNestCount = 0;
bool mAnimation = false;
+ bool mEarlyWakeup = false;
int mStatus = NO_ERROR;
@@ -273,6 +274,7 @@
const Rect& displayRect);
void setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height);
void setAnimationTransaction();
+ void setEarlyWakeup();
};
status_t destroySurface(const sp<IBinder>& id);
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 2598451..765dcd9 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -113,7 +113,10 @@
constexpr int32_t kAllTransformBits =
ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL |
ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL |
- ANATIVEWINDOW_TRANSFORM_ROTATE_90;
+ ANATIVEWINDOW_TRANSFORM_ROTATE_90 |
+ // We don't expose INVERSE_DISPLAY as an NDK constant, but someone could have read it
+ // from a buffer already set by Camera framework, so we allow it to be forwarded.
+ NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
if (!window || !query(window, NATIVE_WINDOW_IS_VALID))
return -EINVAL;
if ((transform & ~kAllTransformBits) != 0)
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 36da084..fe4ae6c 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -838,6 +838,11 @@
}
Region::const_iterator Region::end() const {
+ // Workaround for b/77643177
+ // mStorage should never be empty, but somehow it is and it's causing
+ // an abort in ubsan
+ if (mStorage.isEmpty()) return mStorage.array();
+
size_t numRects = isRect() ? 1 : mStorage.size() - 1;
return mStorage.array() + numRects;
}
diff --git a/services/displayservice/DisplayEventReceiver.cpp b/services/displayservice/DisplayEventReceiver.cpp
index 5993e44..2bb74c2 100644
--- a/services/displayservice/DisplayEventReceiver.cpp
+++ b/services/displayservice/DisplayEventReceiver.cpp
@@ -102,10 +102,20 @@
switch(buf[i].header.type) {
case FwkReceiver::DISPLAY_EVENT_VSYNC: {
- mCallback->onVsync(timestamp, event.vsync.count);
+ auto ret = mCallback->onVsync(timestamp, event.vsync.count);
+ if (!ret.isOk()) {
+ LOG(ERROR) << "AttachedEvent handleEvent fails on onVsync callback"
+ << " because of " << ret.description();
+ return 0; // remove the callback
+ }
} break;
case FwkReceiver::DISPLAY_EVENT_HOTPLUG: {
- mCallback->onHotplug(timestamp, event.hotplug.connected);
+ auto ret = mCallback->onHotplug(timestamp, event.hotplug.connected);
+ if (!ret.isOk()) {
+ LOG(ERROR) << "AttachedEvent handleEvent fails on onHotplug callback"
+ << " because of " << ret.description();
+ return 0; // remove the callback
+ }
} break;
default: {
LOG(ERROR) << "AttachedEvent handleEvent unknown type: " << type;
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 82300e6..2aa4cd3 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -778,6 +778,7 @@
}
void BufferLayer::drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const {
+ ATRACE_CALL();
const State& s(getDrawingState());
computeGeometry(renderArea, getBE().mMesh, useIdentityTransform);
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 6b4f5db..87333d0 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -356,6 +356,7 @@
}
status_t BufferLayerConsumer::bindTextureImageLocked() {
+ ATRACE_CALL();
mRE.checkErrors();
if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) {
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index 80a90a7..c87b669 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -44,13 +44,13 @@
void ColorLayer::onDraw(const RenderArea& renderArea, const Region& /* clip */,
bool useIdentityTransform) const {
- const State& s(getDrawingState());
- if (s.color.a > 0) {
+ half4 color = getColor();
+ if (color.a > 0) {
Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2);
computeGeometry(renderArea, mesh, useIdentityTransform);
auto& engine(mFlinger->getRenderEngine());
engine.setupLayerBlending(getPremultipledAlpha(), false /* opaque */,
- true /* disableTexture */, s.color);
+ true /* disableTexture */, color);
engine.drawMesh(mesh);
engine.disableBlending();
}
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp
index 9e01fd0..7acbd11 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/DispSync.cpp
@@ -209,6 +209,28 @@
return BAD_VALUE;
}
+ status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
+ if (kTraceDetailedInfo) ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+
+ for (size_t i = 0; i < mEventListeners.size(); i++) {
+ if (mEventListeners[i].mCallback == callback) {
+ EventListener& listener = mEventListeners.editItemAt(i);
+ const nsecs_t oldPhase = listener.mPhase;
+ listener.mPhase = phase;
+
+ // Pretend that the last time this event was handled at the same frame but with the
+ // new offset to allow for a seamless offset change without double-firing or
+ // skipping.
+ listener.mLastEventTime -= (oldPhase - phase);
+ mCond.signal();
+ return NO_ERROR;
+ }
+ }
+
+ return BAD_VALUE;
+ }
+
// This method is only here to handle the !SurfaceFlinger::hasSyncFramework
// case.
bool hasAnyEventListeners() {
@@ -487,6 +509,11 @@
return mThread->removeEventListener(callback);
}
+status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) {
+ Mutex::Autolock lock(mMutex);
+ return mThread->changePhaseOffset(callback, phase);
+}
+
void DispSync::setPeriod(nsecs_t period) {
Mutex::Autolock lock(mMutex);
mPeriod = period;
diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/DispSync.h
index 9336f4d..077256a 100644
--- a/services/surfaceflinger/DispSync.h
+++ b/services/surfaceflinger/DispSync.h
@@ -113,6 +113,11 @@
// DispSync object.
status_t removeEventListener(Callback* callback);
+ // changePhaseOffset changes the phase offset of an already-registered event callback. The
+ // method will make sure that there is no skipping or double-firing on the listener per frame,
+ // even when changing the offsets multiple times.
+ status_t changePhaseOffset(Callback* callback, nsecs_t phase);
+
// computeNextRefresh computes when the next refresh is expected to begin.
// The periodOffset value can be used to move forward or backward; an
// offset of zero is the next refresh, -1 is the previous refresh, 1 is
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
index 1fc3100..0fb3d28 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
@@ -293,6 +293,7 @@
}
void GLES20RenderEngine::drawMesh(const Mesh& mesh) {
+ ATRACE_CALL();
if (mesh.getTexCoordsSize()) {
glEnableVertexAttribArray(Program::texCoords);
glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE,
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5be7951..183c1eb 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -296,6 +296,12 @@
auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
+ property_get("debug.sf.early_phase_offset_ns", value, "0");
+ const int earlyWakeupOffsetOffsetNs = atoi(value);
+ ALOGI_IF(earlyWakeupOffsetOffsetNs != 0, "Enabling separate early offset");
+ mVsyncModulator.setPhaseOffsets(sfVsyncPhaseOffsetNs - earlyWakeupOffsetOffsetNs,
+ sfVsyncPhaseOffsetNs);
+
// We should be reading 'persist.sys.sf.color_saturation' here
// but since /data may be encrypted, we need to wait until after vold
// comes online to attempt to read the property. The property is
@@ -522,19 +528,10 @@
return;
}
- // Remove the listener with the old offset
- status_t err = mDispSync->removeEventListener(
- static_cast<DispSync::Callback*>(this));
+ status_t err = mDispSync->changePhaseOffset(static_cast<DispSync::Callback*>(this),
+ mPhaseOffset);
if (err != NO_ERROR) {
- ALOGE("error unregistering vsync callback: %s (%d)",
- strerror(-err), err);
- }
-
- // Add a listener with the new offset
- err = mDispSync->addEventListener(mName, mPhaseOffset,
- static_cast<DispSync::Callback*>(this));
- if (err != NO_ERROR) {
- ALOGE("error registering vsync callback: %s (%d)",
+ ALOGE("error changing vsync offset: %s (%d)",
strerror(-err), err);
}
}
@@ -623,6 +620,7 @@
mSFEventThread = std::make_unique<impl::EventThread>(mSfEventThreadSource.get(), *this, true,
"sfEventThread");
mEventQueue->setEventThread(mSFEventThread.get());
+ mVsyncModulator.setEventThread(mSFEventThread.get());
// Get a RenderEngine for the given display / config (can't fail)
getBE().mRenderEngine =
@@ -1498,6 +1496,7 @@
mHadClientComposition = mHadClientComposition ||
getBE().mHwc->hasClientComposition(displayDevice->getHwcDisplayId());
}
+ mVsyncModulator.setLastFrameUsedRenderEngine(mHadClientComposition);
mLayersWithQueuedFrames.clear();
}
@@ -2122,6 +2121,7 @@
// with mStateLock held to guarantee that mCurrentState won't change
// until the transaction is committed.
+ mVsyncModulator.setTransactionStart(VSyncModulator::TransactionStart::NORMAL);
transactionFlags = getTransactionFlags(eTransactionMask);
handleTransactionLocked(transactionFlags);
@@ -3070,7 +3070,13 @@
}
uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
+ return setTransactionFlags(flags, VSyncModulator::TransactionStart::NORMAL);
+}
+
+uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags,
+ VSyncModulator::TransactionStart transactionStart) {
uint32_t old = android_atomic_or(flags, &mTransactionFlags);
+ mVsyncModulator.setTransactionStart(transactionStart);
if ((old & flags)==0) { // wake the server up
signalTransaction();
}
@@ -3159,7 +3165,10 @@
}
// this triggers the transaction
- setTransactionFlags(transactionFlags);
+ const auto start = (flags & eEarlyWakeup)
+ ? VSyncModulator::TransactionStart::EARLY
+ : VSyncModulator::TransactionStart::NORMAL;
+ setTransactionFlags(transactionFlags, start);
// if this is a synchronous transaction, wait for it to take effect
// before returning.
@@ -4099,9 +4108,9 @@
colorizer.bold(result);
result.append("DispSync configuration: ");
colorizer.reset(result);
- result.appendFormat("app phase %" PRId64 " ns, sf phase %" PRId64 " ns, "
- "present offset %" PRId64 " ns (refresh %" PRId64 " ns)",
- vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs,
+ result.appendFormat("app phase %" PRId64 " ns, sf phase %" PRId64 " ns, early sf phase %" PRId64
+ " ns, present offset %" PRId64 " ns (refresh %" PRId64 " ns)",
+ vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, mVsyncModulator.getEarlyPhaseOffset(),
dispSyncPresentTimeOffset, activeConfig->getVsyncPeriod());
result.append("\n");
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 89c9cfd..a29d1d7 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -33,6 +33,7 @@
#include <utils/RefBase.h>
#include <utils/SortedVector.h>
#include <utils/threads.h>
+#include <utils/Trace.h>
#include <ui/FenceTime.h>
#include <ui/PixelFormat.h>
@@ -54,6 +55,7 @@
#include "Barrier.h"
#include "DisplayDevice.h"
#include "DispSync.h"
+#include "EventThread.h"
#include "FrameTracker.h"
#include "LayerStats.h"
#include "LayerVector.h"
@@ -61,6 +63,7 @@
#include "SurfaceInterceptor.h"
#include "SurfaceTracing.h"
#include "StartPropertySetThread.h"
+#include "VSyncModulator.h"
#include "DisplayHardware/HWC2.h"
#include "DisplayHardware/HWComposer.h"
@@ -492,6 +495,7 @@
uint32_t peekTransactionFlags();
// Can only be called from the main thread or with mStateLock held
uint32_t setTransactionFlags(uint32_t flags);
+ uint32_t setTransactionFlags(uint32_t flags, VSyncModulator::TransactionStart transactionStart);
void commitTransaction();
bool containsAnyInvalidClientState(const Vector<ComposerState>& states);
uint32_t setClientStateLocked(const ComposerState& composerState);
@@ -765,6 +769,8 @@
std::unique_ptr<EventControlThread> mEventControlThread;
sp<IBinder> mBuiltinDisplays[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES];
+ VSyncModulator mVsyncModulator;
+
// Can only accessed from the main thread, these members
// don't need synchronization
State mDrawingState{LayerVector::StateSet::Drawing};
diff --git a/services/surfaceflinger/VSyncModulator.h b/services/surfaceflinger/VSyncModulator.h
new file mode 100644
index 0000000..3126deb
--- /dev/null
+++ b/services/surfaceflinger/VSyncModulator.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/Errors.h>
+
+#include <mutex>
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+/*
+ * Modulates the vsync-offsets depending on current SurfaceFlinger state.
+ */
+class VSyncModulator {
+public:
+
+ enum TransactionStart {
+ EARLY,
+ NORMAL
+ };
+
+ // Sets the phase offsets
+ //
+ // early: the phase offset when waking up early. May be the same as late, in which case we don't
+ // shift offsets.
+ // late: the regular sf phase offset.
+ void setPhaseOffsets(nsecs_t early, nsecs_t late) {
+ mEarlyPhaseOffset = early;
+ mLatePhaseOffset = late;
+ mPhaseOffset = late;
+ }
+
+ nsecs_t getEarlyPhaseOffset() const {
+ return mEarlyPhaseOffset;
+ }
+
+ void setEventThread(EventThread* eventThread) {
+ mEventThread = eventThread;
+ }
+
+ void setTransactionStart(TransactionStart transactionStart) {
+ if (transactionStart == mTransactionStart) return;
+ mTransactionStart = transactionStart;
+ updatePhaseOffsets();
+ }
+
+ void setLastFrameUsedRenderEngine(bool re) {
+ if (re == mLastFrameUsedRenderEngine) return;
+ mLastFrameUsedRenderEngine = re;
+ updatePhaseOffsets();
+ }
+
+private:
+
+ void updatePhaseOffsets() {
+
+ // Do not change phase offsets if disabled.
+ if (mEarlyPhaseOffset == mLatePhaseOffset) return;
+
+ if (mTransactionStart == TransactionStart::EARLY || mLastFrameUsedRenderEngine) {
+ if (mPhaseOffset != mEarlyPhaseOffset) {
+ if (mEventThread) {
+ mEventThread->setPhaseOffset(mEarlyPhaseOffset);
+ }
+ mPhaseOffset = mEarlyPhaseOffset;
+ }
+ } else {
+ if (mPhaseOffset != mLatePhaseOffset) {
+ if (mEventThread) {
+ mEventThread->setPhaseOffset(mLatePhaseOffset);
+ }
+ mPhaseOffset = mLatePhaseOffset;
+ }
+ }
+ }
+
+ nsecs_t mLatePhaseOffset = 0;
+ nsecs_t mEarlyPhaseOffset = 0;
+ EventThread* mEventThread = nullptr;
+ std::atomic<nsecs_t> mPhaseOffset = 0;
+ std::atomic<TransactionStart> mTransactionStart = TransactionStart::NORMAL;
+ std::atomic<bool> mLastFrameUsedRenderEngine = false;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 4e9db72..176c691 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -1008,6 +1008,32 @@
tolerance);
}
+TEST_F(LayerTransactionTest, SetColorWithParentAlpha_Bug74220420) {
+ sp<SurfaceControl> bufferLayer;
+ sp<SurfaceControl> parentLayer;
+ sp<SurfaceControl> colorLayer;
+ ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parentWithAlpha", 32, 32));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED));
+ ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer(
+ "childWithColor", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
+
+ const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
+ const float alpha = 0.25f;
+ const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
+ // this is handwavy, but the precision loss scaled by 255 (8-bit per
+ // channel) should be less than one
+ const uint8_t tolerance = 1;
+ Transaction()
+ .reparent(colorLayer, parentLayer->getHandle())
+ .setColor(colorLayer, color)
+ .setAlpha(parentLayer, alpha)
+ .setLayer(parentLayer, mLayerZBase + 1)
+ .apply();
+ screenshot()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
+ tolerance);
+}
+
TEST_F(LayerTransactionTest, SetColorWithBuffer) {
sp<SurfaceControl> bufferLayer;
ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));