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

Commit 99dbbf93 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Only have one way of dumping print manager state"

parents dd7edac5 9a534c01
Loading
Loading
Loading
Loading
+265 −0
Original line number Diff line number Diff line
/*
 * 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.internal.print;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArrayMap;
import android.util.proto.ProtoOutputStream;

import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedList;

/**
 * Dump either to a proto or a print writer using the same interface.
 *
 * <p>This mirrors the interface of {@link ProtoOutputStream}.
 */
public class DualDumpOutputStream {
    // When writing to a proto, the proto
    private final @Nullable ProtoOutputStream mProtoStream;

    // When printing in clear text, the writer
    private final @Nullable IndentingPrintWriter mIpw;
    // Temporary storage of data when printing to mIpw
    private final LinkedList<DumpObject> mDumpObjects = new LinkedList<>();

    private static abstract class DumpAble {
        final String name;

        private DumpAble(String name) {
            this.name = name;
        }

        abstract void print(IndentingPrintWriter ipw, boolean printName);
    }

    private static class DumpObject extends DumpAble {
        private final LinkedHashMap<String, ArrayList<DumpAble>> mSubObjects = new LinkedHashMap<>();

        private DumpObject(String name) {
            super(name);
        }

        @Override
        void print(IndentingPrintWriter ipw, boolean printName) {
            if (printName) {
                ipw.println(name + "={");
            } else {
                ipw.println("{");
            }
            ipw.increaseIndent();

            for (ArrayList<DumpAble> subObject: mSubObjects.values()) {
                int numDumpables = subObject.size();

                if (numDumpables == 1) {
                    subObject.get(0).print(ipw, true);
                } else {
                    ipw.println(subObject.get(0).name + "=[");
                    ipw.increaseIndent();

                    for (int i = 0; i < numDumpables; i++) {
                        subObject.get(i).print(ipw, false);
                    }

                    ipw.decreaseIndent();
                    ipw.println("]");
                }
            }

            ipw.decreaseIndent();
            ipw.println("}");
        }

        /**
         * Add new field / subobject to this object.
         *
         * <p>If a name is added twice, they will be printed as a array
         *
         * @param fieldName name of the field added
         * @param d The dumpable to add
         */
        public void add(String fieldName, DumpAble d) {
            ArrayList<DumpAble> l = mSubObjects.get(fieldName);

            if (l == null) {
                l = new ArrayList<>(1);
                mSubObjects.put(fieldName, l);
            }

            l.add(d);
        }
    }

    private static class DumpField extends DumpAble {
        private final String mValue;

        private DumpField(String name, String value) {
            super(name);
            this.mValue = value;
        }

        @Override
        void print(IndentingPrintWriter ipw, boolean printName) {
            if (printName) {
                ipw.println(name + "=" + mValue);
            } else {
                ipw.println(mValue);
            }
        }
    }


    /**
     * Create a new DualDumpOutputStream. Only one output should be set.
     *
     * @param proto If dumping to proto the {@link ProtoOutputStream}
     * @param ipw If dumping to a print writer, the {@link IndentingPrintWriter}
     */
    public DualDumpOutputStream(@Nullable ProtoOutputStream proto,
            @Nullable IndentingPrintWriter ipw) {
        Preconditions.checkArgument((proto == null) != (ipw == null));

        mProtoStream = proto;
        mIpw = ipw;

        if (!isProto()) {
            // Add root object
            mDumpObjects.add(new DumpObject(null));
        }
    }

    public void write(@NonNull String fieldName, long fieldId, double val) {
        if (mProtoStream != null) {
            mProtoStream.write(fieldId, val);
        } else {
            mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
        }
    }

    public void write(@NonNull String fieldName, long fieldId, boolean val) {
        if (mProtoStream != null) {
            mProtoStream.write(fieldId, val);
        } else {
            mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
        }
    }

    public void write(@NonNull String fieldName, long fieldId, int val) {
        if (mProtoStream != null) {
            mProtoStream.write(fieldId, val);
        } else {
            mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
        }
    }

