Merge "Exposing flickerlib classes and layer tracing to sysui"

This commit is contained in:
Treehugger Robot
2019-09-19 13:42:49 +00:00
committed by Gerrit Code Review
20 changed files with 285 additions and 177 deletions

View File

@@ -638,4 +638,14 @@ interface IWindowManager
* native InputManager before proceeding with tests. * native InputManager before proceeding with tests.
*/ */
void syncInputTransactions(); void syncInputTransactions();
/**
* Returns whether SurfaceFlinger layer tracing is enabled.
*/
boolean isLayerTracing();
/**
* Enables/disables SurfaceFlinger layer tracing.
*/
void setLayerTracing(boolean enabled);
} }

View File

@@ -7778,4 +7778,64 @@ public class WindowManagerService extends IWindowManager.Stub
0 /* configChanges */, !PRESERVE_WINDOWS, true /* notifyClients */); 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);
}
}
} }

View File

@@ -29,11 +29,24 @@ 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 { java_library {
name: "flickerautomationhelperlib", name: "flickerautomationhelperlib",
sdk_version: "test_current", sdk_version: "test_current",
srcs: [ 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", "src/com/android/server/wm/flicker/WindowUtils.java",
], ],
static_libs: [ static_libs: [

View File

@@ -24,14 +24,14 @@ import java.util.function.Function;
* results. Assertions are functions that are applied over a single trace entry and returns a * 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. * result which includes a detailed reason if the assertion fails.
*/ */
class Assertions { public class Assertions {
/** /**
* Checks assertion on a single trace entry. * Checks assertion on a single trace entry.
* *
* @param <T> trace entry type to perform the assertion on. * @param <T> trace entry type to perform the assertion on.
*/ */
@FunctionalInterface @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. * 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. * Checks assertion on a single layers trace entry.
*/ */
@FunctionalInterface @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 * Utility class to store assertions with an identifier to help generate more useful debug
* data when dealing with multiple assertions. * data when dealing with multiple assertions.
*/ */
static class NamedAssertion<T> { public static class NamedAssertion<T> {
final TraceAssertion<T> assertion; public final TraceAssertion<T> assertion;
final String name; public final String name;
NamedAssertion(TraceAssertion<T> assertion, String name) { public NamedAssertion(TraceAssertion<T> assertion, String name) {
this.assertion = assertion; this.assertion = assertion;
this.name = name; this.name = name;
} }
@@ -67,21 +67,21 @@ class Assertions {
/** /**
* Contains the result of an assertion including the reason for failed assertions. * Contains the result of an assertion including the reason for failed assertions.
*/ */
static class Result { public static class Result {
static final String NEGATION_PREFIX = "!"; public static final String NEGATION_PREFIX = "!";
final boolean success; public final boolean success;
final long timestamp; public final long timestamp;
final String assertionName; public final String assertionName;
final String reason; public final String reason;
Result(boolean success, long timestamp, String assertionName, String reason) { public Result(boolean success, long timestamp, String assertionName, String reason) {
this.success = success; this.success = success;
this.timestamp = timestamp; this.timestamp = timestamp;
this.assertionName = assertionName; this.assertionName = assertionName;
this.reason = reason; this.reason = reason;
} }
Result(boolean success, String reason) { public Result(boolean success, String reason) {
this.success = success; this.success = success;
this.reason = reason; this.reason = reason;
this.assertionName = ""; this.assertionName = "";
@@ -91,7 +91,7 @@ class Assertions {
/** /**
* Returns the negated {@code Result} and adds a negation prefix to the assertion name. * Returns the negated {@code Result} and adds a negation prefix to the assertion name.
*/ */
Result negate() { public Result negate() {
String negatedAssertionName; String negatedAssertionName;
if (this.assertionName.startsWith(NEGATION_PREFIX)) { if (this.assertionName.startsWith(NEGATION_PREFIX)) {
negatedAssertionName = this.assertionName.substring(NEGATION_PREFIX.length() + 1); negatedAssertionName = this.assertionName.substring(NEGATION_PREFIX.length() + 1);
@@ -101,11 +101,11 @@ class Assertions {
return new Result(!this.success, this.timestamp, negatedAssertionName, this.reason); return new Result(!this.success, this.timestamp, negatedAssertionName, this.reason);
} }
boolean passed() { public boolean passed() {
return this.success; return this.success;
} }
boolean failed() { public boolean failed() {
return !this.success; return !this.success;
} }

View File

@@ -38,11 +38,11 @@ public class AssertionsChecker<T extends ITraceEntry> {
private AssertionOption mOption = AssertionOption.NONE; private AssertionOption mOption = AssertionOption.NONE;
private List<NamedAssertion<T>> mAssertions = new LinkedList<>(); 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)); mAssertions.add(new NamedAssertion<>(assertion, name));
} }
void filterByRange(long startTime, long endTime) { public void filterByRange(long startTime, long endTime) {
mFilterEntriesByRange = true; mFilterEntriesByRange = true;
mFilterStartTime = startTime; mFilterStartTime = startTime;
mFilterEndTime = endTime; mFilterEndTime = endTime;
@@ -75,7 +75,7 @@ public class AssertionsChecker<T extends ITraceEntry> {
* @param entries list of entries to perform assertions on * @param entries list of entries to perform assertions on
* @return list of failed assertion results * @return list of failed assertion results
*/ */
List<Result> test(List<T> entries) { public List<Result> test(List<T> entries) {
List<T> filteredEntries; List<T> filteredEntries;
List<Result> failures; List<Result> failures;

View File

@@ -19,7 +19,7 @@ package com.android.server.wm.flicker;
/** /**
* Common interface for Layer and WindowManager trace entries. * Common interface for Layer and WindowManager trace entries.
*/ */
interface ITraceEntry { public interface ITraceEntry {
/** /**
* @return timestamp of current entry * @return timestamp of current entry
*/ */

View File

@@ -16,7 +16,6 @@
package com.android.server.wm.flicker; package com.android.server.wm.flicker;
import android.annotation.Nullable;
import android.graphics.Rect; import android.graphics.Rect;
import android.surfaceflinger.nano.Layers.LayerProto; import android.surfaceflinger.nano.Layers.LayerProto;
import android.surfaceflinger.nano.Layers.RectProto; import android.surfaceflinger.nano.Layers.RectProto;
@@ -25,11 +24,14 @@ import android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto;
import android.surfaceflinger.nano.Layerstrace.LayersTraceProto; import android.surfaceflinger.nano.Layerstrace.LayersTraceProto;
import android.util.SparseArray; import android.util.SparseArray;
import androidx.annotation.Nullable;
import com.android.server.wm.flicker.Assertions.Result; import com.android.server.wm.flicker.Assertions.Result;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -57,7 +59,7 @@ public class LayersTrace {
* @param data binary proto data * @param data binary proto data
* @param source Path to source of data for additional debug information * @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<>(); List<Entry> entries = new ArrayList<>();
LayersTraceFileProto fileProto; LayersTraceFileProto fileProto;
try { try {
@@ -79,15 +81,15 @@ public class LayersTrace {
* *
* @param data binary proto data * @param data binary proto data
*/ */
static LayersTrace parseFrom(byte[] data) { public static LayersTrace parseFrom(byte[] data) {
return parseFrom(data, null); return parseFrom(data, null);
} }
List<Entry> getEntries() { public List<Entry> getEntries() {
return mEntries; return mEntries;
} }
Entry getEntry(long timestamp) { public Entry getEntry(long timestamp) {
Optional<Entry> entry = mEntries.stream() Optional<Entry> entry = mEntries.stream()
.filter(e -> e.getTimestamp() == timestamp) .filter(e -> e.getTimestamp() == timestamp)
.findFirst(); .findFirst();
@@ -97,14 +99,14 @@ public class LayersTrace {
return entry.get(); return entry.get();
} }
Optional<Path> getSource() { public Optional<Path> getSource() {
return Optional.ofNullable(mSource); return Optional.ofNullable(mSource);
} }
/** /**
* Represents a single Layer trace entry. * Represents a single Layer trace entry.
*/ */
static class Entry implements ITraceEntry { public static class Entry implements ITraceEntry {
private long mTimestamp; private long mTimestamp;
private List<Layer> mRootLayers; // hierarchical representation of layers private List<Layer> mRootLayers; // hierarchical representation of layers
private List<Layer> mFlattenedLayers = null; private List<Layer> mFlattenedLayers = null;
@@ -117,7 +119,7 @@ public class LayersTrace {
/** /**
* Constructs the layer hierarchy from a flattened list of layers. * 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<>(); SparseArray<Layer> layerMap = new SparseArray<>();
ArrayList<Layer> orphans = new ArrayList<>(); ArrayList<Layer> orphans = new ArrayList<>();
for (LayerProto proto : protos) { 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. * 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"; String assertionName = "coversRegion";
Collection<Layer> layers = asFlattenedLayers(); Collection<Layer> layers = asFlattenedLayers();
@@ -224,7 +226,7 @@ public class LayersTrace {
* Checks if a layer with name {@code layerName} has a visible region * Checks if a layer with name {@code layerName} has a visible region
* {@code expectedVisibleRegion}. * {@code expectedVisibleRegion}.
*/ */
Result hasVisibleRegion(String layerName, Rect expectedVisibleRegion) { public Result hasVisibleRegion(String layerName, Rect expectedVisibleRegion) {
String assertionName = "hasVisibleRegion"; String assertionName = "hasVisibleRegion";
String reason = "Could not find " + layerName; String reason = "Could not find " + layerName;
for (Layer layer : asFlattenedLayers()) { for (Layer layer : asFlattenedLayers()) {
@@ -252,7 +254,7 @@ public class LayersTrace {
/** /**
* Checks if a layer with name {@code layerName} is visible. * Checks if a layer with name {@code layerName} is visible.
*/ */
Result isVisible(String layerName) { public Result isVisible(String layerName) {
String assertionName = "isVisible"; String assertionName = "isVisible";
String reason = "Could not find " + layerName; String reason = "Could not find " + layerName;
for (Layer layer : asFlattenedLayers()) { for (Layer layer : asFlattenedLayers()) {
@@ -277,24 +279,27 @@ public class LayersTrace {
return mTimestamp; return mTimestamp;
} }
List<Layer> getRootLayers() { public List<Layer> getRootLayers() {
return mRootLayers; return mRootLayers;
} }
List<Layer> asFlattenedLayers() { /**
* Returns all layers as a flattened list using a depth first traversal.
*/
public List<Layer> asFlattenedLayers() {
if (mFlattenedLayers == null) { if (mFlattenedLayers == null) {
mFlattenedLayers = new ArrayList<>(); mFlattenedLayers = new LinkedList<>();
ArrayList<Layer> pendingLayers = new ArrayList<>(this.mRootLayers); ArrayList<Layer> pendingLayers = new ArrayList<>(this.mRootLayers);
while (!pendingLayers.isEmpty()) { while (!pendingLayers.isEmpty()) {
Layer layer = pendingLayers.remove(0); Layer layer = pendingLayers.remove(0);
mFlattenedLayers.add(layer); mFlattenedLayers.add(layer);
pendingLayers.addAll(layer.mChildren); pendingLayers.addAll(0, layer.mChildren);
} }
} }
return mFlattenedLayers; return mFlattenedLayers;
} }
Rect getVisibleBounds(String layerName) { public Rect getVisibleBounds(String layerName) {
List<Layer> layers = asFlattenedLayers(); List<Layer> layers = asFlattenedLayers();
for (Layer layer : layers) { for (Layer layer : layers) {
if (layer.mProto.name.contains(layerName) && layer.isVisible()) { 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. * Represents a single layer with links to its parent and child layers.
*/ */
static class Layer { public static class Layer {
@Nullable @Nullable
LayerProto mProto; public LayerProto mProto;
List<Layer> mChildren; public List<Layer> mChildren;
@Nullable @Nullable
Layer mParent = null; public Layer mParent = null;
private Layer(LayerProto proto) { private Layer(LayerProto proto) {
this.mProto = proto; this.mProto = proto;
@@ -328,16 +333,16 @@ public class LayersTrace {
this.mParent = parentLayer; this.mParent = parentLayer;
} }
int getId() { public int getId() {
return mProto.id; return mProto.id;
} }
boolean isActiveBufferEmpty() { public boolean isActiveBufferEmpty() {
return this.mProto.activeBuffer == null || this.mProto.activeBuffer.height == 0 return this.mProto.activeBuffer == null || this.mProto.activeBuffer.height == 0
|| this.mProto.activeBuffer.width == 0; || this.mProto.activeBuffer.width == 0;
} }
boolean isVisibleRegionEmpty() { public boolean isVisibleRegionEmpty() {
if (this.mProto.visibleRegion == null) { if (this.mProto.visibleRegion == null) {
return true; return true;
} }
@@ -345,32 +350,35 @@ public class LayersTrace {
return visibleRect.height() == 0 || visibleRect.width() == 0; return visibleRect.height() == 0 || visibleRect.width() == 0;
} }
boolean isHidden() { public boolean isHidden() {
return (this.mProto.flags & /* FLAG_HIDDEN */ 0x1) != 0x0; return (this.mProto.flags & /* FLAG_HIDDEN */ 0x1) != 0x0;
} }
boolean isVisible() { public boolean isVisible() {
return (!isActiveBufferEmpty() || isColorLayer()) && return (!isActiveBufferEmpty() || isColorLayer())
!isHidden() && this.mProto.color.a > 0 && !isVisibleRegionEmpty(); && !isHidden()
&& this.mProto.color != null
&& this.mProto.color.a > 0
&& !isVisibleRegionEmpty();
} }
boolean isColorLayer() { public boolean isColorLayer() {
return this.mProto.type.equals("ColorLayer"); return this.mProto.type.equals("ColorLayer");
} }
boolean isRootLayer() { public boolean isRootLayer() {
return mParent == null || mParent.mProto == null; return mParent == null || mParent.mProto == null;
} }
boolean isInvisible() { public boolean isInvisible() {
return !isVisible(); return !isVisible();
} }
boolean isHiddenByParent() { public boolean isHiddenByParent() {
return !isRootLayer() && (mParent.isHidden() || mParent.isHiddenByParent()); return !isRootLayer() && (mParent.isHidden() || mParent.isHiddenByParent());
} }
String getHiddenByParentReason() { public String getHiddenByParentReason() {
String reason = "Layer " + mProto.name; String reason = "Layer " + mProto.name;
if (isHiddenByParent()) { if (isHiddenByParent()) {
reason += " is hidden by parent: " + mParent.mProto.name; reason += " is hidden by parent: " + mParent.mProto.name;
@@ -380,7 +388,7 @@ public class LayersTrace {
return reason; return reason;
} }
String getVisibilityReason() { public String getVisibilityReason() {
String reason = "Layer " + mProto.name; String reason = "Layer " + mProto.name;
if (isVisible()) { if (isVisible()) {
reason += " is visible:"; reason += " is visible:";
@@ -399,7 +407,7 @@ public class LayersTrace {
if (isHidden()) { if (isHidden()) {
reason += " flags=" + this.mProto.flags + " (FLAG_HIDDEN set)"; 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"; reason += " color.a=0";
} }
if (isVisibleRegionEmpty()) { if (isVisibleRegionEmpty()) {

View File

@@ -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.assertAbout;
import static com.google.common.truth.Truth.assertWithMessage; import static com.google.common.truth.Truth.assertWithMessage;
import android.annotation.Nullable;
import android.graphics.Rect; import android.graphics.Rect;
import androidx.annotation.Nullable;
import com.android.server.wm.flicker.Assertions.Result; import com.android.server.wm.flicker.Assertions.Result;
import com.android.server.wm.flicker.LayersTrace.Entry; import com.android.server.wm.flicker.LayersTrace.Entry;
import com.android.server.wm.flicker.TransitionRunner.TransitionResult; import com.android.server.wm.flicker.TransitionRunner.TransitionResult;

View File

@@ -16,10 +16,12 @@
package com.android.server.wm.flicker; package com.android.server.wm.flicker;
import android.annotation.Nullable; import static com.android.server.wm.flicker.monitor.ITransitionMonitor.OUTPUT_DIR;
import android.support.annotation.VisibleForTesting;
import android.util.Log; import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.test.InstrumentationRegistry; import androidx.test.InstrumentationRegistry;
import com.android.server.wm.flicker.monitor.ITransitionMonitor; import com.android.server.wm.flicker.monitor.ITransitionMonitor;
@@ -89,7 +91,7 @@ import java.util.List;
* } * }
* </pre> * </pre>
*/ */
class TransitionRunner { public class TransitionRunner {
private static final String TAG = "FLICKER"; private static final String TAG = "FLICKER";
private final ScreenRecorder mScreenRecorder; private final ScreenRecorder mScreenRecorder;
private final WindowManagerTraceMonitor mWmTraceMonitor; private final WindowManagerTraceMonitor mWmTraceMonitor;
@@ -128,8 +130,12 @@ class TransitionRunner {
mTestTag = builder.mTestTag; mTestTag = builder.mTestTag;
} }
static TransitionBuilder newBuilder() { public static TransitionBuilder newBuilder() {
return new TransitionBuilder(); return newBuilder(OUTPUT_DIR.toString());
}
public static TransitionBuilder newBuilder(String outputDir) {
return new TransitionBuilder(outputDir);
} }
/** /**
@@ -138,7 +144,7 @@ class TransitionRunner {
* *
* @return itself * @return itself
*/ */
TransitionRunner run() { public TransitionRunner run() {
mResults = new ArrayList<>(); mResults = new ArrayList<>();
mAllRunsMonitors.forEach(ITransitionMonitor::start); mAllRunsMonitors.forEach(ITransitionMonitor::start);
mBeforeAlls.forEach(Runnable::run); mBeforeAlls.forEach(Runnable::run);
@@ -159,8 +165,7 @@ class TransitionRunner {
mAfterAlls.forEach(Runnable::run); mAfterAlls.forEach(Runnable::run);
mAllRunsMonitors.forEach(monitor -> { mAllRunsMonitors.forEach(monitor -> {
monitor.stop(); monitor.stop();
Path path = monitor.save(mTestTag); monitor.save(mTestTag);
Log.e(TAG, "Video saved to " + path.toString());
}); });
return this; return this;
} }
@@ -170,7 +175,7 @@ class TransitionRunner {
* *
* @return list of transition results. * @return list of transition results.
*/ */
List<TransitionResult> getResults() { public List<TransitionResult> getResults() {
if (mResults == null) { if (mResults == null) {
throw new IllegalStateException("Results do not exist!"); throw new IllegalStateException("Results do not exist!");
} }
@@ -182,7 +187,7 @@ class TransitionRunner {
* *
* @return list of transition results. * @return list of transition results.
*/ */
void deleteResults() { public void deleteResults() {
if (mResults == null) { if (mResults == null) {
return; return;
} }
@@ -228,33 +233,33 @@ class TransitionRunner {
@VisibleForTesting @VisibleForTesting
public static class TransitionResult { public static class TransitionResult {
@Nullable @Nullable
final Path layersTrace; public final Path layersTrace;
@Nullable @Nullable
final Path windowManagerTrace; public final Path windowManagerTrace;
@Nullable @Nullable
final Path screenCaptureVideo; public final Path screenCaptureVideo;
private boolean flaggedForSaving; private boolean flaggedForSaving;
TransitionResult(@Nullable Path layersTrace, @Nullable Path windowManagerTrace, public TransitionResult(@Nullable Path layersTrace, @Nullable Path windowManagerTrace,
@Nullable Path screenCaptureVideo) { @Nullable Path screenCaptureVideo) {
this.layersTrace = layersTrace; this.layersTrace = layersTrace;
this.windowManagerTrace = windowManagerTrace; this.windowManagerTrace = windowManagerTrace;
this.screenCaptureVideo = screenCaptureVideo; this.screenCaptureVideo = screenCaptureVideo;
} }
void flagForSaving() { public void flagForSaving() {
flaggedForSaving = true; flaggedForSaving = true;
} }
boolean canDelete() { public boolean canDelete() {
return !flaggedForSaving; return !flaggedForSaving;
} }
boolean layersTraceExists() { public boolean layersTraceExists() {
return layersTrace != null && layersTrace.toFile().exists(); return layersTrace != null && layersTrace.toFile().exists();
} }
byte[] getLayersTrace() { public byte[] getLayersTrace() {
try { try {
return Files.toByteArray(this.layersTrace.toFile()); return Files.toByteArray(this.layersTrace.toFile());
} catch (IOException e) { } catch (IOException e) {
@@ -262,11 +267,11 @@ class TransitionRunner {
} }
} }
Path getLayersTracePath() { public Path getLayersTracePath() {
return layersTrace; return layersTrace;
} }
boolean windowManagerTraceExists() { public boolean windowManagerTraceExists() {
return windowManagerTrace != null && windowManagerTrace.toFile().exists(); return windowManagerTrace != null && windowManagerTrace.toFile().exists();
} }
@@ -278,19 +283,19 @@ class TransitionRunner {
} }
} }
Path getWindowManagerTracePath() { public Path getWindowManagerTracePath() {
return windowManagerTrace; return windowManagerTrace;
} }
boolean screenCaptureVideoExists() { public boolean screenCaptureVideoExists() {
return screenCaptureVideo != null && screenCaptureVideo.toFile().exists(); return screenCaptureVideo != null && screenCaptureVideo.toFile().exists();
} }
Path screenCaptureVideoPath() { public Path screenCaptureVideoPath() {
return screenCaptureVideo; return screenCaptureVideo;
} }
void delete() { public void delete() {
if (layersTraceExists()) layersTrace.toFile().delete(); if (layersTraceExists()) layersTrace.toFile().delete();
if (windowManagerTraceExists()) windowManagerTrace.toFile().delete(); if (windowManagerTraceExists()) windowManagerTrace.toFile().delete();
if (screenCaptureVideoExists()) screenCaptureVideo.toFile().delete(); if (screenCaptureVideoExists()) screenCaptureVideo.toFile().delete();
@@ -300,7 +305,7 @@ class TransitionRunner {
/** /**
* Builds a {@link TransitionRunner} instance. * Builds a {@link TransitionRunner} instance.
*/ */
static class TransitionBuilder { public static class TransitionBuilder {
private ScreenRecorder mScreenRecorder; private ScreenRecorder mScreenRecorder;
private WindowManagerTraceMonitor mWmTraceMonitor; private WindowManagerTraceMonitor mWmTraceMonitor;
private LayersTraceMonitor mLayersTraceMonitor; private LayersTraceMonitor mLayersTraceMonitor;
@@ -323,15 +328,15 @@ class TransitionRunner {
private boolean mRecordAllRuns = false; private boolean mRecordAllRuns = false;
TransitionBuilder() { public TransitionBuilder(String outputDir) {
mScreenRecorder = new ScreenRecorder(); mScreenRecorder = new ScreenRecorder();
mWmTraceMonitor = new WindowManagerTraceMonitor(); mWmTraceMonitor = new WindowManagerTraceMonitor(outputDir);
mLayersTraceMonitor = new LayersTraceMonitor(); mLayersTraceMonitor = new LayersTraceMonitor(outputDir);
mFrameStatsMonitor = new mFrameStatsMonitor = new
WindowAnimationFrameStatsMonitor(InstrumentationRegistry.getInstrumentation()); WindowAnimationFrameStatsMonitor(InstrumentationRegistry.getInstrumentation());
} }
TransitionRunner build() { public TransitionRunner build() {
if (mCaptureWindowManagerTrace) { if (mCaptureWindowManagerTrace) {
mPerRunMonitors.add(mWmTraceMonitor); mPerRunMonitors.add(mWmTraceMonitor);
} }
@@ -355,52 +360,52 @@ class TransitionRunner {
return new TransitionRunner(this); return new TransitionRunner(this);
} }
TransitionBuilder runBeforeAll(Runnable runnable) { public TransitionBuilder runBeforeAll(Runnable runnable) {
mBeforeAlls.add(runnable); mBeforeAlls.add(runnable);
return this; return this;
} }
TransitionBuilder runBefore(Runnable runnable) { public TransitionBuilder runBefore(Runnable runnable) {
mBefores.add(runnable); mBefores.add(runnable);
return this; return this;
} }
TransitionBuilder run(Runnable runnable) { public TransitionBuilder run(Runnable runnable) {
mTransitions.add(runnable); mTransitions.add(runnable);
return this; return this;
} }
TransitionBuilder runAfter(Runnable runnable) { public TransitionBuilder runAfter(Runnable runnable) {
mAfters.add(runnable); mAfters.add(runnable);
return this; return this;
} }
TransitionBuilder runAfterAll(Runnable runnable) { public TransitionBuilder runAfterAll(Runnable runnable) {
mAfterAlls.add(runnable); mAfterAlls.add(runnable);
return this; return this;
} }
TransitionBuilder repeat(int iterations) { public TransitionBuilder repeat(int iterations) {
mIterations = iterations; mIterations = iterations;
return this; return this;
} }
TransitionBuilder skipWindowManagerTrace() { public TransitionBuilder skipWindowManagerTrace() {
mCaptureWindowManagerTrace = false; mCaptureWindowManagerTrace = false;
return this; return this;
} }
TransitionBuilder skipLayersTrace() { public TransitionBuilder skipLayersTrace() {
mCaptureLayersTrace = false; mCaptureLayersTrace = false;
return this; return this;
} }
TransitionBuilder includeJankyRuns() { public TransitionBuilder includeJankyRuns() {
mRunJankFree = false; mRunJankFree = false;
return this; return this;
} }
TransitionBuilder recordEachRun() { public TransitionBuilder recordEachRun() {
if (mRecordAllRuns) { if (mRecordAllRuns) {
throw new IllegalArgumentException("Invalid option with recordAllRuns"); throw new IllegalArgumentException("Invalid option with recordAllRuns");
} }
@@ -408,7 +413,7 @@ class TransitionRunner {
return this; return this;
} }
TransitionBuilder recordAllRuns() { public TransitionBuilder recordAllRuns() {
if (mRecordEachRun) { if (mRecordEachRun) {
throw new IllegalArgumentException("Invalid option with recordEachRun"); throw new IllegalArgumentException("Invalid option with recordEachRun");
} }
@@ -416,7 +421,11 @@ class TransitionRunner {
return this; 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; mTestTag = testTag;
return this; return this;
} }

View File

@@ -16,7 +16,7 @@
package com.android.server.wm.flicker; 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.flicker.Assertions.Result;
import com.android.server.wm.nano.AppWindowTokenProto; import com.android.server.wm.nano.AppWindowTokenProto;
@@ -58,7 +58,7 @@ public class WindowManagerTrace {
* @param data binary proto data * @param data binary proto data
* @param source Path to source of data for additional debug information * @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<>(); List<Entry> entries = new ArrayList<>();
WindowManagerTraceFileProto fileProto; WindowManagerTraceFileProto fileProto;
@@ -73,7 +73,7 @@ public class WindowManagerTrace {
return new WindowManagerTrace(entries, source); return new WindowManagerTrace(entries, source);
} }
static WindowManagerTrace parseFrom(byte[] data) { public static WindowManagerTrace parseFrom(byte[] data) {
return parseFrom(data, null); return parseFrom(data, null);
} }
@@ -81,7 +81,7 @@ public class WindowManagerTrace {
return mEntries; return mEntries;
} }
Entry getEntry(long timestamp) { public Entry getEntry(long timestamp) {
Optional<Entry> entry = mEntries.stream() Optional<Entry> entry = mEntries.stream()
.filter(e -> e.getTimestamp() == timestamp) .filter(e -> e.getTimestamp() == timestamp)
.findFirst(); .findFirst();
@@ -91,17 +91,17 @@ public class WindowManagerTrace {
return entry.get(); return entry.get();
} }
Optional<Path> getSource() { public Optional<Path> getSource() {
return Optional.ofNullable(mSource); return Optional.ofNullable(mSource);
} }
/** /**
* Represents a single WindowManager trace entry. * Represents a single WindowManager trace entry.
*/ */
static class Entry implements ITraceEntry { public static class Entry implements ITraceEntry {
private final WindowManagerTraceProto mProto; private final WindowManagerTraceProto mProto;
Entry(WindowManagerTraceProto proto) { public Entry(WindowManagerTraceProto proto) {
mProto = proto; mProto = proto;
} }
@@ -162,7 +162,7 @@ public class WindowManagerTrace {
/** /**
* Checks if aboveAppWindow with {@code windowTitle} is visible. * Checks if aboveAppWindow with {@code windowTitle} is visible.
*/ */
Result isAboveAppWindowVisible(String windowTitle) { public Result isAboveAppWindowVisible(String windowTitle) {
WindowTokenProto[] windowTokenProtos = mProto.windowManagerService WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
.rootWindowContainer .rootWindowContainer
.displays[DEFAULT_DISPLAY].aboveAppWindows; .displays[DEFAULT_DISPLAY].aboveAppWindows;
@@ -173,7 +173,7 @@ public class WindowManagerTrace {
/** /**
* Checks if belowAppWindow with {@code windowTitle} is visible. * Checks if belowAppWindow with {@code windowTitle} is visible.
*/ */
Result isBelowAppWindowVisible(String windowTitle) { public Result isBelowAppWindowVisible(String windowTitle) {
WindowTokenProto[] windowTokenProtos = mProto.windowManagerService WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
.rootWindowContainer .rootWindowContainer
.displays[DEFAULT_DISPLAY].belowAppWindows; .displays[DEFAULT_DISPLAY].belowAppWindows;
@@ -185,7 +185,7 @@ public class WindowManagerTrace {
/** /**
* Checks if imeWindow with {@code windowTitle} is visible. * Checks if imeWindow with {@code windowTitle} is visible.
*/ */
Result isImeWindowVisible(String windowTitle) { public Result isImeWindowVisible(String windowTitle) {
WindowTokenProto[] windowTokenProtos = mProto.windowManagerService WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
.rootWindowContainer .rootWindowContainer
.displays[DEFAULT_DISPLAY].imeWindows; .displays[DEFAULT_DISPLAY].imeWindows;
@@ -197,7 +197,7 @@ public class WindowManagerTrace {
/** /**
* Checks if app window with {@code windowTitle} is on top. * Checks if app window with {@code windowTitle} is on top.
*/ */
Result isVisibleAppWindowOnTop(String windowTitle) { public Result isVisibleAppWindowOnTop(String windowTitle) {
String topAppWindow = getTopVisibleAppWindow(); String topAppWindow = getTopVisibleAppWindow();
boolean success = topAppWindow.contains(windowTitle); boolean success = topAppWindow.contains(windowTitle);
String reason = "wanted=" + windowTitle + " found=" + topAppWindow; String reason = "wanted=" + windowTitle + " found=" + topAppWindow;
@@ -207,7 +207,7 @@ public class WindowManagerTrace {
/** /**
* Checks if app window with {@code windowTitle} is visible. * Checks if app window with {@code windowTitle} is visible.
*/ */
Result isAppWindowVisible(String windowTitle) { public Result isAppWindowVisible(String windowTitle) {
final String assertionName = "isAppWindowVisible"; final String assertionName = "isAppWindowVisible";
boolean titleFound = false; boolean titleFound = false;
StackProto[] stacks = mProto.windowManagerService.rootWindowContainer StackProto[] stacks = mProto.windowManagerService.rootWindowContainer

View File

@@ -28,9 +28,9 @@ import androidx.test.InstrumentationRegistry;
/** /**
* Helper functions to retrieve system window sizes and positions. * 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(); Point display = new Point();
WindowManager wm = WindowManager wm =
(WindowManager) InstrumentationRegistry.getContext().getSystemService( (WindowManager) InstrumentationRegistry.getContext().getSystemService(
@@ -46,7 +46,7 @@ class WindowUtils {
return wm.getDefaultDisplay().getRotation(); return wm.getDefaultDisplay().getRotation();
} }
static Rect getDisplayBounds(int requestedRotation) { public static Rect getDisplayBounds(int requestedRotation) {
Rect displayBounds = getDisplayBounds(); Rect displayBounds = getDisplayBounds();
int currentDisplayRotation = getCurrentRotation(); int currentDisplayRotation = getCurrentRotation();
@@ -66,7 +66,7 @@ class WindowUtils {
} }
static Rect getAppPosition(int requestedRotation) { public static Rect getAppPosition(int requestedRotation) {
Rect displayBounds = getDisplayBounds(); Rect displayBounds = getDisplayBounds();
int currentDisplayRotation = getCurrentRotation(); int currentDisplayRotation = getCurrentRotation();
@@ -85,7 +85,7 @@ class WindowUtils {
return new Rect(0, 0, displayBounds.width(), displayBounds.height()); 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(); Resources resources = InstrumentationRegistry.getContext().getResources();
String resourceName; String resourceName;
Rect displayBounds = getDisplayBounds(); Rect displayBounds = getDisplayBounds();
@@ -104,7 +104,7 @@ class WindowUtils {
return new Rect(0, 0, width, height); return new Rect(0, 0, width, height);
} }
static Rect getNavigationBarPosition(int requestedRotation) { public static Rect getNavigationBarPosition(int requestedRotation) {
Resources resources = InstrumentationRegistry.getContext().getResources(); Resources resources = InstrumentationRegistry.getContext().getResources();
Rect displayBounds = getDisplayBounds(); Rect displayBounds = getDisplayBounds();
int displayWidth = Math.min(displayBounds.width(), displayBounds.height()); 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(); Resources resources = InstrumentationRegistry.getContext().getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android"); int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
return resources.getDimensionPixelSize(resourceId); return resources.getDimensionPixelSize(resourceId);
} }
static int getDockedStackDividerInset() { public static int getDockedStackDividerInset() {
Resources resources = InstrumentationRegistry.getContext().getResources(); Resources resources = InstrumentationRegistry.getContext().getResources();
int resourceId = resources.getIdentifier("docked_stack_divider_insets", "dimen", int resourceId = resources.getIdentifier("docked_stack_divider_insets", "dimen",
"android"); "android");

View File

@@ -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.assertAbout;
import static com.google.common.truth.Truth.assertWithMessage; 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.Assertions.Result;
import com.android.server.wm.flicker.TransitionRunner.TransitionResult; import com.android.server.wm.flicker.TransitionRunner.TransitionResult;

View File

@@ -14,7 +14,7 @@
* limitations under the License. * 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.os.SystemClock.sleep;
import static android.system.helpers.OverviewHelper.isRecentsInLauncher; import static android.system.helpers.OverviewHelper.isRecentsInLauncher;
@@ -44,6 +44,8 @@ import android.view.ViewConfiguration;
import androidx.test.InstrumentationRegistry; import androidx.test.InstrumentationRegistry;
import com.android.server.wm.flicker.WindowUtils;
/** /**
* Collection of UI Automation helper functions. * 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 * This removes some delays when using the UIAutomator library required to create fast UI
* transitions. * transitions.
*/ */
static void setFastWait() { public static void setFastWait() {
Configurator.getInstance().setWaitForIdleTimeout(0); Configurator.getInstance().setWaitForIdleTimeout(0);
} }
/** /**
* Reverts {@link android.app.UiAutomation#waitForIdle(long, long)} to default behavior. * Reverts {@link android.app.UiAutomation#waitForIdle(long, long)} to default behavior.
*/ */
static void setDefaultWait() { public static void setDefaultWait() {
Configurator.getInstance().setWaitForIdleTimeout(10000); Configurator.getInstance().setWaitForIdleTimeout(10000);
} }
@@ -124,7 +126,7 @@ public class AutomationUtils {
device.waitForIdle(); device.waitForIdle();
} }
static void clearRecents(UiDevice device) { public static void clearRecents(UiDevice device) {
if (isQuickstepEnabled(device)) { if (isQuickstepEnabled(device)) {
openQuickstep(device); openQuickstep(device);
@@ -201,7 +203,7 @@ public class AutomationUtils {
sleep(2000); 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"); BySelector dividerSelector = By.res(SYSTEMUI_PACKAGE, "docked_divider_handle");
UiObject2 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT); UiObject2 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
assertNotNull("Unable to find Split screen divider", divider); assertNotNull("Unable to find Split screen divider", divider);
@@ -218,7 +220,7 @@ public class AutomationUtils {
sleep(2000); sleep(2000);
} }
static void closePipWindow(UiDevice device) { public static void closePipWindow(UiDevice device) {
UiObject2 pipWindow = device.findObject( UiObject2 pipWindow = device.findObject(
By.res(SYSTEMUI_PACKAGE, "background")); By.res(SYSTEMUI_PACKAGE, "background"));
pipWindow.click(); pipWindow.click();
@@ -229,7 +231,7 @@ public class AutomationUtils {
sleep(2000); sleep(2000);
} }
static void expandPipWindow(UiDevice device) { public static void expandPipWindow(UiDevice device) {
UiObject2 pipWindow = device.findObject( UiObject2 pipWindow = device.findObject(
By.res(SYSTEMUI_PACKAGE, "background")); By.res(SYSTEMUI_PACKAGE, "background"));
pipWindow.click(); pipWindow.click();

View File

@@ -16,21 +16,22 @@
package com.android.server.wm.flicker.monitor; package com.android.server.wm.flicker.monitor;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.view.IWindowManager;
import android.util.Log; import android.view.WindowManagerGlobal;
/** /**
* Captures Layers trace from SurfaceFlinger. * Captures Layers trace from SurfaceFlinger.
*/ */
public class LayersTraceMonitor extends TraceMonitor { public class LayersTraceMonitor extends TraceMonitor {
private static final String TAG = "LayersTraceMonitor"; private IWindowManager mWm = WindowManagerGlobal.getWindowManagerService();
private IBinder mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger");
public LayersTraceMonitor() { public LayersTraceMonitor() {
traceFileName = "layers_trace.pb"; this(OUTPUT_DIR.toString());
}
public LayersTraceMonitor(String outputDir) {
super(outputDir, "layers_trace.pb");
} }
@Override @Override
@@ -45,30 +46,19 @@ public class LayersTraceMonitor extends TraceMonitor {
@Override @Override
public boolean isEnabled() throws RemoteException { public boolean isEnabled() throws RemoteException {
Parcel data = Parcel.obtain(); try {
Parcel reply = Parcel.obtain(); return mWm.isLayerTracing();
data.writeInterfaceToken("android.ui.ISurfaceComposer"); } catch (RemoteException e) {
mSurfaceFlinger.transact(/* LAYER_TRACE_STATUS_CODE */ 1026, e.printStackTrace();
data, reply, 0 /* flags */); }
return reply.readBoolean(); return false;
} }
private void setEnabled(boolean isEnabled) { private void setEnabled(boolean isEnabled) {
Parcel data = null;
try { try {
if (mSurfaceFlinger != null) { mWm.setLayerTracing(isEnabled);
data = Parcel.obtain();
data.writeInterfaceToken("android.ui.ISurfaceComposer");
data.writeInt(isEnabled ? 1 : 0);
mSurfaceFlinger.transact( /* LAYER_TRACE_CONTROL_CODE */ 1025,
data, null, 0 /* flags */);
}
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Could not set layer tracing." + e.toString()); e.printStackTrace();
} finally {
if (data != null) {
data.recycle();
}
} }
} }
} }

View File

@@ -20,25 +20,25 @@ import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import android.support.annotation.VisibleForTesting;
import android.util.Log; import android.util.Log;
import androidx.annotation.VisibleForTesting;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
/** /**
* Captures screen contents and saves it as a mp4 video file. * Captures screen contents and saves it as a mp4 video file.
*/ */
public class ScreenRecorder implements ITransitionMonitor { public class ScreenRecorder implements ITransitionMonitor {
@VisibleForTesting @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 static final String TAG = "FLICKER";
private Thread recorderThread; private Thread recorderThread;
@VisibleForTesting @VisibleForTesting
static Path getPath(String testTag) { public static Path getPath(String testTag) {
return OUTPUT_DIR.resolve(testTag + ".mp4"); return OUTPUT_DIR.resolve(testTag + ".mp4");
} }
@@ -69,8 +69,10 @@ public class ScreenRecorder implements ITransitionMonitor {
@Override @Override
public Path save(String testTag) { public Path save(String testTag) {
try { try {
return Files.move(DEFAULT_OUTPUT_PATH, getPath(testTag), Path targetPath = Files.move(DEFAULT_OUTPUT_PATH, getPath(testTag),
REPLACE_EXISTING); REPLACE_EXISTING);
Log.i(TAG, "Video saved to " + targetPath.toString());
return targetPath;
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -20,7 +20,7 @@ import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import android.os.RemoteException; import android.os.RemoteException;
import com.android.internal.annotations.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
@@ -34,9 +34,15 @@ public abstract class TraceMonitor implements ITransitionMonitor {
public static final String TAG = "FLICKER"; public static final String TAG = "FLICKER";
private static final String TRACE_DIR = "/data/misc/wmtrace/"; 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 * 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) { public Path save(String testTag) {
OUTPUT_DIR.toFile().mkdirs(); OUTPUT_DIR.toFile().mkdirs();
Path traceFileCopy = getOutputTraceFilePath(testTag); Path traceFileCopy = getOutputTraceFilePath(testTag);
// Read the input stream fully.
String copyCommand = String.format(Locale.getDefault(), "mv %s%s %s", TRACE_DIR, String copyCommand = String.format(Locale.getDefault(), "mv %s%s %s", TRACE_DIR,
traceFileName, traceFileCopy.toString()); mTraceFileName, traceFileCopy.toString());
runShellCommand(copyCommand); runShellCommand(copyCommand);
return traceFileCopy; return traceFileCopy;
} }
@VisibleForTesting @VisibleForTesting
Path getOutputTraceFilePath(String testTag) { public Path getOutputTraceFilePath(String testTag) {
return OUTPUT_DIR.resolve(traceFileName + "_" + testTag); return mOutputDir.resolve(mTraceFileName + "_" + testTag);
} }
} }

View File

@@ -24,16 +24,20 @@ import android.view.WindowManagerGlobal;
* Captures WindowManager trace from WindowManager. * Captures WindowManager trace from WindowManager.
*/ */
public class WindowManagerTraceMonitor extends TraceMonitor { public class WindowManagerTraceMonitor extends TraceMonitor {
private IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); private IWindowManager mWm = WindowManagerGlobal.getWindowManagerService();
public WindowManagerTraceMonitor() { public WindowManagerTraceMonitor() {
traceFileName = "wm_trace.pb"; this(OUTPUT_DIR.toString());
}
public WindowManagerTraceMonitor(String outputDir) {
super(outputDir, "wm_trace.pb");
} }
@Override @Override
public void start() { public void start() {
try { try {
wm.startWindowTrace(); mWm.startWindowTrace();
} catch (RemoteException e) { } catch (RemoteException e) {
throw new RuntimeException("Could not start trace", e); throw new RuntimeException("Could not start trace", e);
} }
@@ -42,7 +46,7 @@ public class WindowManagerTraceMonitor extends TraceMonitor {
@Override @Override
public void stop() { public void stop() {
try { try {
wm.stopWindowTrace(); mWm.stopWindowTrace();
} catch (RemoteException e) { } catch (RemoteException e) {
throw new RuntimeException("Could not stop trace", e); throw new RuntimeException("Could not stop trace", e);
} }
@@ -50,6 +54,6 @@ public class WindowManagerTraceMonitor extends TraceMonitor {
@Override @Override
public boolean isEnabled() throws RemoteException{ public boolean isEnabled() throws RemoteException{
return wm.isWindowTraceEnabled(); return mWm.isWindowTraceEnabled();
} }
} }

View File

@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.monitor; 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; import androidx.test.InstrumentationRegistry;

View File

@@ -19,12 +19,12 @@ package com.android.server.wm.flicker;
import static android.os.SystemClock.sleep; import static android.os.SystemClock.sleep;
import static android.view.Surface.rotationToString; import static android.view.Surface.rotationToString;
import static com.android.server.wm.flicker.AutomationUtils.clearRecents; import static com.android.server.wm.flicker.helpers.AutomationUtils.clearRecents;
import static com.android.server.wm.flicker.AutomationUtils.closePipWindow; import static com.android.server.wm.flicker.helpers.AutomationUtils.closePipWindow;
import static com.android.server.wm.flicker.AutomationUtils.exitSplitScreen; import static com.android.server.wm.flicker.helpers.AutomationUtils.exitSplitScreen;
import static com.android.server.wm.flicker.AutomationUtils.expandPipWindow; import static com.android.server.wm.flicker.helpers.AutomationUtils.expandPipWindow;
import static com.android.server.wm.flicker.AutomationUtils.launchSplitScreen; import static com.android.server.wm.flicker.helpers.AutomationUtils.launchSplitScreen;
import static com.android.server.wm.flicker.AutomationUtils.stopPackage; import static com.android.server.wm.flicker.helpers.AutomationUtils.stopPackage;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -40,6 +40,7 @@ import android.view.Surface;
import androidx.test.InstrumentationRegistry; import androidx.test.InstrumentationRegistry;
import com.android.server.wm.flicker.TransitionRunner.TransitionBuilder; 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. * Collection of common transitions which can be used to test different apps or scenarios.

View File

@@ -16,7 +16,7 @@
package com.android.server.wm.flicker; 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; import static com.google.common.truth.Truth.assertWithMessage;