blob: e3592eb8cd01db0b9529515ce697a7b52eea8a32 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm.flicker;
import android.annotation.Nullable;
import com.android.server.wm.flicker.Assertions.Result;
import com.android.server.wm.nano.AppWindowTokenProto;
import com.android.server.wm.nano.StackProto;
import com.android.server.wm.nano.TaskProto;
import com.android.server.wm.nano.WindowManagerTraceFileProto;
import com.android.server.wm.nano.WindowManagerTraceProto;
import com.android.server.wm.nano.WindowStateProto;
import com.android.server.wm.nano.WindowTokenProto;
import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* Contains a collection of parsed WindowManager trace entries and assertions to apply over
* a single entry.
*
* Each entry is parsed into a list of {@link WindowManagerTrace.Entry} objects.
*/
public class WindowManagerTrace {
private static final int DEFAULT_DISPLAY = 0;
private final List<Entry> mEntries;
@Nullable
final private Path mSource;
private WindowManagerTrace(List<Entry> entries, Path source) {
this.mEntries = entries;
this.mSource = source;
}
/**
* Parses {@code WindowManagerTraceFileProto} from {@code data} and uses the proto to
* generates a list of trace entries.
*
* @param data binary proto data
* @param source Path to source of data for additional debug information
*/
static WindowManagerTrace parseFrom(byte[] data, Path source) {
List<Entry> entries = new ArrayList<>();
WindowManagerTraceFileProto fileProto;
try {
fileProto = WindowManagerTraceFileProto.parseFrom(data);
} catch (InvalidProtocolBufferNanoException e) {
throw new RuntimeException(e);
}
for (WindowManagerTraceProto entryProto : fileProto.entry) {
entries.add(new Entry(entryProto));
}
return new WindowManagerTrace(entries, source);
}
static WindowManagerTrace parseFrom(byte[] data) {
return parseFrom(data, null);
}
public List<Entry> getEntries() {
return mEntries;
}
Entry getEntry(long timestamp) {
Optional<Entry> entry = mEntries.stream()
.filter(e -> e.getTimestamp() == timestamp)
.findFirst();
if (!entry.isPresent()) {
throw new RuntimeException("Entry does not exist for timestamp " + timestamp);
}
return entry.get();
}
Optional<Path> getSource() {
return Optional.ofNullable(mSource);
}
/**
* Represents a single WindowManager trace entry.
*/
static class Entry implements ITraceEntry {
private final WindowManagerTraceProto mProto;
Entry(WindowManagerTraceProto proto) {
mProto = proto;
}
private static Result isWindowVisible(String windowTitle,
WindowTokenProto[] windowTokenProtos) {
boolean titleFound = false;
for (WindowTokenProto windowToken : windowTokenProtos) {
for (WindowStateProto windowState : windowToken.windows) {
if (windowState.identifier.title.contains(windowTitle)) {
titleFound = true;
if (isVisible(windowState)) {
return new Result(true /* success */,
windowState.identifier.title + " is visible");
}
}
}
}
String reason;
if (!titleFound) {
reason = windowTitle + " cannot be found";
} else {
reason = windowTitle + " is invisible";
}
return new Result(false /* success */, reason);
}
private static boolean isVisible(WindowStateProto windowState) {
return windowState.windowContainer.visible;
}
@Override
public long getTimestamp() {
return mProto.elapsedRealtimeNanos;
}
/**
* Returns window title of the top most visible app window.
*/
private String getTopVisibleAppWindow() {
StackProto[] stacks = mProto.windowManagerService.rootWindowContainer
.displays[DEFAULT_DISPLAY].stacks;
for (StackProto stack : stacks) {
for (TaskProto task : stack.tasks) {
for (AppWindowTokenProto token : task.appWindowTokens) {
for (WindowStateProto windowState : token.windowToken.windows) {
if (windowState.windowContainer.visible) {
return task.appWindowTokens[0].name;
}
}
}
}
}
return "";
}
/**
* Checks if aboveAppWindow with {@code windowTitle} is visible.
*/
Result isAboveAppWindowVisible(String windowTitle) {
WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
.rootWindowContainer
.displays[DEFAULT_DISPLAY].aboveAppWindows;
Result result = isWindowVisible(windowTitle, windowTokenProtos);
return new Result(result.success, getTimestamp(), "showsAboveAppWindow", result.reason);
}
/**
* Checks if belowAppWindow with {@code windowTitle} is visible.
*/
Result isBelowAppWindowVisible(String windowTitle) {
WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
.rootWindowContainer
.displays[DEFAULT_DISPLAY].belowAppWindows;
Result result = isWindowVisible(windowTitle, windowTokenProtos);
return new Result(result.success, getTimestamp(), "isBelowAppWindowVisible",
result.reason);
}
/**
* Checks if imeWindow with {@code windowTitle} is visible.
*/
Result isImeWindowVisible(String windowTitle) {
WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
.rootWindowContainer
.displays[DEFAULT_DISPLAY].imeWindows;
Result result = isWindowVisible(windowTitle, windowTokenProtos);
return new Result(result.success, getTimestamp(), "isImeWindowVisible",
result.reason);
}
/**
* Checks if app window with {@code windowTitle} is on top.
*/
Result isVisibleAppWindowOnTop(String windowTitle) {
String topAppWindow = getTopVisibleAppWindow();
boolean success = topAppWindow.contains(windowTitle);
String reason = "wanted=" + windowTitle + " found=" + topAppWindow;
return new Result(success, getTimestamp(), "isAppWindowOnTop", reason);
}
/**
* Checks if app window with {@code windowTitle} is visible.
*/
Result isAppWindowVisible(String windowTitle) {
final String assertionName = "isAppWindowVisible";
boolean titleFound = false;
StackProto[] stacks = mProto.windowManagerService.rootWindowContainer
.displays[DEFAULT_DISPLAY].stacks;
for (StackProto stack : stacks) {
for (TaskProto task : stack.tasks) {
for (AppWindowTokenProto token : task.appWindowTokens) {
if (token.name.contains(windowTitle)) {
titleFound = true;
for (WindowStateProto windowState : token.windowToken.windows) {
if (windowState.windowContainer.visible) {
return new Result(true /* success */, getTimestamp(),
assertionName, "Window " + token.name +
"is visible");
}
}
}
}
}
}
String reason;
if (!titleFound) {
reason = "Window " + windowTitle + " cannot be found";
} else {
reason = "Window " + windowTitle + " is invisible";
}
return new Result(false /* success */, getTimestamp(), assertionName, reason);
}
}
}