Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b53b30cd authored by Winson Chung's avatar Winson Chung
Browse files

Update trace buffer to support SysUI & Launcher

- Move TraceBuffer to sysui-accessible package, and make it generic
  to support both lite and nano proto callers
- Expose shell command to start/stop sysui tracing

Bug: 144854916
Test: atest TraceBufferTest

Change-Id: Id117024d943f148a91631fd9fcae1fd70fca8ab5
parent 4cfbdb2e
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -210,4 +210,14 @@ oneway interface IStatusBar
     * Cancels toast with token {@code token} in {@code packageName}.
     */
    void hideToast(String packageName, IBinder token);

    /**
     * Notifies SystemUI to start tracing.
     */
    void startTracing();

    /**
     * Notifies SystemUI to stop tracing.
     */
    void stopTracing();
}
+15 −0
Original line number Diff line number Diff line
@@ -123,4 +123,19 @@ interface IStatusBarService
     * Dismiss the warning that the device is about to go to sleep due to user inactivity.
     */
    void dismissInattentiveSleepWarning(boolean animated);

    /**
     * Notifies SystemUI to start tracing.
     */
    void startTracing();

    /**
     * Notifies SystemUI to stop tracing.
     */
    void stopTracing();

    /**
     * Returns whether SystemUI tracing is enabled.
     */
    boolean isTracing();
}
+94 −28
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@
 * limitations under the License.
 */

package com.android.server.utils;
package com.android.internal.util;

import android.util.proto.ProtoOutputStream;

@@ -27,19 +27,87 @@ import java.io.OutputStream;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Queue;
import java.util.function.Consumer;

/**
 * Buffer used for tracing and logging.
 *
 * @param <P> The class type of the proto provider
 * @param <S> The proto class type of the encapsulating proto
 * @param <T> The proto class type of the individual entry protos in the buffer
 *
 * {@hide}
 */
public class TraceBuffer {
public class TraceBuffer<P, S extends P, T extends P> {
    private final Object mBufferLock = new Object();

    private final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>();
    private final ProtoProvider<P, S, T> mProtoProvider;
    private final Queue<T> mBuffer = new ArrayDeque<>();
    private final Consumer mProtoDequeuedCallback;
    private int mBufferUsedSize;
    private int mBufferCapacity;

    /**
     * An interface to get protos from different sources (ie. fw-proto/proto-lite/nano-proto) for
     * the trace buffer.
     *
     * @param <P> The class type of the proto provider
     * @param <S> The proto class type of the encapsulating proto
     * @param <T> The proto class type of the individual protos in the buffer
     */
    public interface ProtoProvider<P, S extends P, T extends P> {
        /**
         * @return The size of the given proto.
         */
        int getItemSize(P proto);

        /**
         * @return The bytes of the given proto.
         */
        byte[] getBytes(P proto);

        /**
         * Writes the given encapsulating proto and buffer of protos to the given output
         * stream.
         */
        void write(S encapsulatingProto, Queue<T> buffer, OutputStream os) throws IOException;
    }

    /**
     * An implementation of the ProtoProvider that uses only the framework ProtoOutputStream.
     */
    private static class ProtoOutputStreamProvider implements
            ProtoProvider<ProtoOutputStream, ProtoOutputStream, ProtoOutputStream> {
        @Override
        public int getItemSize(ProtoOutputStream proto) {
            return proto.getRawSize();
        }

        @Override
        public byte[] getBytes(ProtoOutputStream proto) {
            return proto.getBytes();
        }

        @Override
        public void write(ProtoOutputStream encapsulatingProto, Queue<ProtoOutputStream> buffer,
                OutputStream os) throws IOException {
            os.write(encapsulatingProto.getBytes());
            for (ProtoOutputStream protoOutputStream : buffer) {
                byte[] protoBytes = protoOutputStream.getBytes();
                os.write(protoBytes);
            }
        }
    }

    public TraceBuffer(int bufferCapacity) {
        this(bufferCapacity, new ProtoOutputStreamProvider(), null);
    }

