/*
|
* Copyright (C) 2019 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;
|
|
import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
|
import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
|
import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
|
|
import android.util.proto.ProtoOutputStream;
|
|
import com.android.internal.annotations.VisibleForTesting;
|
|
import java.io.File;
|
import java.io.FileOutputStream;
|
import java.io.IOException;
|
import java.io.OutputStream;
|
import java.util.ArrayDeque;
|
import java.util.Arrays;
|
import java.util.Queue;
|
|
/**
|
* Buffer used for window tracing.
|
*/
|
class WindowTraceBuffer {
|
private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
|
|
private final Object mBufferLock = new Object();
|
|
private final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>();
|
private int mBufferUsedSize;
|
private int mBufferCapacity;
|
|
WindowTraceBuffer(int bufferCapacity) {
|
mBufferCapacity = bufferCapacity;
|
resetBuffer();
|
}
|
|
int getAvailableSpace() {
|
return mBufferCapacity - mBufferUsedSize;
|
}
|
|
int size() {
|
return mBuffer.size();
|
}
|
|
void setCapacity(int capacity) {
|
mBufferCapacity = capacity;
|
}
|
|
/**
|
* Inserts the specified element into this buffer.
|
*
|
* @param proto the element to add
|
* @throws IllegalStateException if the element cannot be added because it is larger
|
* than the buffer size.
|
*/
|
void add(ProtoOutputStream proto) {
|
int protoLength = proto.getRawSize();
|
if (protoLength > mBufferCapacity) {
|
throw new IllegalStateException("Trace object too large for the buffer. Buffer size:"
|
+ mBufferCapacity + " Object size: " + protoLength);
|
}
|
synchronized (mBufferLock) {
|
discardOldest(protoLength);
|
mBuffer.add(proto);
|
mBufferUsedSize += protoLength;
|
mBufferLock.notify();
|
}
|
}
|
|
boolean contains(byte[] other) {
|
return mBuffer.stream()
|
.anyMatch(p -> Arrays.equals(p.getBytes(), other));
|
}
|
|
/**
|
* Writes the trace buffer to disk.
|
*/
|
void writeTraceToFile(File traceFile) throws IOException {
|
synchronized (mBufferLock) {
|
traceFile.delete();
|
try (OutputStream os = new FileOutputStream(traceFile)) {
|
traceFile.setReadable(true /* readable */, false /* ownerOnly */);
|
ProtoOutputStream proto = new ProtoOutputStream();
|
proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
|
os.write(proto.getBytes());
|
for (ProtoOutputStream protoOutputStream : mBuffer) {
|
proto = protoOutputStream;
|
byte[] protoBytes = proto.getBytes();
|
os.write(protoBytes);
|
}
|
os.flush();
|
}
|
}
|
}
|
|
/**
|
* Checks if the element can be added to the buffer. The element is already certain to be
|
* smaller than the overall buffer size.
|
*
|
* @param protoLength byte array representation of the Proto object to add
|
*/
|
private void discardOldest(int protoLength) {
|
long availableSpace = getAvailableSpace();
|
|
while (availableSpace < protoLength) {
|
|
ProtoOutputStream item = mBuffer.poll();
|
if (item == null) {
|
throw new IllegalStateException("No element to discard from buffer");
|
}
|
mBufferUsedSize -= item.getRawSize();
|
availableSpace = getAvailableSpace();
|
}
|
}
|
|
/**
|
* Removes all elements form the buffer
|
*/
|
void resetBuffer() {
|
synchronized (mBufferLock) {
|
mBuffer.clear();
|
mBufferUsedSize = 0;
|
}
|
}
|
|
@VisibleForTesting
|
int getBufferSize() {
|
return mBufferUsedSize;
|
}
|
|
String getStatus() {
|
synchronized (mBufferLock) {
|
return "Buffer size: "
|
+ mBufferCapacity
|
+ " bytes"
|
+ "\n"
|
+ "Buffer usage: "
|
+ mBufferUsedSize
|
+ " bytes"
|
+ "\n"
|
+ "Elements in the buffer: "
|
+ mBuffer.size();
|
}
|
}
|
}
|