diff options
20 files changed, 286 insertions, 178 deletions
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index b347a78a8780..39792ce58367 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -638,4 +638,14 @@ interface IWindowManager * native InputManager before proceeding with tests. */ void syncInputTransactions(); + + /** + * Returns whether SurfaceFlinger layer tracing is enabled. + */ + boolean isLayerTracing(); + + /** + * Enables/disables SurfaceFlinger layer tracing. + */ + void setLayerTracing(boolean enabled); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f67b4fe78f58..e1f85446de7b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7807,4 +7807,64 @@ public class WindowManagerService extends IWindowManager.Stub 0 /* configChanges */, !PRESERVE_WINDOWS, true /* notifyClients */); } } + + /** Return whether layer tracing is enabled */ + public boolean isLayerTracing() { + mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP, + "isLayerTracing"); + long token = Binder.clearCallingIdentity(); + try { + Parcel data = null; + Parcel reply = null; + try { + IBinder sf = ServiceManager.getService("SurfaceFlinger"); + if (sf != null) { + reply = Parcel.obtain(); + data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + sf.transact(/* LAYER_TRACE_STATUS_CODE */ 1026, data, reply, 0 /* flags */); + return reply.readBoolean(); + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to get layer tracing"); + } finally { + if (data != null) { + data.recycle(); + } + if (reply != null) { + reply.recycle(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + return false; + } + + /** Enable or disable layer tracing */ + public void setLayerTracing(boolean enabled) { + mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.DUMP, + "setLayerTracing"); + long token = Binder.clearCallingIdentity(); + try { + Parcel data = null; + try { + IBinder sf = ServiceManager.getService("SurfaceFlinger"); + if (sf != null) { + data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + data.writeInt(enabled ? 1 : 0); + sf.transact(/* LAYER_TRACE_CONTROL_CODE */ 1025, data, null, 0 /* flags */); + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to set layer tracing"); + } finally { + if (data != null) { + data.recycle(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } } diff --git a/tests/FlickerTests/lib/Android.bp b/tests/FlickerTests/lib/Android.bp index 5d8ed2c205e9..e0f0188ee618 100644 --- a/tests/FlickerTests/lib/Android.bp +++ b/tests/FlickerTests/lib/Android.bp @@ -30,10 +30,23 @@ java_test { } java_library { + name: "flickerlib_without_helpers", + platform_apis: true, + srcs: ["src/**/*.java"], + exclude_srcs: ["src/**/helpers/*.java"], + static_libs: [ + "cts-wm-util", + "platformprotosnano", + "layersprotosnano", + "truth-prebuilt" + ], +} + +java_library { name: "flickerautomationhelperlib", sdk_version: "test_current", srcs: [ - "src/com/android/server/wm/flicker/AutomationUtils.java", + "src/com/android/server/wm/flicker/helpers/AutomationUtils.java", "src/com/android/server/wm/flicker/WindowUtils.java", ], static_libs: [ diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java index 84f9f871324c..38255ee6fe8d 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java @@ -24,14 +24,14 @@ import java.util.function.Function; * results. Assertions are functions that are applied over a single trace entry and returns a * result which includes a detailed reason if the assertion fails. */ -class Assertions { +public class Assertions { /** * Checks assertion on a single trace entry. * * @param <T> trace entry type to perform the assertion on. */ @FunctionalInterface - interface TraceAssertion<T> extends Function<T, Result> { + public interface TraceAssertion<T> extends Function<T, Result> { /** * Returns an assertion that represents the logical negation of this assertion. * @@ -46,7 +46,7 @@ class Assertions { * Checks assertion on a single layers trace entry. */ @FunctionalInterface - interface LayersTraceAssertion extends TraceAssertion<LayersTrace.Entry> { + public interface LayersTraceAssertion extends TraceAssertion<LayersTrace.Entry> { } @@ -54,11 +54,11 @@ class Assertions { * Utility class to store assertions with an identifier to help generate more useful debug * data when dealing with multiple assertions. */ - static class NamedAssertion<T> { - final TraceAssertion<T> assertion; - final String name; + public static class NamedAssertion<T> { + public final TraceAssertion<T> assertion; + public final String name; - NamedAssertion(TraceAssertion<T> assertion, String name) { + public NamedAssertion(TraceAssertion<T> assertion, String name) { this.assertion = assertion; this.name = name; } @@ -67,21 +67,21 @@ class Assertions { /** * Contains the result of an assertion including the reason for failed assertions. */ - static class Result { - static final String NEGATION_PREFIX = "!"; - final boolean success; - final long timestamp; - final String assertionName; - final String reason; - - Result(boolean success, long timestamp, String assertionName, String reason) { + public static class Result { + public static final String NEGATION_PREFIX = "!"; + public final boolean success; + public final long timestamp; + public final String assertionName; + public final String reason; + + public Result(boolean success, long timestamp, String assertionName, String reason) { this.success = success; this.timestamp = timestamp; this.assertionName = assertionName; this.reason = reason; } - Result(boolean success, String reason) { + public Result(boolean success, String reason) { this.success = success; this.reason = reason; this.assertionName = ""; @@ -91,7 +91,7 @@ class Assertions { /** * Returns the negated {@code Result} and adds a negation prefix to the assertion name. */ - Result negate() { + public Result negate() { String negatedAssertionName; if (this.assertionName.startsWith(NEGATION_PREFIX)) { negatedAssertionName = this.assertionName.substring(NEGATION_PREFIX.length() + 1); @@ -101,11 +101,11 @@ class Assertions { return new Result(!this.success, this.timestamp, negatedAssertionName, this.reason); } - boolean passed() { + public boolean passed() { return this.success; } - boolean failed() { + public boolean failed() { return !this.success; } diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java index 3c65d3c341b3..5c4df81299c1 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java @@ -38,11 +38,11 @@ public class AssertionsChecker<T extends ITraceEntry> { private AssertionOption mOption = AssertionOption.NONE; private List<NamedAssertion<T>> mAssertions = new LinkedList<>(); - void add(Assertions.TraceAssertion<T> assertion, String name) { + public void add(Assertions.TraceAssertion<T> assertion, String name) { mAssertions.add(new NamedAssertion<>(assertion, name)); } - void filterByRange(long startTime, long endTime) { + public void filterByRange(long startTime, long endTime) { mFilterEntriesByRange = true; mFilterStartTime = startTime; mFilterEndTime = endTime; @@ -75,7 +75,7 @@ public class AssertionsChecker<T extends ITraceEntry> { * @param entries list of entries to perform assertions on * @return list of failed assertion results */ - List<Result> test(List<T> entries) { + public List<Result> test(List<T> entries) { List<T> filteredEntries; List<Result> failures; diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java index 9525f41b46b2..c47f7f42e54e 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java @@ -19,7 +19,7 @@ package com.android.server.wm.flicker; /** * Common interface for Layer and WindowManager trace entries. */ -interface ITraceEntry { +public interface ITraceEntry { /** * @return timestamp of current entry */ diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java index 660ec0fe4833..68986d48783a 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java @@ -16,7 +16,6 @@ package com.android.server.wm.flicker; -import android.annotation.Nullable; import android.graphics.Rect; import android.surfaceflinger.nano.Layers.LayerProto; import android.surfaceflinger.nano.Layers.RectProto; @@ -25,11 +24,14 @@ import android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto; import android.surfaceflinger.nano.Layerstrace.LayersTraceProto; import android.util.SparseArray; +import androidx.annotation.Nullable; + import com.android.server.wm.flicker.Assertions.Result; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; +import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -57,7 +59,7 @@ public class LayersTrace { * @param data binary proto data * @param source Path to source of data for additional debug information */ - static LayersTrace parseFrom(byte[] data, Path source) { + public static LayersTrace parseFrom(byte[] data, Path source) { List<Entry> entries = new ArrayList<>(); LayersTraceFileProto fileProto; try { @@ -79,15 +81,15 @@ public class LayersTrace { * * @param data binary proto data */ - static LayersTrace parseFrom(byte[] data) { + public static LayersTrace parseFrom(byte[] data) { return parseFrom(data, null); } - List<Entry> getEntries() { + public List<Entry> getEntries() { return mEntries; } - Entry getEntry(long timestamp) { + public Entry getEntry(long timestamp) { Optional<Entry> entry = mEntries.stream() .filter(e -> e.getTimestamp() == timestamp) .findFirst(); @@ -97,14 +99,14 @@ public class LayersTrace { return entry.get(); } - Optional<Path> getSource() { + public Optional<Path> getSource() { return Optional.ofNullable(mSource); } /** * Represents a single Layer trace entry. */ - static class Entry implements ITraceEntry { + public static class Entry implements ITraceEntry { private long mTimestamp; private List<Layer> mRootLayers; // hierarchical representation of layers private List<Layer> mFlattenedLayers = null; @@ -117,7 +119,7 @@ public class LayersTrace { /** * Constructs the layer hierarchy from a flattened list of layers. */ - static Entry fromFlattenedLayers(long timestamp, LayerProto[] protos) { + public static Entry fromFlattenedLayers(long timestamp, LayerProto[] protos) { SparseArray<Layer> layerMap = new SparseArray<>(); ArrayList<Layer> orphans = new ArrayList<>(); for (LayerProto proto : protos) { @@ -181,7 +183,7 @@ public class LayersTrace { /** * Checks if a region specified by {@code testRect} is covered by all visible layers. */ - Result coversRegion(Rect testRect) { + public Result coversRegion(Rect testRect) { String assertionName = "coversRegion"; Collection<Layer> layers = asFlattenedLayers(); @@ -224,7 +226,7 @@ public class LayersTrace { * Checks if a layer with name {@code layerName} has a visible region * {@code expectedVisibleRegion}. */ - Result hasVisibleRegion(String layerName, Rect expectedVisibleRegion) { + public Result hasVisibleRegion(String layerName, Rect expectedVisibleRegion) { String assertionName = "hasVisibleRegion"; String reason = "Could not find " + layerName; for (Layer layer : asFlattenedLayers()) { @@ -252,7 +254,7 @@ public class LayersTrace { /** * Checks if a layer with name {@code layerName} is visible. */ - Result isVisible(String layerName) { + public Result isVisible(String layerName) { String assertionName = "isVisible"; String reason = "Could not find " + layerName; for (Layer layer : asFlattenedLayers()) { @@ -277,24 +279,27 @@ public class LayersTrace { return mTimestamp; } - List<Layer> getRootLayers() { + public List<Layer> getRootLayers() { return mRootLayers; } - List<Layer> asFlattenedLayers() { + /** + * Returns all layers as a flattened list using a depth first traversal. + */ + public List<Layer> asFlattenedLayers() { if (mFlattenedLayers == null) { - mFlattenedLayers = new ArrayList<>(); + mFlattenedLayers = new LinkedList<>(); ArrayList<Layer> pendingLayers = new ArrayList<>(this.mRootLayers); while (!pendingLayers.isEmpty()) { Layer layer = pendingLayers.remove(0); mFlattenedLayers.add(layer); - pendingLayers.addAll(layer.mChildren); + pendingLayers.addAll(0, layer.mChildren); } } return mFlattenedLayers; } - Rect getVisibleBounds(String layerName) { + public Rect getVisibleBounds(String layerName) { List<Layer> layers = asFlattenedLayers(); for (Layer layer : layers) { if (layer.mProto.name.contains(layerName) && layer.isVisible()) { @@ -308,12 +313,12 @@ public class LayersTrace { /** * Represents a single layer with links to its parent and child layers. */ - static class Layer { + public static class Layer { @Nullable - LayerProto mProto; - List<Layer> mChildren; + public LayerProto mProto; + public List<Layer> mChildren; @Nullable - Layer mParent = null; + public Layer mParent = null; private Layer(LayerProto proto) { this.mProto = proto; @@ -328,16 +333,16 @@ public class LayersTrace { this.mParent = parentLayer; } - int getId() { + public int getId() { return mProto.id; } - boolean isActiveBufferEmpty() { + public boolean isActiveBufferEmpty() { return this.mProto.activeBuffer == null || this.mProto.activeBuffer.height == 0 || this.mProto.activeBuffer.width == 0; } - boolean isVisibleRegionEmpty() { + public boolean isVisibleRegionEmpty() { if (this.mProto.visibleRegion == null) { return true; } @@ -345,32 +350,35 @@ public class LayersTrace { return visibleRect.height() == 0 || visibleRect.width() == 0; } - boolean isHidden() { + public boolean isHidden() { return (this.mProto.flags & /* FLAG_HIDDEN */ 0x1) != 0x0; } - boolean isVisible() { - return (!isActiveBufferEmpty() || isColorLayer()) && - !isHidden() && this.mProto.color.a > 0 && !isVisibleRegionEmpty(); + public boolean isVisible() { + return (!isActiveBufferEmpty() || isColorLayer()) + && !isHidden() + && this.mProto.color != null + && this.mProto.color.a > 0 + && !isVisibleRegionEmpty(); } - boolean isColorLayer() { + public boolean isColorLayer() { return this.mProto.type.equals("ColorLayer"); } - boolean isRootLayer() { + public boolean isRootLayer() { return mParent == null || mParent.mProto == null; } - boolean isInvisible() { + public boolean isInvisible() { return !isVisible(); } - boolean isHiddenByParent() { + public boolean isHiddenByParent() { return !isRootLayer() && (mParent.isHidden() || mParent.isHiddenByParent()); } - String getHiddenByParentReason() { + public String getHiddenByParentReason() { String reason = "Layer " + mProto.name; if (isHiddenByParent()) { reason += " is hidden by parent: " + mParent.mProto.name; @@ -380,7 +388,7 @@ public class LayersTrace { return reason; } - String getVisibilityReason() { + public String getVisibilityReason() { String reason = "Layer " + mProto.name; if (isVisible()) { reason += " is visible:"; @@ -399,7 +407,7 @@ public class LayersTrace { if (isHidden()) { reason += " flags=" + this.mProto.flags + " (FLAG_HIDDEN set)"; } - if (this.mProto.color.a == 0) { + if (this.mProto.color == null || this.mProto.color.a == 0) { reason += " color.a=0"; } if (isVisibleRegionEmpty()) { diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java index 4085810a213d..4a5129ed2269 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java @@ -19,9 +19,10 @@ package com.android.server.wm.flicker; import static com.google.common.truth.Truth.assertAbout; import static com.google.common.truth.Truth.assertWithMessage; -import android.annotation.Nullable; import android.graphics.Rect; +import androidx.annotation.Nullable; + import com.android.server.wm.flicker.Assertions.Result; import com.android.server.wm.flicker.LayersTrace.Entry; import com.android.server.wm.flicker.TransitionRunner.TransitionResult; diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java index 0a3fe3c00de2..241a1c04bdb8 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java @@ -16,10 +16,12 @@ package com.android.server.wm.flicker; -import android.annotation.Nullable; -import android.support.annotation.VisibleForTesting; +import static com.android.server.wm.flicker.monitor.ITransitionMonitor.OUTPUT_DIR; + import android.util.Log; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.test.InstrumentationRegistry; import com.android.server.wm.flicker.monitor.ITransitionMonitor; @@ -89,7 +91,7 @@ import java.util.List; * } * </pre> */ -class TransitionRunner { +public class TransitionRunner { private static final String TAG = "FLICKER"; private final ScreenRecorder mScreenRecorder; private final WindowManagerTraceMonitor mWmTraceMonitor; @@ -128,8 +130,12 @@ class TransitionRunner { mTestTag = builder.mTestTag; } - static TransitionBuilder newBuilder() { - return new TransitionBuilder(); + public static TransitionBuilder newBuilder() { + return newBuilder(OUTPUT_DIR.toString()); + } + + public static TransitionBuilder newBuilder(String outputDir) { + return new TransitionBuilder(outputDir); } /** @@ -138,7 +144,7 @@ class TransitionRunner { * * @return itself */ - TransitionRunner run() { + public TransitionRunner run() { mResults = new ArrayList<>(); mAllRunsMonitors.forEach(ITransitionMonitor::start); mBeforeAlls.forEach(Runnable::run); @@ -159,8 +165,7 @@ class TransitionRunner { mAfterAlls.forEach(Runnable::run); mAllRunsMonitors.forEach(monitor -> { monitor.stop(); - Path path = monitor.save(mTestTag); - Log.e(TAG, "Video saved to " + path.toString()); + monitor.save(mTestTag); }); return this; } @@ -170,7 +175,7 @@ class TransitionRunner { * * @return list of transition results. */ - List<TransitionResult> getResults() { + public List<TransitionResult> getResults() { if (mResults == null) { throw new IllegalStateException("Results do not exist!"); } @@ -182,7 +187,7 @@ class TransitionRunner { * * @return list of transition results. */ - void deleteResults() { + public void deleteResults() { if (mResults == null) { return; } @@ -228,33 +233,33 @@ class TransitionRunner { @VisibleForTesting public static class TransitionResult { @Nullable - final Path layersTrace; + public final Path layersTrace; @Nullable - final Path windowManagerTrace; + public final Path windowManagerTrace; @Nullable - final Path screenCaptureVideo; + public final Path screenCaptureVideo; private boolean flaggedForSaving; - TransitionResult(@Nullable Path layersTrace, @Nullable Path windowManagerTrace, + public TransitionResult(@Nullable Path layersTrace, @Nullable Path windowManagerTrace, @Nullable Path screenCaptureVideo) { this.layersTrace = layersTrace; this.windowManagerTrace = windowManagerTrace; this.screenCaptureVideo = screenCaptureVideo; } - void flagForSaving() { + public void flagForSaving() { flaggedForSaving = true; } - boolean canDelete() { + public boolean canDelete() { return !flaggedForSaving; } - boolean layersTraceExists() { + public boolean layersTraceExists() { return layersTrace != null && layersTrace.toFile().exists(); } - byte[] getLayersTrace() { + public byte[] getLayersTrace() { try { return Files.toByteArray(this.layersTrace.toFile()); } catch (IOException e) { @@ -262,11 +267,11 @@ class TransitionRunner { } } - Path getLayersTracePath() { + public Path getLayersTracePath() { return layersTrace; } - boolean windowManagerTraceExists() { + public boolean windowManagerTraceExists() { return windowManagerTrace != null && windowManagerTrace.toFile().exists(); } @@ -278,19 +283,19 @@ class TransitionRunner { } } - Path getWindowManagerTracePath() { + public Path getWindowManagerTracePath() { return windowManagerTrace; } - boolean screenCaptureVideoExists() { + public boolean screenCaptureVideoExists() { return screenCaptureVideo != null && screenCaptureVideo.toFile().exists(); } - Path screenCaptureVideoPath() { + public Path screenCaptureVideoPath() { return screenCaptureVideo; } - void delete() { + public void delete() { if (layersTraceExists()) layersTrace.toFile().delete(); if (windowManagerTraceExists()) windowManagerTrace.toFile().delete(); if (screenCaptureVideoExists()) screenCaptureVideo.toFile().delete(); @@ -300,7 +305,7 @@ class TransitionRunner { /** * Builds a {@link TransitionRunner} instance. */ - static class TransitionBuilder { + public static class TransitionBuilder { private ScreenRecorder mScreenRecorder; private WindowManagerTraceMonitor mWmTraceMonitor; private LayersTraceMonitor mLayersTraceMonitor; @@ -323,15 +328,15 @@ class TransitionRunner { private boolean mRecordAllRuns = false; - TransitionBuilder() { + public TransitionBuilder(String outputDir) { mScreenRecorder = new ScreenRecorder(); - mWmTraceMonitor = new WindowManagerTraceMonitor(); - mLayersTraceMonitor = new LayersTraceMonitor(); + mWmTraceMonitor = new WindowManagerTraceMonitor(outputDir); + mLayersTraceMonitor = new LayersTraceMonitor(outputDir); mFrameStatsMonitor = new WindowAnimationFrameStatsMonitor(InstrumentationRegistry.getInstrumentation()); } - TransitionRunner build() { + public TransitionRunner build() { if (mCaptureWindowManagerTrace) { mPerRunMonitors.add(mWmTraceMonitor); } @@ -355,52 +360,52 @@ class TransitionRunner { return new TransitionRunner(this); } - TransitionBuilder runBeforeAll(Runnable runnable) { + public TransitionBuilder runBeforeAll(Runnable runnable) { mBeforeAlls.add(runnable); return this; } - TransitionBuilder runBefore(Runnable runnable) { + public TransitionBuilder runBefore(Runnable runnable) { mBefores.add(runnable); return this; } - TransitionBuilder run(Runnable runnable) { + public TransitionBuilder run(Runnable runnable) { mTransitions.add(runnable); return this; } - TransitionBuilder runAfter(Runnable runnable) { + public TransitionBuilder runAfter(Runnable runnable) { mAfters.add(runnable); return this; } - TransitionBuilder runAfterAll(Runnable runnable) { + public TransitionBuilder runAfterAll(Runnable runnable) { mAfterAlls.add(runnable); return this; } - TransitionBuilder repeat(int iterations) { + public TransitionBuilder repeat(int iterations) { mIterations = iterations; return this; } - TransitionBuilder skipWindowManagerTrace() { + public TransitionBuilder skipWindowManagerTrace() { mCaptureWindowManagerTrace = false; return this; } - TransitionBuilder skipLayersTrace() { + public TransitionBuilder skipLayersTrace() { mCaptureLayersTrace = false; return this; } - TransitionBuilder includeJankyRuns() { + public TransitionBuilder includeJankyRuns() { mRunJankFree = false; return this; } - TransitionBuilder recordEachRun() { + public TransitionBuilder recordEachRun() { if (mRecordAllRuns) { throw new IllegalArgumentException("Invalid option with recordAllRuns"); } @@ -408,7 +413,7 @@ class TransitionRunner { return this; } - TransitionBuilder recordAllRuns() { + public TransitionBuilder recordAllRuns() { if (mRecordEachRun) { throw new IllegalArgumentException("Invalid option with recordEachRun"); } @@ -416,7 +421,11 @@ class TransitionRunner { return this; } - TransitionBuilder withTag(String testTag) { + public TransitionBuilder withTag(String testTag) { + if (testTag.contains(" ")) { + throw new IllegalArgumentException("The test tag can not contain spaces since it " + + "is a part of the file name"); + } mTestTag = testTag; return this; } diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java index e3592eb8cd01..412e72d82e55 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java @@ -16,7 +16,7 @@ package com.android.server.wm.flicker; -import android.annotation.Nullable; +import androidx.annotation.Nullable; import com.android.server.wm.flicker.Assertions.Result; import com.android.server.wm.nano.AppWindowTokenProto; @@ -58,7 +58,7 @@ public class WindowManagerTrace { * @param data binary proto data * @param source Path to source of data for additional debug information */ - static WindowManagerTrace parseFrom(byte[] data, Path source) { + public static WindowManagerTrace parseFrom(byte[] data, Path source) { List<Entry> entries = new ArrayList<>(); WindowManagerTraceFileProto fileProto; @@ -73,7 +73,7 @@ public class WindowManagerTrace { return new WindowManagerTrace(entries, source); } - static WindowManagerTrace parseFrom(byte[] data) { + public static WindowManagerTrace parseFrom(byte[] data) { return parseFrom(data, null); } @@ -81,7 +81,7 @@ public class WindowManagerTrace { return mEntries; } - Entry getEntry(long timestamp) { + public Entry getEntry(long timestamp) { Optional<Entry> entry = mEntries.stream() .filter(e -> e.getTimestamp() == timestamp) .findFirst(); @@ -91,17 +91,17 @@ public class WindowManagerTrace { return entry.get(); } - Optional<Path> getSource() { + public Optional<Path> getSource() { return Optional.ofNullable(mSource); } /** * Represents a single WindowManager trace entry. */ - static class Entry implements ITraceEntry { + public static class Entry implements ITraceEntry { private final WindowManagerTraceProto mProto; - Entry(WindowManagerTraceProto proto) { + public Entry(WindowManagerTraceProto proto) { mProto = proto; } @@ -162,7 +162,7 @@ public class WindowManagerTrace { /** * Checks if aboveAppWindow with {@code windowTitle} is visible. */ - Result isAboveAppWindowVisible(String windowTitle) { + public Result isAboveAppWindowVisible(String windowTitle) { WindowTokenProto[] windowTokenProtos = mProto.windowManagerService .rootWindowContainer .displays[DEFAULT_DISPLAY].aboveAppWindows; @@ -173,7 +173,7 @@ public class WindowManagerTrace { /** * Checks if belowAppWindow with {@code windowTitle} is visible. */ - Result isBelowAppWindowVisible(String windowTitle) { + public Result isBelowAppWindowVisible(String windowTitle) { WindowTokenProto[] windowTokenProtos = mProto.windowManagerService .rootWindowContainer .displays[DEFAULT_DISPLAY].belowAppWindows; @@ -185,7 +185,7 @@ public class WindowManagerTrace { /** * Checks if imeWindow with {@code windowTitle} is visible. */ - Result isImeWindowVisible(String windowTitle) { + public Result isImeWindowVisible(String windowTitle) { WindowTokenProto[] windowTokenProtos = mProto.windowManagerService .rootWindowContainer .displays[DEFAULT_DISPLAY].imeWindows; @@ -197,7 +197,7 @@ public class WindowManagerTrace { /** * Checks if app window with {@code windowTitle} is on top. */ - Result isVisibleAppWindowOnTop(String windowTitle) { + public Result isVisibleAppWindowOnTop(String windowTitle) { String topAppWindow = getTopVisibleAppWindow(); boolean success = topAppWindow.contains(windowTitle); String reason = "wanted=" + windowTitle + " found=" + topAppWindow; @@ -207,7 +207,7 @@ public class WindowManagerTrace { /** * Checks if app window with {@code windowTitle} is visible. */ - Result isAppWindowVisible(String windowTitle) { + public Result isAppWindowVisible(String windowTitle) { final String assertionName = "isAppWindowVisible"; boolean titleFound = false; StackProto[] stacks = mProto.windowManagerService.rootWindowContainer diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java index c54396f895e4..3d25fbed5135 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java @@ -28,9 +28,9 @@ import androidx.test.InstrumentationRegistry; /** * Helper functions to retrieve system window sizes and positions. */ -class WindowUtils { +public class WindowUtils { - static Rect getDisplayBounds() { + public static Rect getDisplayBounds() { Point display = new Point(); WindowManager wm = (WindowManager) InstrumentationRegistry.getContext().getSystemService( @@ -46,7 +46,7 @@ class WindowUtils { return wm.getDefaultDisplay().getRotation(); } - static Rect getDisplayBounds(int requestedRotation) { + public static Rect getDisplayBounds(int requestedRotation) { Rect displayBounds = getDisplayBounds(); int currentDisplayRotation = getCurrentRotation(); @@ -66,7 +66,7 @@ class WindowUtils { } - static Rect getAppPosition(int requestedRotation) { + public static Rect getAppPosition(int requestedRotation) { Rect displayBounds = getDisplayBounds(); int currentDisplayRotation = getCurrentRotation(); @@ -85,7 +85,7 @@ class WindowUtils { return new Rect(0, 0, displayBounds.width(), displayBounds.height()); } - static Rect getStatusBarPosition(int requestedRotation) { + public static Rect getStatusBarPosition(int requestedRotation) { Resources resources = InstrumentationRegistry.getContext().getResources(); String resourceName; Rect displayBounds = getDisplayBounds(); @@ -104,7 +104,7 @@ class WindowUtils { return new Rect(0, 0, width, height); } - static Rect getNavigationBarPosition(int requestedRotation) { + public static Rect getNavigationBarPosition(int requestedRotation) { Resources resources = InstrumentationRegistry.getContext().getResources(); Rect displayBounds = getDisplayBounds(); int displayWidth = Math.min(displayBounds.width(), displayBounds.height()); @@ -129,13 +129,13 @@ class WindowUtils { } } - static int getNavigationBarHeight() { + public static int getNavigationBarHeight() { Resources resources = InstrumentationRegistry.getContext().getResources(); int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android"); return resources.getDimensionPixelSize(resourceId); } - static int getDockedStackDividerInset() { + public static int getDockedStackDividerInset() { Resources resources = InstrumentationRegistry.getContext().getResources(); int resourceId = resources.getIdentifier("docked_stack_divider_insets", "dimen", "android"); diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java index e76da6e90834..064cc2702f39 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java @@ -19,7 +19,7 @@ package com.android.server.wm.flicker; import static com.google.common.truth.Truth.assertAbout; import static com.google.common.truth.Truth.assertWithMessage; -import android.annotation.Nullable; +import androidx.annotation.Nullable; import com.android.server.wm.flicker.Assertions.Result; import com.android.server.wm.flicker.TransitionRunner.TransitionResult; diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/helpers/AutomationUtils.java index e00a2474556c..6821ff02e371 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/helpers/AutomationUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.wm.flicker; +package com.android.server.wm.flicker.helpers; import static android.os.SystemClock.sleep; import static android.system.helpers.OverviewHelper.isRecentsInLauncher; @@ -44,6 +44,8 @@ import android.view.ViewConfiguration; import androidx.test.InstrumentationRegistry; +import com.android.server.wm.flicker.WindowUtils; + /** * Collection of UI Automation helper functions. */ @@ -70,14 +72,14 @@ public class AutomationUtils { * This removes some delays when using the UIAutomator library required to create fast UI * transitions. */ - static void setFastWait() { + public static void setFastWait() { Configurator.getInstance().setWaitForIdleTimeout(0); } /** * Reverts {@link android.app.UiAutomation#waitForIdle(long, long)} to default behavior. */ - static void setDefaultWait() { + public static void setDefaultWait() { Configurator.getInstance().setWaitForIdleTimeout(10000); } @@ -124,7 +126,7 @@ public class AutomationUtils { device.waitForIdle(); } - static void clearRecents(UiDevice device) { + public static void clearRecents(UiDevice device) { if (isQuickstepEnabled(device)) { openQuickstep(device); @@ -201,7 +203,7 @@ public class AutomationUtils { sleep(2000); } - static void resizeSplitScreen(UiDevice device, Rational windowHeightRatio) { + public static void resizeSplitScreen(UiDevice device, Rational windowHeightRatio) { BySelector dividerSelector = By.res(SYSTEMUI_PACKAGE, "docked_divider_handle"); UiObject2 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT); assertNotNull("Unable to find Split screen divider", divider); @@ -218,7 +220,7 @@ public class AutomationUtils { sleep(2000); } - static void closePipWindow(UiDevice device) { + public static void closePipWindow(UiDevice device) { UiObject2 pipWindow = device.findObject( By.res(SYSTEMUI_PACKAGE, "background")); pipWindow.click(); @@ -229,7 +231,7 @@ public class AutomationUtils { sleep(2000); } - static void expandPipWindow(UiDevice device) { + public static void expandPipWindow(UiDevice device) { UiObject2 pipWindow = device.findObject( By.res(SYSTEMUI_PACKAGE, "background")); pipWindow.click(); diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java index c55d068b41b8..da75b3e86d6b 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java @@ -16,21 +16,22 @@ package com.android.server.wm.flicker.monitor; -import android.os.IBinder; -import android.os.Parcel; import android.os.RemoteException; -import android.os.ServiceManager; -import android.util.Log; +import android.view.IWindowManager; +import android.view.WindowManagerGlobal; /** * Captures Layers trace from SurfaceFlinger. */ public class LayersTraceMonitor extends TraceMonitor { - private static final String TAG = "LayersTraceMonitor"; - private IBinder mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger"); + private IWindowManager mWm = WindowManagerGlobal.getWindowManagerService(); public LayersTraceMonitor() { - traceFileName = "layers_trace.pb"; + this(OUTPUT_DIR.toString()); + } + + public LayersTraceMonitor(String outputDir) { + super(outputDir, "layers_trace.pb"); } @Override @@ -45,30 +46,19 @@ public class LayersTraceMonitor extends TraceMonitor { @Override public boolean isEnabled() throws RemoteException { - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - data.writeInterfaceToken("android.ui.ISurfaceComposer"); - mSurfaceFlinger.transact(/* LAYER_TRACE_STATUS_CODE */ 1026, - data, reply, 0 /* flags */); - return reply.readBoolean(); + try { + return mWm.isLayerTracing(); + } catch (RemoteException e) { + e.printStackTrace(); + } + return false; } private void setEnabled(boolean isEnabled) { - Parcel data = null; try { - if (mSurfaceFlinger != null) { - data = Parcel.obtain(); - data.writeInterfaceToken("android.ui.ISurfaceComposer"); - data.writeInt(isEnabled ? 1 : 0); - mSurfaceFlinger.transact( /* LAYER_TRACE_CONTROL_CODE */ 1025, - data, null, 0 /* flags */); - } + mWm.setLayerTracing(isEnabled); } catch (RemoteException e) { - Log.e(TAG, "Could not set layer tracing." + e.toString()); - } finally { - if (data != null) { - data.recycle(); - } + e.printStackTrace(); } } } diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java index 4787586777ae..dce1c2739b15 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java @@ -20,25 +20,25 @@ import static com.android.compatibility.common.util.SystemUtil.runShellCommand; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; -import android.support.annotation.VisibleForTesting; import android.util.Log; +import androidx.annotation.VisibleForTesting; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; /** * Captures screen contents and saves it as a mp4 video file. */ public class ScreenRecorder implements ITransitionMonitor { @VisibleForTesting - static final Path DEFAULT_OUTPUT_PATH = OUTPUT_DIR.resolve("transition.mp4"); + public static final Path DEFAULT_OUTPUT_PATH = OUTPUT_DIR.resolve("transition.mp4"); private static final String TAG = "FLICKER"; private Thread recorderThread; @VisibleForTesting - static Path getPath(String testTag) { + public static Path getPath(String testTag) { return OUTPUT_DIR.resolve(testTag + ".mp4"); } @@ -69,8 +69,10 @@ public class ScreenRecorder implements ITransitionMonitor { @Override public Path save(String testTag) { try { - return Files.move(DEFAULT_OUTPUT_PATH, getPath(testTag), + Path targetPath = Files.move(DEFAULT_OUTPUT_PATH, getPath(testTag), REPLACE_EXISTING); + Log.i(TAG, "Video saved to " + targetPath.toString()); + return targetPath; } catch (IOException e) { throw new RuntimeException(e); } diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java index 0e154ecd5d4d..1ba36bba92ef 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java @@ -20,7 +20,7 @@ import static com.android.compatibility.common.util.SystemUtil.runShellCommand; import android.os.RemoteException; -import com.android.internal.annotations.VisibleForTesting; +import androidx.annotation.VisibleForTesting; import java.nio.file.Path; import java.nio.file.Paths; @@ -34,9 +34,15 @@ public abstract class TraceMonitor implements ITransitionMonitor { public static final String TAG = "FLICKER"; private static final String TRACE_DIR = "/data/misc/wmtrace/"; - String traceFileName; + private Path mOutputDir; + public String mTraceFileName; - abstract boolean isEnabled() throws RemoteException; + public abstract boolean isEnabled() throws RemoteException; + + public TraceMonitor(String outputDir, String traceFileName) { + mOutputDir = Paths.get(outputDir); + mTraceFileName = traceFileName; + } /** * Saves trace file to the external storage directory suffixing the name with the testtag @@ -53,14 +59,16 @@ public abstract class TraceMonitor implements ITransitionMonitor { public Path save(String testTag) { OUTPUT_DIR.toFile().mkdirs(); Path traceFileCopy = getOutputTraceFilePath(testTag); + + // Read the input stream fully. String copyCommand = String.format(Locale.getDefault(), "mv %s%s %s", TRACE_DIR, - traceFileName, traceFileCopy.toString()); + mTraceFileName, traceFileCopy.toString()); runShellCommand(copyCommand); return traceFileCopy; } @VisibleForTesting - Path getOutputTraceFilePath(String testTag) { - return OUTPUT_DIR.resolve(traceFileName + "_" + testTag); + public Path getOutputTraceFilePath(String testTag) { + return mOutputDir.resolve(mTraceFileName + "_" + testTag); } } diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java index ae160b68c976..11de4aa86343 100644 --- a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java +++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java @@ -24,16 +24,20 @@ import android.view.WindowManagerGlobal; * Captures WindowManager trace from WindowManager. */ public class WindowManagerTraceMonitor extends TraceMonitor { - private IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + private IWindowManager mWm = WindowManagerGlobal.getWindowManagerService(); public WindowManagerTraceMonitor() { - traceFileName = "wm_trace.pb"; + this(OUTPUT_DIR.toString()); + } + + public WindowManagerTraceMonitor(String outputDir) { + super(outputDir, "wm_trace.pb"); } @Override public void start() { try { - wm.startWindowTrace(); + mWm.startWindowTrace(); } catch (RemoteException e) { throw new RuntimeException("Could not start trace", e); } @@ -42,7 +46,7 @@ public class WindowManagerTraceMonitor extends TraceMonitor { @Override public void stop() { try { - wm.stopWindowTrace(); + mWm.stopWindowTrace(); } catch (RemoteException e) { throw new RuntimeException("Could not stop trace", e); } @@ -50,6 +54,6 @@ public class WindowManagerTraceMonitor extends TraceMonitor { @Override public boolean isEnabled() throws RemoteException{ - return wm.isWindowTraceEnabled(); + return mWm.isWindowTraceEnabled(); } } diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java index dd6fed04d3e6..f31238477e95 100644 --- a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java +++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java @@ -16,7 +16,7 @@ package com.android.server.wm.flicker.monitor; -import static com.android.server.wm.flicker.AutomationUtils.wakeUpAndGoToHomeScreen; +import static com.android.server.wm.flicker.helpers.AutomationUtils.wakeUpAndGoToHomeScreen; import androidx.test.InstrumentationRegistry; diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java index 65888acc184b..5cf2c1cd6827 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java @@ -19,12 +19,12 @@ package com.android.server.wm.flicker; import static android.os.SystemClock.sleep; import static android.view.Surface.rotationToString; -import static com.android.server.wm.flicker.AutomationUtils.clearRecents; -import static com.android.server.wm.flicker.AutomationUtils.closePipWindow; -import static com.android.server.wm.flicker.AutomationUtils.exitSplitScreen; -import static com.android.server.wm.flicker.AutomationUtils.expandPipWindow; -import static com.android.server.wm.flicker.AutomationUtils.launchSplitScreen; -import static com.android.server.wm.flicker.AutomationUtils.stopPackage; +import static com.android.server.wm.flicker.helpers.AutomationUtils.clearRecents; +import static com.android.server.wm.flicker.helpers.AutomationUtils.closePipWindow; +import static com.android.server.wm.flicker.helpers.AutomationUtils.exitSplitScreen; +import static com.android.server.wm.flicker.helpers.AutomationUtils.expandPipWindow; +import static com.android.server.wm.flicker.helpers.AutomationUtils.launchSplitScreen; +import static com.android.server.wm.flicker.helpers.AutomationUtils.stopPackage; import android.content.Context; import android.content.Intent; @@ -40,6 +40,7 @@ import android.view.Surface; import androidx.test.InstrumentationRegistry; import com.android.server.wm.flicker.TransitionRunner.TransitionBuilder; +import com.android.server.wm.flicker.helpers.AutomationUtils; /** * Collection of common transitions which can be used to test different apps or scenarios. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java index 00e11c0cef41..8c9d6b4dc7a0 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java @@ -16,7 +16,7 @@ package com.android.server.wm.flicker; -import static com.android.server.wm.flicker.AutomationUtils.setDefaultWait; +import static com.android.server.wm.flicker.helpers.AutomationUtils.setDefaultWait; import static com.google.common.truth.Truth.assertWithMessage; |