    public TraceBuffer(int bufferCapacity, ProtoProvider protoProvider,
            Consumer<T> protoDequeuedCallback) {
        mBufferCapacity = bufferCapacity;
        mProtoProvider = protoProvider;
        mProtoDequeuedCallback = protoDequeuedCallback;
        resetBuffer();
    }

@@ -65,8 +133,8 @@ public class TraceBuffer {
     * @throws IllegalStateException if the element cannot be added because it is larger
     *                               than the buffer size.
     */
    public void add(ProtoOutputStream proto) {
        int protoLength = proto.getRawSize();
    public void add(T proto) {
        int protoLength = mProtoProvider.getItemSize(proto);
        if (protoLength > mBufferCapacity) {
            throw new IllegalStateException("Trace object too large for the buffer. Buffer size:"
                    + mBufferCapacity + " Object size: " + protoLength);
@@ -79,26 +147,22 @@ public class TraceBuffer {
        }
    }

    boolean contains(byte[] other) {
    @VisibleForTesting
    public boolean contains(byte[] other) {
        return mBuffer.stream()
                .anyMatch(p -> Arrays.equals(p.getBytes(), other));
                .anyMatch(p -> Arrays.equals(mProtoProvider.getBytes(p), other));
    }

    /**
     * Writes the trace buffer to disk inside the encapsulatingProto..
     * Writes the trace buffer to disk inside the encapsulatingProto.
     */
    public void writeTraceToFile(File traceFile, ProtoOutputStream encapsulatingProto)
    public void writeTraceToFile(File traceFile, S encapsulatingProto)
            throws IOException {
        synchronized (mBufferLock) {
            traceFile.delete();
            try (OutputStream os = new FileOutputStream(traceFile)) {
                traceFile.setReadable(true /* readable */, false /* ownerOnly */);
                os.write(encapsulatingProto.getBytes());
                for (ProtoOutputStream protoOutputStream : mBuffer) {
                    encapsulatingProto = protoOutputStream;
                    byte[] protoBytes = encapsulatingProto.getBytes();
                    os.write(protoBytes);
                }
                mProtoProvider.write(encapsulatingProto, mBuffer, os);
                os.flush();
            }
        }
@@ -115,12 +179,16 @@ public class TraceBuffer {

        while (availableSpace < protoLength) {

            ProtoOutputStream item = mBuffer.poll();
            P item = mBuffer.poll();
            if (item == null) {
                throw new IllegalStateException("No element to discard from buffer");
            }
            mBufferUsedSize -= item.getRawSize();
            mBufferUsedSize -= mProtoProvider.getItemSize(item);
            availableSpace = getAvailableSpace();

            if (mProtoDequeuedCallback != null) {
                mProtoDequeuedCallback.accept(item);
            }
        }
    }

@@ -129,13 +197,18 @@ public class TraceBuffer {
     */
    public void resetBuffer() {
        synchronized (mBufferLock) {
            if (mProtoDequeuedCallback != null) {
                for (T item : mBuffer) {
                    mProtoDequeuedCallback.accept(item);
                }
            }
            mBuffer.clear();
            mBufferUsedSize = 0;
        }
    }

    @VisibleForTesting
    int getBufferSize() {
    public int getBufferSize() {
        return mBufferUsedSize;
    }

@@ -144,16 +217,9 @@ public class TraceBuffer {
     */
    public String getStatus() {
        synchronized (mBufferLock) {
            return "Buffer size: "
                    + mBufferCapacity
                    + " bytes"
                    + "\n"
                    + "Buffer usage: "
                    + mBufferUsedSize
                    + " bytes"
                    + "\n"
                    + "Elements in the buffer: "
                    + mBuffer.size();
            return "Buffer size: " + mBufferCapacity + " bytes" + "\n"
                    + "Buffer usage: " + mBufferUsedSize + " bytes" + "\n"
                    + "Elements in the buffer: " + mBuffer.size();
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.protolog.common.IProtoLogGroup;
import com.android.server.protolog.common.LogDataType;
import com.android.server.utils.TraceBuffer;
import com.android.internal.util.TraceBuffer;
import com.android.server.wm.ProtoLogGroup;

import java.io.File;
+28 −4
Original line number Diff line number Diff line
@@ -79,7 +79,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D

    private final Context mContext;

    private final WindowManagerService mWindowManager;
    private Handler mHandler = new Handler();
    private NotificationDelegate mNotificationDelegate;
    private volatile IStatusBar mBar;
@@ -93,6 +92,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
    private final Object mLock = new Object();
    private final DeathRecipient mDeathRecipient = new DeathRecipient();
    private int mCurrentUserId;
    private boolean mTracingEnabled;

    private SparseArray<UiState> mDisplayUiState = new SparseArray<>();

@@ -176,11 +176,10 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
    }

    /**
     * Construct the service, add the status bar view to the window manager
     * Construct the service
     */
    public StatusBarManagerService(Context context, WindowManagerService windowManager) {
    public StatusBarManagerService(Context context) {
        mContext = context;
        mWindowManager = windowManager;

        LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
        LocalServices.addService(GlobalActionsProvider.class, mGlobalActionsProvider);
@@ -720,6 +719,31 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
        }
    }

    @Override
    public void startTracing() {
        if (mBar != null) {
            try {
                mBar.startTracing();
                mTracingEnabled = true;
            } catch (RemoteException ex) {}
        }
    }

    @Override
    public void stopTracing() {
        if (mBar != null) {
            try {
                mTracingEnabled = false;
                mBar.stopTracing();
            } catch (RemoteException ex) {}
        }
    }

    @Override
    public boolean isTracing() {
        return mTracingEnabled;
    }

    // TODO(b/117478341): make it aware of multi-display if needed.
    @Override
    public void disable(int what, IBinder token, String pkg) {
Loading