    public void write(@NonNull String fieldName, long fieldId, float val) {
        if (mProtoStream != null) {
            mProtoStream.write(fieldId, val);
        } else {
            mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
        }
    }

    public void write(@NonNull String fieldName, long fieldId, byte[] val) {
        if (mProtoStream != null) {
            mProtoStream.write(fieldId, val);
        } else {
            mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, Arrays.toString(val)));
        }
    }

    public void write(@NonNull String fieldName, long fieldId, long val) {
        if (mProtoStream != null) {
            mProtoStream.write(fieldId, val);
        } else {
            mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
        }
    }

    public void write(@NonNull String fieldName, long fieldId, @Nullable String val) {
        if (mProtoStream != null) {
            mProtoStream.write(fieldId, val);
        } else {
            mDumpObjects.getLast().add(fieldName, new DumpField(fieldName, String.valueOf(val)));
        }
    }

    public long start(@NonNull String fieldName, long fieldId) {
        if (mProtoStream != null) {
            return mProtoStream.start(fieldId);
        } else {
            DumpObject d = new DumpObject(fieldName);
            mDumpObjects.getLast().add(fieldName, d);
            mDumpObjects.addLast(d);
            return 0;
        }
    }

    public void end(long token) {
        if (mProtoStream != null) {
            mProtoStream.end(token);
        } else {
            mDumpObjects.removeLast();
        }
    }

    public void flush() {
        if (mProtoStream != null) {
            mProtoStream.flush();
        } else {
            if (mDumpObjects.size() == 1) {
                mDumpObjects.getFirst().print(mIpw, false);

                // Reset root object
                mDumpObjects.clear();
                mDumpObjects.add(new DumpObject(null));
            }

            mIpw.flush();
        }
    }

    /**
     * Add a dump from a different service into this dump.
     *
     * <p>Only for clear text dump. For proto dump use {@link #write(String, long, byte[])}.
     *
     * @param fieldName The name of the field
     * @param nestedState The state of the dump
     */
    public void writeNested(@NonNull String fieldName, byte[] nestedState) {
        Preconditions.checkNotNull(mIpw);

        mDumpObjects.getLast().add(fieldName,
                new DumpField(fieldName, (new String(nestedState, StandardCharsets.UTF_8)).trim()));
    }

    /**
     * @return {@code true} iff we are dumping to a proto
     */
    public boolean isProto() {
        return mProtoStream != null;
    }
}
+108 −87

File changed.

Preview size limit exceeded, changes collapsed.

+16 −40
Original line number Diff line number Diff line
@@ -59,7 +59,9 @@ import android.util.proto.ProtoOutputStream;

