Add SurfaceInterceptor initial implementation
SurfaceInterceptor allows capturing streams of surface updates
that come into SurfaceFlinger. It captures the creation and
deletion of surfaces, changes to a surface like size or position,
buffer updates, and hw vsync updates (for a complete list of what
is captured see
/frameworks/native/cmds/surfacecapturereplay/proto/src/trace.proto)
SurfaceInterceptor uses a protocol buffer object to store each
trace. This allows for the produced trace to be either played back
into SurfaceFlinger or manually inspected.
To enable the interceptor: service call SurfaceFlinger 1020 i32 1
To disable the interceptor: service call SurfaceFlinger 1020 i32 0
Each trace file will be saved to /data/SurfaceTrace.dat
For SurfaceInterceptor to work SELinux must be set to permissive.
Change-Id: I5adff9dcd19456d86cfd5b1f51096dff9990e086
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 2a8fa4a..ad62ba6 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -18,6 +18,7 @@
MessageQueue.cpp \
MonitoredProducer.cpp \
SurfaceFlingerConsumer.cpp \
+ SurfaceInterceptor.cpp \
Transform.cpp \
DisplayHardware/FramebufferSurface.cpp \
DisplayHardware/HWC2.cpp \
@@ -36,11 +37,12 @@
RenderEngine/Texture.cpp \
RenderEngine/GLES10RenderEngine.cpp \
RenderEngine/GLES11RenderEngine.cpp \
- RenderEngine/GLES20RenderEngine.cpp
+ RenderEngine/GLES20RenderEngine.cpp \
+LOCAL_MODULE := libsurfaceflinger
LOCAL_C_INCLUDES := \
- frameworks/native/vulkan/include \
- external/vulkan-validation-layers/libs/vkjson
+ frameworks/native/vulkan/include \
+ external/vulkan-validation-layers/libs/vkjson \
LOCAL_CFLAGS := -DLOG_TAG=\"SurfaceFlinger\"
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
@@ -108,7 +110,7 @@
LOCAL_CFLAGS += -fvisibility=hidden -Werror=format
LOCAL_CFLAGS += -std=c++14
-LOCAL_STATIC_LIBRARIES := libvkjson
+LOCAL_STATIC_LIBRARIES := libtrace_proto libvkjson
LOCAL_SHARED_LIBRARIES := \
libcutils \
liblog \
@@ -122,9 +124,8 @@
libui \
libgui \
libpowermanager \
- libvulkan
-
-LOCAL_MODULE := libsurfaceflinger
+ libvulkan \
+ libprotobuf-cpp-full
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
@@ -158,6 +159,7 @@
libdl
LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
+LOCAL_STATIC_LIBRARIES := libtrace_proto
LOCAL_MODULE := surfaceflinger
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
new file mode 100644
index 0000000..c860228
--- /dev/null
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -0,0 +1,411 @@
+/*
+ * Copyright 2016 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 "SurfaceInterceptor"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "Layer.h"
+#include "SurfaceFlinger.h"
+#include "SurfaceInterceptor.h"
+
+#include <cutils/log.h>
+
+#include <utils/Trace.h>
+
+#include <fstream>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+void SurfaceInterceptor::enable(const SortedVector<sp<Layer>>& layers) {
+ ATRACE_CALL();
+ if (mEnabled) {
+ return;
+ }
+ mEnabled = true;
+ saveExistingLayers(layers);
+}
+
+void SurfaceInterceptor::disable() {
+ ATRACE_CALL();
+ if (!mEnabled) {
+ return;
+ }
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ mEnabled = false;
+ status_t err(writeProtoFileLocked());
+ ALOGE_IF(err == PERMISSION_DENIED, "Could not save the proto file! Permission denied");
+ ALOGE_IF(err == NOT_ENOUGH_DATA, "Could not save the proto file! There are missing fields");
+ mTrace.Clear();
+}
+
+void SurfaceInterceptor::saveExistingLayers(const SortedVector<sp<Layer>>& layers) {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ for (const auto& layer : layers) {
+ saveLayerCreateLocked(layer);
+ saveInitialLayerStateLocked(layer);
+ }
+}
+
+void SurfaceInterceptor::saveInitialLayerStateLocked(const sp<const Layer>& layer) {
+ ATRACE_CALL();
+ if (layer == nullptr) {
+ return;
+ }
+ Increment* increment(addTraceIncrementLocked());
+ Transaction* transaction(increment->mutable_transaction());
+ transaction->set_synchronous(layer->mTransactionFlags & BnSurfaceComposer::eSynchronous);
+ transaction->set_animation(layer->mTransactionFlags & BnSurfaceComposer::eAnimation);
+
+ const int32_t layerId(getLayerId(layer));
+ addPositionLocked(transaction, layerId, layer->mCurrentState.active.transform.tx(),
+ layer->mCurrentState.active.transform.ty());
+ addDepthLocked(transaction, layerId, layer->mCurrentState.z);
+ addSizeLocked(transaction, layerId, layer->mCurrentState.active.w,
+ layer->mCurrentState.active.h);
+ addAlphaLocked(transaction, layerId, layer->mCurrentState.alpha);
+ addTransparentRegionLocked(transaction, layerId, layer->mCurrentState.activeTransparentRegion);
+ addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack);
+ addCropLocked(transaction, layerId, layer->mCurrentState.crop);
+ if (layer->mCurrentState.handle != NULL) {
+ addDeferTransactionLocked(transaction, layerId, layer->mCurrentState.handle,
+ layer->mCurrentState.frameNumber);
+ }
+ addFinalCropLocked(transaction, layerId, layer->mCurrentState.finalCrop);
+ addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode());
+ addFlagsLocked(transaction, layerId, layer->mCurrentState.flags);
+}
+
+status_t SurfaceInterceptor::writeProtoFileLocked() {
+ ATRACE_CALL();
+ std::ofstream output(mOutputFileName, std::ios::out | std::ios::trunc | std::ios::binary);
+ // SerializeToOstream returns false when it's missing required data or when it could not write
+ if (!mTrace.IsInitialized()) {
+ return NOT_ENOUGH_DATA;
+ }
+ if (!mTrace.SerializeToOstream(&output)) {
+ return PERMISSION_DENIED;
+ }
+ return NO_ERROR;
+}
+
+void SurfaceInterceptor::setOutputFileName(const std::string& outputFileName) {
+ mOutputFileName = outputFileName;
+}
+
+const sp<const Layer> SurfaceInterceptor::getLayer(const sp<const IBinder>& handle) {
+ const auto layerHandle(static_cast<const Layer::Handle*>(handle.get()));
+ const sp<const Layer> layer(layerHandle->owner.promote());
+ // layer could be a nullptr at this point
+ return layer;
+}
+
+const std::string SurfaceInterceptor::getLayerName(const sp<const Layer>& layer) {
+ return layer->getName().string();
+}
+
+int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) {
+ return layer->sequence;
+}
+
+Increment* SurfaceInterceptor::addTraceIncrementLocked() {
+ Increment* increment(mTrace.add_increment());
+ increment->set_time_stamp(systemTime());
+ return increment;
+}
+
+Change* SurfaceInterceptor::addChangeLocked(Transaction* transaction, int32_t layerId) {
+ Change* change(transaction->add_change());
+ change->set_id(layerId);
+ return change;
+}
+
+void SurfaceInterceptor::setProtoRectLocked(Rectangle* protoRect, const Rect& rect) {
+ protoRect->set_left(rect.left);
+ protoRect->set_top(rect.top);
+ protoRect->set_right(rect.right);
+ protoRect->set_bottom(rect.bottom);
+}
+
+void SurfaceInterceptor::addPositionLocked(Transaction* transaction, int32_t layerId, float x,
+ float y)
+{
+ Change* change(addChangeLocked(transaction, layerId));
+ PositionChange* posChange(change->mutable_position());
+ posChange->set_x(x);
+ posChange->set_y(y);
+}
+
+void SurfaceInterceptor::addDepthLocked(Transaction* transaction, int32_t layerId, uint32_t z) {
+ Change* change(addChangeLocked(transaction, layerId));
+ LayerChange* depthChange(change->mutable_layer());
+ depthChange->set_layer(z);
+}
+
+void SurfaceInterceptor::addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w,
+ uint32_t h)
+{
+ Change* change(addChangeLocked(transaction, layerId));
+ SizeChange* sizeChange(change->mutable_size());
+ sizeChange->set_w(w);
+ sizeChange->set_h(h);
+}
+
+void SurfaceInterceptor::addAlphaLocked(Transaction* transaction, int32_t layerId, float alpha) {
+ Change* change(addChangeLocked(transaction, layerId));
+ AlphaChange* alphaChange(change->mutable_alpha());
+ alphaChange->set_alpha(alpha);
+}
+
+void SurfaceInterceptor::addMatrixLocked(Transaction* transaction, int32_t layerId,
+ const layer_state_t::matrix22_t& matrix)
+{
+ Change* change(addChangeLocked(transaction, layerId));
+ MatrixChange* matrixChange(change->mutable_matrix());
+ matrixChange->set_dsdx(matrix.dsdx);
+ matrixChange->set_dtdx(matrix.dtdx);
+ matrixChange->set_dsdy(matrix.dsdy);
+ matrixChange->set_dtdy(matrix.dtdy);
+}
+
+void SurfaceInterceptor::addTransparentRegionLocked(Transaction* transaction, int32_t layerId,
+ const Region& transRegion)
+{
+ Change* change(addChangeLocked(transaction, layerId));
+ TransparentRegionHintChange* transparentChange(change->mutable_transparent_region_hint());
+
+ for (const auto& rect : transRegion) {
+ Rectangle* protoRect(transparentChange->add_region());
+ setProtoRectLocked(protoRect, rect);
+ }
+}
+
+void SurfaceInterceptor::addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags) {
+ // There can be multiple flags changed
+ if (flags & layer_state_t::eLayerHidden) {
+ Change* change(addChangeLocked(transaction, layerId));
+ HiddenFlagChange* flagChange(change->mutable_hidden_flag());
+ flagChange->set_hidden_flag(true);
+ }
+ if (flags & layer_state_t::eLayerOpaque) {
+ Change* change(addChangeLocked(transaction, layerId));
+ OpaqueFlagChange* flagChange(change->mutable_opaque_flag());
+ flagChange->set_opaque_flag(true);
+ }
+ if (flags & layer_state_t::eLayerSecure) {
+ Change* change(addChangeLocked(transaction, layerId));
+ SecureFlagChange* flagChange(change->mutable_secure_flag());
+ flagChange->set_secure_flag(true);
+ }
+}
+
+void SurfaceInterceptor::addLayerStackLocked(Transaction* transaction, int32_t layerId,
+ uint32_t layerStack)
+{
+ Change* change(addChangeLocked(transaction, layerId));
+ LayerStackChange* layerStackChange(change->mutable_layer_stack());
+ layerStackChange->set_layer_stack(layerStack);
+}
+
+void SurfaceInterceptor::addCropLocked(Transaction* transaction, int32_t layerId,
+ const Rect& rect)
+{
+ Change* change(addChangeLocked(transaction, layerId));
+ CropChange* cropChange(change->mutable_crop());
+ Rectangle* protoRect(cropChange->mutable_rectangle());
+ setProtoRectLocked(protoRect, rect);
+}
+
+void SurfaceInterceptor::addFinalCropLocked(Transaction* transaction, int32_t layerId,
+ const Rect& rect)
+{
+ Change* change(addChangeLocked(transaction, layerId));
+ FinalCropChange* finalCropChange(change->mutable_final_crop());
+ Rectangle* protoRect(finalCropChange->mutable_rectangle());
+ setProtoRectLocked(protoRect, rect);
+}
+
+void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
+ const sp<const IBinder>& handle, uint64_t frameNumber)
+{
+ Change* change(addChangeLocked(transaction, layerId));
+ const sp<const Layer> layer(getLayer(handle));
+ if (layer == nullptr) {
+ ALOGE("An existing layer could not be retrieved with the handle"
+ " for the deferred transaction");
+ return;
+ }
+ DeferredTransactionChange* deferTransaction(change->mutable_deferred_transaction());
+ deferTransaction->set_layer_id(getLayerId(layer));
+ deferTransaction->set_frame_number(frameNumber);
+}
+
+void SurfaceInterceptor::addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId,
+ int32_t overrideScalingMode)
+{
+ Change* change(addChangeLocked(transaction, layerId));
+ OverrideScalingModeChange* overrideChange(change->mutable_override_scaling_mode());
+ overrideChange->set_override_scaling_mode(overrideScalingMode);
+}
+
+void SurfaceInterceptor::addChangedPropertiesLocked(Transaction* transaction,
+ const layer_state_t& state)
+{
+ const sp<const Layer> layer(getLayer(state.surface));
+ if (layer == nullptr) {
+ ALOGE("An existing layer could not be retrieved with the surface "
+ "from the layer_state_t surface in the update transaction");
+ return;
+ }
+
+ const int32_t layerId(getLayerId(layer));
+
+ if (state.what & layer_state_t::ePositionChanged) {
+ addPositionLocked(transaction, layerId, state.x, state.y);
+ }
+ if (state.what & layer_state_t::eLayerChanged) {
+ addDepthLocked(transaction, layerId, state.z);
+ }
+ if (state.what & layer_state_t::eSizeChanged) {
+ addSizeLocked(transaction, layerId, state.w, state.h);
+ }
+ if (state.what & layer_state_t::eAlphaChanged) {
+ addAlphaLocked(transaction, layerId, state.alpha);
+ }
+ if (state.what & layer_state_t::eMatrixChanged) {
+ addMatrixLocked(transaction, layerId, state.matrix);
+ }
+ if (state.what & layer_state_t::eTransparentRegionChanged) {
+ addTransparentRegionLocked(transaction, layerId, state.transparentRegion);
+ }
+ if (state.what & layer_state_t::eFlagsChanged) {
+ addFlagsLocked(transaction, layerId, state.flags);
+ }
+ if (state.what & layer_state_t::eLayerStackChanged) {
+ addLayerStackLocked(transaction, layerId, state.layerStack);
+ }
+ if (state.what & layer_state_t::eCropChanged) {
+ addCropLocked(transaction, layerId, state.crop);
+ }
+ if (state.what & layer_state_t::eDeferTransaction) {
+ addDeferTransactionLocked(transaction, layerId, state.handle, state.frameNumber);
+ }
+ if (state.what & layer_state_t::eFinalCropChanged) {
+ addFinalCropLocked(transaction, layerId, state.finalCrop);
+ }
+ if (state.what & layer_state_t::eOverrideScalingModeChanged) {
+ addOverrideScalingModeLocked(transaction, layerId, state.overrideScalingMode);
+ }
+}
+
+void SurfaceInterceptor::addUpdatedLayersLocked(Increment* increment, uint32_t flags,
+ const Vector<ComposerState>& stateUpdates)
+{
+ Transaction* transaction(increment->mutable_transaction());
+ transaction->set_synchronous(flags & BnSurfaceComposer::eSynchronous);
+ transaction->set_animation(flags & BnSurfaceComposer::eAnimation);
+ for (const auto& compState: stateUpdates) {
+ addChangedPropertiesLocked(transaction, compState.state);
+ }
+}
+
+void SurfaceInterceptor::addCreatedLayerLocked(Increment* increment, const sp<const Layer>& layer) {
+ Create* create(increment->mutable_create());
+ create->set_id(getLayerId(layer));
+ create->set_name(getLayerName(layer));
+ create->set_w(layer->mCurrentState.active.w);
+ create->set_h(layer->mCurrentState.active.h);
+}
+
+void SurfaceInterceptor::addDeletedLayerLocked(Increment* increment, const sp<const Layer>& layer) {
+ Delete* deleteLayer(increment->mutable_delete_());
+ deleteLayer->set_id(getLayerId(layer));
+}
+
+void SurfaceInterceptor::addUpdatedBufferLocked(Increment* increment, const sp<const Layer>& layer,
+ uint32_t width, uint32_t height, uint64_t frameNumber)
+{
+ BufferUpdate* update(increment->mutable_buffer_update());
+ update->set_id(getLayerId(layer));
+ update->set_w(width);
+ update->set_h(height);
+ update->set_frame_number(frameNumber);
+}
+
+void SurfaceInterceptor::addUpdatedVsyncLocked(Increment* increment, nsecs_t timestamp) {
+ VSyncEvent* event(increment->mutable_vsync_event());
+ event->set_when(timestamp);
+}
+
+void SurfaceInterceptor::saveLayerUpdates(const Vector<ComposerState>& stateUpdates,
+ uint32_t flags)
+{
+ ATRACE_CALL();
+ if (!mEnabled || stateUpdates.size() <= 0) {
+ return;
+ }
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ addUpdatedLayersLocked(addTraceIncrementLocked(), flags, stateUpdates);
+}
+
+void SurfaceInterceptor::saveLayerCreate(const sp<const Layer>& layer) {
+ ATRACE_CALL();
+ if (!mEnabled || layer == nullptr) {
+ return;
+ }
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ addCreatedLayerLocked(addTraceIncrementLocked(), layer);
+}
+
+void SurfaceInterceptor::saveLayerCreateLocked(const sp<const Layer>& layer) {
+ if (!mEnabled || layer == nullptr) {
+ return;
+ }
+ addCreatedLayerLocked(addTraceIncrementLocked(), layer);
+}
+
+void SurfaceInterceptor::saveLayerDelete(const sp<const Layer>& layer) {
+ ATRACE_CALL();
+ if (!mEnabled || layer == nullptr) {
+ return;
+ }
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ addDeletedLayerLocked(addTraceIncrementLocked(), layer);
+}
+
+void SurfaceInterceptor::saveBufferUpdate(const sp<const Layer>& layer, uint32_t width,
+ uint32_t height, uint64_t frameNumber)
+{
+ ATRACE_CALL();
+ if (!mEnabled || layer == nullptr) {
+ return;
+ }
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ addUpdatedBufferLocked(addTraceIncrementLocked(), layer, width, height, frameNumber);
+}
+
+void SurfaceInterceptor::saveVSyncEvent(nsecs_t timestamp) {
+ if (!mEnabled) {
+ return;
+ }
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ addUpdatedVsyncLocked(addTraceIncrementLocked(), timestamp);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
new file mode 100644
index 0000000..5582a5c
--- /dev/null
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2016 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.
+ */
+
+#ifndef ANDROID_SURFACEINTERCEPTOR_H
+#define ANDROID_SURFACEINTERCEPTOR_H
+
+#include <frameworks/native/cmds/surfacecapturereplay/proto/src/trace.pb.h>
+
+#include <mutex>
+
+namespace android {
+
+class BufferItem;
+class Layer;
+struct layer_state_t;
+
+constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";
+
+/*
+ * SurfaceInterceptor intercepts and stores incoming streams of window
+ * properties on SurfaceFlinger.
+ */
+class SurfaceInterceptor {
+public:
+ // The layer vector is used to capture the inital snapshot in the trace
+ void enable(const SortedVector<sp<Layer>>& layers);
+ void disable();
+ void setOutputFileName(const std::string& OutputFileName);
+
+ void saveLayerUpdates(const Vector<ComposerState>& state, uint32_t flags);
+ void saveLayerCreate(const sp<const Layer>& layer);
+ void saveLayerDelete(const sp<const Layer>& layer);
+ void saveBufferUpdate(const sp<const Layer>& layer, uint32_t width, uint32_t height,
+ uint64_t frameNumber);
+ void saveVSyncEvent(nsecs_t timestamp);
+
+private:
+ void saveExistingLayers(const SortedVector<sp<Layer>>& layers);
+ void saveInitialLayerStateLocked(const sp<const Layer>& layer);
+ void saveLayerCreateLocked(const sp<const Layer>& layer);
+ status_t writeProtoFileLocked();
+ const sp<const Layer> getLayer(const sp<const IBinder>& handle);
+ const std::string getLayerName(const sp<const Layer>& layer);
+ int32_t getLayerId(const sp<const Layer>& layer);
+ Increment* addTraceIncrementLocked();
+
+ void addUpdatedLayersLocked(Increment* increment, uint32_t flags,
+ const Vector<ComposerState>& stateUpdates);
+ void addCreatedLayerLocked(Increment* increment, const sp<const Layer>& layer);
+ void addDeletedLayerLocked(Increment* increment, const sp<const Layer>& layer);
+ void addUpdatedBufferLocked(Increment* increment, const sp<const Layer>& layer, uint32_t width,
+ uint32_t height, uint64_t frameNumber);
+ void addUpdatedVsyncLocked(Increment* increment, nsecs_t timestamp);
+
+ Change* addChangeLocked(Transaction* transaction, int32_t layerId);
+ void setProtoRectLocked(Rectangle* protoRect, const Rect& rect);
+ void addPositionLocked(Transaction* transaction, int32_t layerId, float x, float y);
+ void addDepthLocked(Transaction* transaction, int32_t layerId, uint32_t z);
+ void addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w, uint32_t h);
+ void addAlphaLocked(Transaction* transaction, int32_t layerId, float alpha);
+ void addMatrixLocked(Transaction* transaction, int32_t layerId,
+ const layer_state_t::matrix22_t& matrix);
+ void addTransparentRegionLocked(Transaction* transaction, int32_t layerId,
+ const Region& transRegion);
+ void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags);
+ void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack);
+ void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
+ void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
+ const sp<const IBinder>& handle, uint64_t frameNumber);
+ void addFinalCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
+ void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId,
+ int32_t overrideScalingMode);
+ void addChangedPropertiesLocked(Transaction* transaction, const layer_state_t& state);
+
+ bool mEnabled {false};
+ std::string mOutputFileName {DEFAULT_FILENAME};
+ std::mutex mTraceMutex {};
+ Trace mTrace {};
+};
+
+}
+
+#endif // ANDROID_SURFACEINTERCEPTOR_H
diff --git a/services/surfaceflinger/tests/Android.mk b/services/surfaceflinger/tests/Android.mk
index 979062e..e5dffe5 100644
--- a/services/surfaceflinger/tests/Android.mk
+++ b/services/surfaceflinger/tests/Android.mk
@@ -3,21 +3,29 @@
include $(CLEAR_VARS)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_CFLAGS := -std=c++14
+
LOCAL_MODULE := SurfaceFlinger_test
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := \
Transaction_test.cpp \
+ SurfaceInterceptor_test.cpp
LOCAL_SHARED_LIBRARIES := \
- libEGL \
- libGLESv2 \
- libbinder \
- libcutils \
- libgui \
- libui \
- libutils \
+ libEGL \
+ libGLESv2 \
+ libbinder \
+ libcutils \
+ libgui \
+ libprotobuf-cpp-full \
+ libui \
+ libutils \
+
+LOCAL_STATIC_LIBRARIES := libtrace_proto
+
+LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
# Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
# to integrate with auto-test framework.
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
new file mode 100644
index 0000000..ebc4dd1
--- /dev/null
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -0,0 +1,734 @@
+/*
+ * Copyright (C) 2016 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 <frameworks/native/cmds/surfacecapturereplay/proto/src/trace.pb.h>
+
+#include <gtest/gtest.h>
+
+#include <android/native_window.h>
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/LayerState.h>
+#include <ui/DisplayInfo.h>
+
+#include <fstream>
+#include <random>
+#include <thread>
+
+namespace android {
+
+constexpr int32_t SCALING_UPDATE = 1;
+constexpr uint32_t BUFFER_UPDATES = 18;
+constexpr uint32_t LAYER_UPDATE = INT_MAX - 2;
+constexpr uint32_t SIZE_UPDATE = 134;
+constexpr uint32_t STACK_UPDATE = 1;
+constexpr uint64_t DEFERRED_UPDATE = 13;
+constexpr float ALPHA_UPDATE = 0.29f;
+constexpr float POSITION_UPDATE = 121;
+const Rect CROP_UPDATE(16, 16, 32, 32);
+
+constexpr auto LAYER_NAME = "Layer Create and Delete Test";
+
+constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";
+
+// Fill an RGBA_8888 formatted surface with a single color.
+static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) {
+ ANativeWindow_Buffer outBuffer;
+ sp<Surface> s = sc->getSurface();
+ ASSERT_TRUE(s != nullptr);
+ ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
+ uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
+ for (int y = 0; y < outBuffer.height; y++) {
+ for (int x = 0; x < outBuffer.width; x++) {
+ uint8_t* pixel = img + (4 * (y*outBuffer.stride + x));
+ pixel[0] = r;
+ pixel[1] = g;
+ pixel[2] = b;
+ pixel[3] = 255;
+ }
+ }
+ ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+}
+
+static status_t readProtoFile(Trace* trace) {
+ std::ifstream input(DEFAULT_FILENAME, std::ios::in | std::ios::binary);
+ if (input && !trace->ParseFromIstream(&input)) {
+ return PERMISSION_DENIED;
+ }
+ return NO_ERROR;
+}
+
+static void enableInterceptor() {
+ system("service call SurfaceFlinger 1020 i32 1 > /dev/null");
+}
+
+static void disableInterceptor() {
+ system("service call SurfaceFlinger 1020 i32 0 > /dev/null");
+}
+
+uint32_t getLayerId(const std::string& layerName) {
+ enableInterceptor();
+ disableInterceptor();
+ Trace capturedTrace;
+ readProtoFile(&capturedTrace);
+ uint32_t layerId = 0;
+ for (const auto& increment : *capturedTrace.mutable_increment()) {
+ if (increment.increment_case() == increment.kCreate) {
+ if (increment.create().name() == layerName) {
+ layerId = increment.create().id();
+ break;
+ }
+ }
+ }
+ return layerId;
+}
+
+class SurfaceInterceptorTest : public ::testing::Test {
+protected:
+ virtual void SetUp() {
+ // Allow SurfaceInterceptor write to /data
+ system("setenforce 0");
+
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+ sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
+ ISurfaceComposer::eDisplayIdMain));
+ DisplayInfo info;
+ SurfaceComposerClient::getDisplayInfo(display, &info);
+ ssize_t displayWidth = info.w;
+ ssize_t displayHeight = info.h;
+
+ // Background surface
+ mBGSurfaceControl = mComposerClient->createSurface(
+ String8("BG Interceptor Test Surface"), displayWidth, displayHeight,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(mBGSurfaceControl != NULL);
+ ASSERT_TRUE(mBGSurfaceControl->isValid());
+ mBGLayerId = getLayerId("BG Interceptor Test Surface");
+
+ SurfaceComposerClient::openGlobalTransaction();
+ mComposerClient->setDisplayLayerStack(display, 0);
+ ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT_MAX-3));
+ ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show());
+ SurfaceComposerClient::closeGlobalTransaction(true);
+ }
+
+ virtual void TearDown() {
+ mComposerClient->dispose();
+ mBGSurfaceControl.clear();
+ mComposerClient.clear();
+ }
+
+ sp<SurfaceComposerClient> mComposerClient;
+ sp<SurfaceControl> mBGSurfaceControl;
+ uint32_t mBGLayerId;
+
+public:
+ void captureTest(void (SurfaceInterceptorTest::* action)(void),
+ bool (SurfaceInterceptorTest::* verification)(Trace *));
+ void captureTest(void (SurfaceInterceptorTest::* action)(void), Change::ChangeCase changeCase);
+ void runInTransaction(void (SurfaceInterceptorTest::* action)(void), bool intercepted = false);
+
+ // Verification of changes to a surface
+ bool positionUpdateFound(const Change& change, bool foundPosition);
+ bool sizeUpdateFound(const Change& change, bool foundSize);
+ bool alphaUpdateFound(const Change& change, bool foundAlpha);
+ bool layerUpdateFound(const Change& change, bool foundLayer);
+ bool cropUpdateFound(const Change& change, bool foundCrop);
+ bool finalCropUpdateFound(const Change& change, bool foundFinalCrop);
+ bool matrixUpdateFound(const Change& change, bool foundMatrix);
+ bool scalingModeUpdateFound(const Change& change, bool foundScalingMode);
+ bool transparentRegionHintUpdateFound(const Change& change, bool foundTransparentRegion);
+ bool layerStackUpdateFound(const Change& change, bool foundLayerStack);
+ bool hiddenFlagUpdateFound(const Change& change, bool foundHiddenFlag);
+ bool opaqueFlagUpdateFound(const Change& change, bool foundOpaqueFlag);
+ bool secureFlagUpdateFound(const Change& change, bool foundSecureFlag);
+ bool deferredTransactionUpdateFound(const Change& change, bool foundDeferred);
+ bool surfaceUpdateFound(Trace* trace, Change::ChangeCase changeCase);
+ void assertAllUpdatesFound(Trace* trace);
+
+ // Verification of creation and deletion of a surface
+ bool surfaceCreateFound(Trace* trace);
+ bool surfaceDeleteFound(Trace* trace, uint32_t targetLayerId);
+
+ // Verification of buffer updates
+ bool bufferUpdatesFound(Trace* trace);
+
+ // Perform each of the possible changes to a surface
+ void positionUpdate();
+ void sizeUpdate();
+ void alphaUpdate();
+ void layerUpdate();
+ void cropUpdate();
+ void finalCropUpdate();
+ void matrixUpdate();
+ void overrideScalingModeUpdate();
+ void transparentRegionHintUpdate();
+ void layerStackUpdate();
+ void hiddenFlagUpdate();
+ void opaqueFlagUpdate();
+ void secureFlagUpdate();
+ void deferredTransactionUpdate();
+ void runAllUpdates();
+ void surfaceCreate();
+ void nBufferUpdates();
+};
+
+void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(void),
+ bool (SurfaceInterceptorTest::* verification)(Trace *))
+{
+ runInTransaction(action, true);
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ ASSERT_TRUE((this->*verification)(&capturedTrace));
+}
+
+void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(void),
+ Change::ChangeCase changeCase)
+{
+ runInTransaction(action, true);
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ ASSERT_TRUE(surfaceUpdateFound(&capturedTrace, changeCase));
+}
+
+void SurfaceInterceptorTest::runInTransaction(void (SurfaceInterceptorTest::* action)(void),
+ bool intercepted)
+{
+ if (intercepted) {
+ enableInterceptor();
+ }
+ SurfaceComposerClient::openGlobalTransaction();
+ (this->*action)();
+ SurfaceComposerClient::closeGlobalTransaction(true);
+ if (intercepted) {
+ disableInterceptor();
+ }
+}
+
+void SurfaceInterceptorTest::positionUpdate() {
+ mBGSurfaceControl->setPosition(POSITION_UPDATE, POSITION_UPDATE);
+}
+
+void SurfaceInterceptorTest::sizeUpdate() {
+ mBGSurfaceControl->setSize(SIZE_UPDATE, SIZE_UPDATE);
+}
+
+void SurfaceInterceptorTest::alphaUpdate() {
+ mBGSurfaceControl->setAlpha(ALPHA_UPDATE);
+}
+
+void SurfaceInterceptorTest::layerUpdate() {
+ mBGSurfaceControl->setLayer(LAYER_UPDATE);
+}
+
+void SurfaceInterceptorTest::cropUpdate() {
+ mBGSurfaceControl->setCrop(CROP_UPDATE);
+}
+
+void SurfaceInterceptorTest::finalCropUpdate() {
+ mBGSurfaceControl->setFinalCrop(CROP_UPDATE);
+}
+
+void SurfaceInterceptorTest::matrixUpdate() {
+ mBGSurfaceControl->setMatrix(M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2);
+}
+
+void SurfaceInterceptorTest::overrideScalingModeUpdate() {
+ mBGSurfaceControl->setOverrideScalingMode(SCALING_UPDATE);
+}
+
+void SurfaceInterceptorTest::transparentRegionHintUpdate() {
+ Region region(CROP_UPDATE);
+ mBGSurfaceControl->setTransparentRegionHint(region);
+}
+
+void SurfaceInterceptorTest::layerStackUpdate() {
+ mBGSurfaceControl->setLayerStack(STACK_UPDATE);
+}
+
+void SurfaceInterceptorTest::hiddenFlagUpdate() {
+ mBGSurfaceControl->setFlags(layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
+}
+
+void SurfaceInterceptorTest::opaqueFlagUpdate() {
+ mBGSurfaceControl->setFlags(layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+}
+
+void SurfaceInterceptorTest::secureFlagUpdate() {
+ mBGSurfaceControl->setFlags(layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
+}
+
+void SurfaceInterceptorTest::deferredTransactionUpdate() {
+ mBGSurfaceControl->deferTransactionUntil(mBGSurfaceControl->getHandle(), DEFERRED_UPDATE);
+}
+
+void SurfaceInterceptorTest::runAllUpdates() {
+ runInTransaction(&SurfaceInterceptorTest::positionUpdate);
+ runInTransaction(&SurfaceInterceptorTest::sizeUpdate);
+ runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
+ runInTransaction(&SurfaceInterceptorTest::layerUpdate);
+ runInTransaction(&SurfaceInterceptorTest::cropUpdate);
+ runInTransaction(&SurfaceInterceptorTest::finalCropUpdate);
+ runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
+ runInTransaction(&SurfaceInterceptorTest::overrideScalingModeUpdate);
+ runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate);
+ runInTransaction(&SurfaceInterceptorTest::layerStackUpdate);
+ runInTransaction(&SurfaceInterceptorTest::hiddenFlagUpdate);
+ runInTransaction(&SurfaceInterceptorTest::opaqueFlagUpdate);
+ runInTransaction(&SurfaceInterceptorTest::secureFlagUpdate);
+ runInTransaction(&SurfaceInterceptorTest::deferredTransactionUpdate);
+}
+
+void SurfaceInterceptorTest::surfaceCreate() {
+ mComposerClient->createSurface(String8(LAYER_NAME), SIZE_UPDATE, SIZE_UPDATE,
+ PIXEL_FORMAT_RGBA_8888, 0);
+}
+
+void SurfaceInterceptorTest::nBufferUpdates() {
+ std::random_device rd;
+ std::mt19937_64 gen(rd());
+ // This makes testing fun
+ std::uniform_int_distribution<uint8_t> dis;
+ for (uint32_t i = 0; i < BUFFER_UPDATES; ++i) {
+ fillSurfaceRGBA8(mBGSurfaceControl, dis(gen), dis(gen), dis(gen));
+ }
+}
+
+bool SurfaceInterceptorTest::positionUpdateFound(const Change& change, bool foundPosition) {
+ // There should only be one position transaction with x and y = POSITION_UPDATE
+ bool hasX(change.position().x() == POSITION_UPDATE);
+ bool hasY(change.position().y() == POSITION_UPDATE);
+ if (hasX && hasY && !foundPosition) {
+ foundPosition = true;
+ }
+ // Failed because the position update was found a second time
+ else if (hasX && hasY && foundPosition) {
+ [] () { FAIL(); }();
+ }
+ return foundPosition;
+}
+
+bool SurfaceInterceptorTest::sizeUpdateFound(const Change& change, bool foundSize) {
+ bool hasWidth(change.size().h() == SIZE_UPDATE);
+ bool hasHeight(change.size().w() == SIZE_UPDATE);
+ if (hasWidth && hasHeight && !foundSize) {
+ foundSize = true;
+ }
+ else if (hasWidth && hasHeight && foundSize) {
+ [] () { FAIL(); }();
+ }
+ return foundSize;
+}
+
+bool SurfaceInterceptorTest::alphaUpdateFound(const Change& change, bool foundAlpha) {
+ bool hasAlpha(change.alpha().alpha() == ALPHA_UPDATE);
+ if (hasAlpha && !foundAlpha) {
+ foundAlpha = true;
+ }
+ else if (hasAlpha && foundAlpha) {
+ [] () { FAIL(); }();
+ }
+ return foundAlpha;
+}
+
+bool SurfaceInterceptorTest::layerUpdateFound(const Change& change, bool foundLayer) {
+ bool hasLayer(change.layer().layer() == LAYER_UPDATE);
+ if (hasLayer && !foundLayer) {
+ foundLayer = true;
+ }
+ else if (hasLayer && foundLayer) {
+ [] () { FAIL(); }();
+ }
+ return foundLayer;
+}
+
+bool SurfaceInterceptorTest::cropUpdateFound(const Change& change, bool foundCrop) {
+ bool hasLeft(change.crop().rectangle().left() == CROP_UPDATE.left);
+ bool hasTop(change.crop().rectangle().top() == CROP_UPDATE.top);
+ bool hasRight(change.crop().rectangle().right() == CROP_UPDATE.right);
+ bool hasBottom(change.crop().rectangle().bottom() == CROP_UPDATE.bottom);
+ if (hasLeft && hasRight && hasTop && hasBottom && !foundCrop) {
+ foundCrop = true;
+ }
+ else if (hasLeft && hasRight && hasTop && hasBottom && foundCrop) {
+ [] () { FAIL(); }();
+ }
+ return foundCrop;
+}
+
+bool SurfaceInterceptorTest::finalCropUpdateFound(const Change& change, bool foundFinalCrop) {
+ bool hasLeft(change.final_crop().rectangle().left() == CROP_UPDATE.left);
+ bool hasTop(change.final_crop().rectangle().top() == CROP_UPDATE.top);
+ bool hasRight(change.final_crop().rectangle().right() == CROP_UPDATE.right);
+ bool hasBottom(change.final_crop().rectangle().bottom() == CROP_UPDATE.bottom);
+ if (hasLeft && hasRight && hasTop && hasBottom && !foundFinalCrop) {
+ foundFinalCrop = true;
+ }
+ else if (hasLeft && hasRight && hasTop && hasBottom && foundFinalCrop) {
+ [] () { FAIL(); }();
+ }
+ return foundFinalCrop;
+}
+
+bool SurfaceInterceptorTest::matrixUpdateFound(const Change& change, bool foundMatrix) {
+ bool hasSx((float)change.matrix().dsdx() == (float)M_SQRT1_2);
+ bool hasTx((float)change.matrix().dtdx() == (float)M_SQRT1_2);
+ bool hasSy((float)change.matrix().dsdy() == (float)-M_SQRT1_2);
+ bool hasTy((float)change.matrix().dtdy() == (float)M_SQRT1_2);
+ if (hasSx && hasTx && hasSy && hasTy && !foundMatrix) {
+ foundMatrix = true;
+ }
+ else if (hasSx && hasTx && hasSy && hasTy && foundMatrix) {
+ [] () { FAIL(); }();
+ }
+ return foundMatrix;
+}
+
+bool SurfaceInterceptorTest::scalingModeUpdateFound(const Change& change, bool foundScalingMode) {
+ bool hasScalingUpdate(change.override_scaling_mode().override_scaling_mode() == SCALING_UPDATE);
+ if (hasScalingUpdate && !foundScalingMode) {
+ foundScalingMode = true;
+ }
+ else if (hasScalingUpdate && foundScalingMode) {
+ [] () { FAIL(); }();
+ }
+ return foundScalingMode;
+}
+
+bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const Change& change,
+ bool foundTransparentRegion)
+{
+ auto traceRegion = change.transparent_region_hint().region(0);
+ bool hasLeft(traceRegion.left() == CROP_UPDATE.left);
+ bool hasTop(traceRegion.top() == CROP_UPDATE.top);
+ bool hasRight(traceRegion.right() == CROP_UPDATE.right);
+ bool hasBottom(traceRegion.bottom() == CROP_UPDATE.bottom);
+ if (hasLeft && hasRight && hasTop && hasBottom && !foundTransparentRegion) {
+ foundTransparentRegion = true;
+ }
+ else if (hasLeft && hasRight && hasTop && hasBottom && foundTransparentRegion) {
+ [] () { FAIL(); }();
+ }
+ return foundTransparentRegion;
+}
+
+bool SurfaceInterceptorTest::layerStackUpdateFound(const Change& change, bool foundLayerStack) {
+ bool hasLayerStackUpdate(change.layer_stack().layer_stack() == STACK_UPDATE);
+ if (hasLayerStackUpdate && !foundLayerStack) {
+ foundLayerStack = true;
+ }
+ else if (hasLayerStackUpdate && foundLayerStack) {
+ [] () { FAIL(); }();
+ }
+ return foundLayerStack;
+}
+
+bool SurfaceInterceptorTest::hiddenFlagUpdateFound(const Change& change, bool foundHiddenFlag) {
+ bool hasHiddenFlag(change.hidden_flag().hidden_flag());
+ if (hasHiddenFlag && !foundHiddenFlag) {
+ foundHiddenFlag = true;
+ }
+ else if (hasHiddenFlag && foundHiddenFlag) {
+ [] () { FAIL(); }();
+ }
+ return foundHiddenFlag;
+}
+
+bool SurfaceInterceptorTest::opaqueFlagUpdateFound(const Change& change, bool foundOpaqueFlag) {
+ bool hasOpaqueFlag(change.opaque_flag().opaque_flag());
+ if (hasOpaqueFlag && !foundOpaqueFlag) {
+ foundOpaqueFlag = true;
+ }
+ else if (hasOpaqueFlag && foundOpaqueFlag) {
+ [] () { FAIL(); }();
+ }
+ return foundOpaqueFlag;
+}
+
+bool SurfaceInterceptorTest::secureFlagUpdateFound(const Change& change, bool foundSecureFlag) {
+ bool hasSecureFlag(change.secure_flag().secure_flag());
+ if (hasSecureFlag && !foundSecureFlag) {
+ foundSecureFlag = true;
+ }
+ else if (hasSecureFlag && foundSecureFlag) {
+ [] () { FAIL(); }();
+ }
+ return foundSecureFlag;
+}
+
+bool SurfaceInterceptorTest::deferredTransactionUpdateFound(const Change& change,
+ bool foundDeferred)
+{
+ bool hasId(change.deferred_transaction().layer_id() == mBGLayerId);
+ bool hasFrameNumber(change.deferred_transaction().frame_number() ==
+ DEFERRED_UPDATE);
+ if (hasId && hasFrameNumber && !foundDeferred) {
+ foundDeferred = true;
+ }
+ else if (hasId && hasFrameNumber && foundDeferred) {
+ [] () { FAIL(); }();
+ }
+ return foundDeferred;
+}
+
+bool SurfaceInterceptorTest::surfaceUpdateFound(Trace* trace, Change::ChangeCase changeCase) {
+ bool updateFound = false;
+ for (const auto& increment : *trace->mutable_increment()) {
+ if (increment.increment_case() == increment.kTransaction) {
+ for (const auto& change : increment.transaction().change()) {
+ if (change.id() == mBGLayerId && change.Change_case() == changeCase) {
+ switch (changeCase) {
+ case Change::ChangeCase::kPosition:
+ // updateFound is sent for the tests to fail on duplicated increments
+ updateFound = positionUpdateFound(change, updateFound);
+ break;
+ case Change::ChangeCase::kSize:
+ updateFound = sizeUpdateFound(change, updateFound);
+ break;
+ case Change::ChangeCase::kAlpha:
+ updateFound = alphaUpdateFound(change, updateFound);
+ break;
+ case Change::ChangeCase::kLayer:
+ updateFound = layerUpdateFound(change, updateFound);
+ break;
+ case Change::ChangeCase::kCrop:
+ updateFound = cropUpdateFound(change, updateFound);
+ break;
+ case Change::ChangeCase::kFinalCrop:
+ updateFound = finalCropUpdateFound(change, updateFound);
+ break;
+ case Change::ChangeCase::kMatrix:
+ updateFound = matrixUpdateFound(change, updateFound);
+ break;
+ case Change::ChangeCase::kOverrideScalingMode:
+ updateFound = scalingModeUpdateFound(change, updateFound);
+ break;
+ case Change::ChangeCase::kTransparentRegionHint:
+ updateFound = transparentRegionHintUpdateFound(change, updateFound);
+ break;
+ case Change::ChangeCase::kLayerStack:
+ updateFound = layerStackUpdateFound(change, updateFound);
+ break;
+ case Change::ChangeCase::kHiddenFlag:
+ updateFound = hiddenFlagUpdateFound(change, updateFound);
+ break;
+ case Change::ChangeCase::kOpaqueFlag:
+ updateFound = opaqueFlagUpdateFound(change, updateFound);
+ break;
+ case Change::ChangeCase::kSecureFlag:
+ updateFound = secureFlagUpdateFound(change, updateFound);
+ break;
+ case Change::ChangeCase::kDeferredTransaction:
+ updateFound = deferredTransactionUpdateFound(change, updateFound);
+ break;
+ case Change::ChangeCase::CHANGE_NOT_SET:
+ break;
+ }
+ }
+ }
+ }
+ }
+ return updateFound;
+}
+
+void SurfaceInterceptorTest::assertAllUpdatesFound(Trace* trace) {
+ ASSERT_TRUE(surfaceUpdateFound(trace, Change::ChangeCase::kPosition));
+ ASSERT_TRUE(surfaceUpdateFound(trace, Change::ChangeCase::kSize));
+ ASSERT_TRUE(surfaceUpdateFound(trace, Change::ChangeCase::kAlpha));
+ ASSERT_TRUE(surfaceUpdateFound(trace, Change::ChangeCase::kLayer));
+ ASSERT_TRUE(surfaceUpdateFound(trace, Change::ChangeCase::kCrop));
+ ASSERT_TRUE(surfaceUpdateFound(trace, Change::ChangeCase::kFinalCrop));
+ ASSERT_TRUE(surfaceUpdateFound(trace, Change::ChangeCase::kMatrix));
+ ASSERT_TRUE(surfaceUpdateFound(trace, Change::ChangeCase::kOverrideScalingMode));
+ ASSERT_TRUE(surfaceUpdateFound(trace, Change::ChangeCase::kTransparentRegionHint));
+ ASSERT_TRUE(surfaceUpdateFound(trace, Change::ChangeCase::kLayerStack));
+ ASSERT_TRUE(surfaceUpdateFound(trace, Change::ChangeCase::kHiddenFlag));
+ ASSERT_TRUE(surfaceUpdateFound(trace, Change::ChangeCase::kOpaqueFlag));
+ ASSERT_TRUE(surfaceUpdateFound(trace, Change::ChangeCase::kSecureFlag));
+ ASSERT_TRUE(surfaceUpdateFound(trace, Change::ChangeCase::kDeferredTransaction));
+}
+
+bool SurfaceInterceptorTest::surfaceCreateFound(Trace* trace) {
+ bool foundLayer = false;
+ for (const auto& inc : *trace->mutable_increment()) {
+ if (inc.increment_case() == inc.kCreate) {
+ bool isMatch(inc.create().name() == LAYER_NAME && inc.create().w() == SIZE_UPDATE &&
+ inc.create().h() == SIZE_UPDATE);
+ if (isMatch && !foundLayer) {
+ foundLayer = true;
+ }
+ else if (isMatch && foundLayer) {
+ return false;
+ }
+ }
+ }
+ return foundLayer;
+}
+
+bool SurfaceInterceptorTest::surfaceDeleteFound(Trace* trace, uint32_t targetLayerId) {
+ bool foundLayer = false;
+ for (const auto& increment : *trace->mutable_increment()) {
+ if (increment.increment_case() == increment.kDelete) {
+ bool isMatch(increment.delete_().id() == targetLayerId);
+ if (isMatch && !foundLayer) {
+ foundLayer = true;
+ }
+ else if (isMatch && foundLayer) {
+ return false;
+ }
+ }
+ }
+ return foundLayer;
+}
+
+bool SurfaceInterceptorTest::bufferUpdatesFound(Trace* trace) {
+ uint32_t updates = 0;
+ for (const auto& inc : *trace->mutable_increment()) {
+ if (inc.increment_case() == inc.kBufferUpdate && inc.buffer_update().id() == mBGLayerId) {
+ updates++;
+ }
+ }
+ return updates == BUFFER_UPDATES;
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptPositionUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::positionUpdate, Change::ChangeCase::kPosition);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSizeUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::sizeUpdate, Change::ChangeCase::kSize);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptAlphaUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::alphaUpdate, Change::ChangeCase::kAlpha);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptLayerUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::layerUpdate, Change::ChangeCase::kLayer);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptCropUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::cropUpdate, Change::ChangeCase::kCrop);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptFinalCropUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::finalCropUpdate, Change::ChangeCase::kFinalCrop);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::matrixUpdate, Change::ChangeCase::kMatrix);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptOverrideScalingModeUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::overrideScalingModeUpdate,
+ Change::ChangeCase::kOverrideScalingMode);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptTransparentRegionHintUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::transparentRegionHintUpdate,
+ Change::ChangeCase::kTransparentRegionHint);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptLayerStackUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::layerStackUpdate, Change::ChangeCase::kLayerStack);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptHiddenFlagUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::hiddenFlagUpdate, Change::ChangeCase::kHiddenFlag);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptOpaqueFlagUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::opaqueFlagUpdate, Change::ChangeCase::kOpaqueFlag);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSecureFlagUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::secureFlagUpdate, Change::ChangeCase::kSecureFlag);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptDeferredTransactionUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::deferredTransactionUpdate,
+ Change::ChangeCase::kDeferredTransaction);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
+ enableInterceptor();
+ runAllUpdates();
+ disableInterceptor();
+
+ // Find all of the updates in the single trace
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ assertAllUpdatesFound(&capturedTrace);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptLayerCreateWorks) {
+ captureTest(&SurfaceInterceptorTest::surfaceCreate, &SurfaceInterceptorTest::surfaceCreateFound);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptLayerDeleteWorks) {
+ sp<SurfaceControl> layerToDelete = mComposerClient->createSurface(String8(LAYER_NAME),
+ SIZE_UPDATE, SIZE_UPDATE, PIXEL_FORMAT_RGBA_8888, 0);
+ uint32_t targetLayerId = getLayerId(LAYER_NAME);
+ enableInterceptor();
+ mComposerClient->destroySurface(layerToDelete->getHandle());
+ disableInterceptor();
+
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ ASSERT_TRUE(surfaceDeleteFound(&capturedTrace, targetLayerId));
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptBufferUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::nBufferUpdates,
+ &SurfaceInterceptorTest::bufferUpdatesFound);
+}
+
+// If the interceptor is enabled while buffer updates are being pushed, the interceptor should
+// first create a snapshot of the existing surfaces and then start capturing the buffer updates
+TEST_F(SurfaceInterceptorTest, InterceptWhileBufferUpdatesWorks) {
+ std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
+ enableInterceptor();
+ disableInterceptor();
+ bufferUpdates.join();
+
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+ const auto& firstIncrement = capturedTrace.mutable_increment(0);
+ ASSERT_EQ(firstIncrement->increment_case(), Increment::IncrementCase::kCreate);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptSimultaneousUpdatesWorks) {
+ enableInterceptor();
+ std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
+ std::thread surfaceUpdates(&SurfaceInterceptorTest::runAllUpdates, this);
+ runInTransaction(&SurfaceInterceptorTest::surfaceCreate);
+ bufferUpdates.join();
+ surfaceUpdates.join();
+ disableInterceptor();
+
+ Trace capturedTrace;
+ ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
+
+ assertAllUpdatesFound(&capturedTrace);
+ ASSERT_TRUE(bufferUpdatesFound(&capturedTrace));
+ ASSERT_TRUE(surfaceCreateFound(&capturedTrace));
+}
+
+}