import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.HandlerCaller;
import com.android.internal.print.DualDumpOutputStream;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.printspooler.R;
import com.android.printspooler.util.ApprovedPrintServices;
@@ -159,43 +161,10 @@ public final class PrintSpoolerService extends Service {
        return new PrintSpooler();
    }

    private void dumpLocked(PrintWriter pw, String[] args) {
        String prefix = (args.length > 0) ? args[0] : "";
        String tab = "  ";

        pw.append(prefix).append("print jobs:").println();
        final int printJobCount = mPrintJobs.size();
        for (int i = 0; i < printJobCount; i++) {
            PrintJobInfo printJob = mPrintJobs.get(i);
            pw.append(prefix).append(tab).append(printJob.toString());
            pw.println();
        }

        pw.append(prefix).append("print job files:").println();
        File[] files = getFilesDir().listFiles();
        if (files != null) {
            final int fileCount = files.length;
            for (int i = 0; i < fileCount; i++) {
                File file = files[i];
                if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
                    pw.append(prefix).append(tab).append(file.getName()).println();
                }
            }
        }

        pw.append(prefix).append("approved print services:").println();
        Set<String> approvedPrintServices = (new ApprovedPrintServices(this)).getApprovedServices();
        if (approvedPrintServices != null) {
            for (String approvedService : approvedPrintServices) {
                pw.append(prefix).append(tab).append(approvedService).println();
            }
        }
    }

    private void dumpLocked(@NonNull ProtoOutputStream proto) {
    private void dumpLocked(@NonNull DualDumpOutputStream proto) {
        int numPrintJobs = mPrintJobs.size();
        for (int i = 0; i < numPrintJobs; i++) {
            writePrintJobInfo(this, proto, PrintSpoolerInternalStateProto.PRINT_JOBS,
            writePrintJobInfo(this, proto, "print_jobs", PrintSpoolerInternalStateProto.PRINT_JOBS,
                    mPrintJobs.get(i));
        }

@@ -204,7 +173,8 @@ public final class PrintSpoolerService extends Service {
            for (int i = 0; i < files.length; i++) {
                File file = files[i];
                if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
                    proto.write(PrintSpoolerInternalStateProto.PRINT_JOB_FILES, file.getName());
                    proto.write("print_job_files", PrintSpoolerInternalStateProto.PRINT_JOB_FILES,
                            file.getName());
                }
            }
        }
@@ -214,8 +184,8 @@ public final class PrintSpoolerService extends Service {
            for (String approvedService : approvedPrintServices) {
                ComponentName componentName = ComponentName.unflattenFromString(approvedService);
                if (componentName != null) {
                    writeComponentName(proto, PrintSpoolerInternalStateProto.APPROVED_SERVICES,
                            componentName);
                    writeComponentName(proto, "approved_services",
                            PrintSpoolerInternalStateProto.APPROVED_SERVICES, componentName);
                }
            }
        }
@@ -244,9 +214,15 @@ public final class PrintSpoolerService extends Service {
        try {
            synchronized (mLock) {
                if (dumpAsProto) {
                    dumpLocked(new ProtoOutputStream(fd));
                    dumpLocked(new DualDumpOutputStream(new ProtoOutputStream(fd), null));
                } else {
                    dumpLocked(pw, args);
                    try (FileOutputStream out = new FileOutputStream(fd)) {
                        try (PrintWriter w = new PrintWriter(out)) {
                            dumpLocked(new DualDumpOutputStream(null, new IndentingPrintWriter(w,
                                    "  ")));
                        }
                    } catch (IOException ignored) {
                    }
                }
            }
        } finally {
+10 −16
Original line number Diff line number Diff line
@@ -57,7 +57,9 @@ import android.util.proto.ProtoOutputStream;

import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.internal.print.DualDumpOutputStream;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.SystemService;

@@ -670,20 +672,24 @@ public final class PrintManagerService extends SystemService {
            final long identity = Binder.clearCallingIdentity();
            try {
                if (dumpAsProto) {
                    dump(new ProtoOutputStream(fd), userStatesToDump);
                    dump(new DualDumpOutputStream(new ProtoOutputStream(fd), null),
                            userStatesToDump);
                } else {
                    dump(fd, pw, userStatesToDump);
                    pw.println("PRINT MANAGER STATE (dumpsys print)");

                    dump(new DualDumpOutputStream(null, new IndentingPrintWriter(pw, "  ")),
                            userStatesToDump);
                }
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
        }

        private void dump(@NonNull ProtoOutputStream proto,
        private void dump(@NonNull DualDumpOutputStream proto,
                @NonNull ArrayList<UserState> userStatesToDump) {
            final int userStateCount = userStatesToDump.size();
            for (int i = 0; i < userStateCount; i++) {
                long token = proto.start(PrintServiceDumpProto.USER_STATES);
                long token = proto.start("user_states", PrintServiceDumpProto.USER_STATES);
                userStatesToDump.get(i).dump(proto);
                proto.end(token);
            }
@@ -691,18 +697,6 @@ public final class PrintManagerService extends SystemService {
            proto.flush();
        }

        private void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
                @NonNull ArrayList<UserState> userStatesToDump) {
            pw = Preconditions.checkNotNull(pw);

            pw.println("PRINT MANAGER STATE (dumpsys print)");
            final int userStateCount = userStatesToDump.size();
            for (int i = 0; i < userStateCount; i++) {
                userStatesToDump.get(i).dump(fd, pw, "");
                pw.println();
            }
        }

        private void registerContentObservers() {
            final Uri enabledPrintServicesUri = Settings.Secure.getUriFor(
                    Settings.Secure.DISABLED_PRINT_SERVICES);
+13 −33
Original line number Diff line number Diff line
@@ -47,11 +47,10 @@ import android.printservice.IPrintService;
import android.printservice.IPrintServiceClient;
import android.service.print.ActivePrintServiceProto;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.print.DualDumpOutputStream;

import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -532,49 +531,30 @@ final class RemotePrintService implements DeathRecipient {
        }
    }

    public void dump(@NonNull ProtoOutputStream proto) {
        writeComponentName(proto, ActivePrintServiceProto.COMPONENT_NAME, mComponentName);
    public void dump(@NonNull DualDumpOutputStream proto) {
        writeComponentName(proto, "component_name", ActivePrintServiceProto.COMPONENT_NAME,
                mComponentName);

        proto.write(ActivePrintServiceProto.IS_DESTROYED, mDestroyed);
        proto.write(ActivePrintServiceProto.IS_BOUND, isBound());
        proto.write(ActivePrintServiceProto.HAS_DISCOVERY_SESSION, mHasPrinterDiscoverySession);
        proto.write(ActivePrintServiceProto.HAS_ACTIVE_PRINT_JOBS, mHasActivePrintJobs);
        proto.write(ActivePrintServiceProto.IS_DISCOVERING_PRINTERS,
        proto.write("is_destroyed", ActivePrintServiceProto.IS_DESTROYED, mDestroyed);
        proto.write("is_bound", ActivePrintServiceProto.IS_BOUND, isBound());
        proto.write("has_discovery_session", ActivePrintServiceProto.HAS_DISCOVERY_SESSION,
                mHasPrinterDiscoverySession);
        proto.write("has_active_print_jobs", ActivePrintServiceProto.HAS_ACTIVE_PRINT_JOBS,
                mHasActivePrintJobs);
        proto.write("is_discovering_printers", ActivePrintServiceProto.IS_DISCOVERING_PRINTERS,
                mDiscoveryPriorityList != null);

        synchronized (mLock) {
            if (mTrackedPrinterList != null) {
                int numTrackedPrinters = mTrackedPrinterList.size();
                for (int i = 0; i < numTrackedPrinters; i++) {
                    writePrinterId(proto, ActivePrintServiceProto.TRACKED_PRINTERS,
                            mTrackedPrinterList.get(i));
                    writePrinterId(proto, "tracked_printers",
                            ActivePrintServiceProto.TRACKED_PRINTERS, mTrackedPrinterList.get(i));
                }
            }
        }
    }

    public void dump(PrintWriter pw, String prefix) {
        String tab = "  ";
        pw.append(prefix).append("service:").println();
        pw.append(prefix).append(tab).append("componentName=")
                .append(mComponentName.flattenToString()).println();
        pw.append(prefix).append(tab).append("destroyed=")
                .append(String.valueOf(mDestroyed)).println();
        pw.append(prefix).append(tab).append("bound=")
                .append(String.valueOf(isBound())).println();
        pw.append(prefix).append(tab).append("hasDicoverySession=")
                .append(String.valueOf(mHasPrinterDiscoverySession)).println();
        pw.append(prefix).append(tab).append("hasActivePrintJobs=")
                .append(String.valueOf(mHasActivePrintJobs)).println();
        pw.append(prefix).append(tab).append("isDiscoveringPrinters=")
                .append(String.valueOf(mDiscoveryPriorityList != null)).println();

        synchronized (mLock) {
            pw.append(prefix).append(tab).append("trackedPrinters=").append(
                    (mTrackedPrinterList != null) ? mTrackedPrinterList.toString() : "null");
        }
    }

    private boolean isBound() {
        return mPrintService != null;
    }
Loading