/** * Copyright (c) 2016, 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.utils; import android.annotation.IntDef; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; /** * Helper for {@link android.os.Binder#dump(java.io.FileDescriptor, String[])} that supports the * {@link #PRIORITY_ARG} and {@link #PROTO_ARG} arguments. *
* Typical usage: * *
public class SpringfieldNuclearPowerPlant extends Binder {
private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() {
@Override
public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
if (asProto) {
ProtoOutputStream proto = new ProtoOutputStream(fd);
proto.write(SpringfieldProto.DONUTS, 1);
proto.flush();
} else {
pw.println("Donuts in the box: 1");
}
}
@Override
public void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args) {
if (asProto) {
ProtoOutputStream proto = new ProtoOutputStream(fd);
proto.write(SpringfieldProto.REACTOR_STATUS, DANGER_MELTDOWN_IMMINENT);
proto.flush();
} else {
pw.println("Nuclear reactor status: DANGER - MELTDOWN IMMINENT");
}
}
};
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
PriorityDump.dump(mPriorityDumper, fd, pw, args);
}
}
*
*
* Disclaimer: a real-life service should prioritize core status over donuts :-)
*
* Then to invoke it: * *
*
$ adb shell dumpsys snpp
Donuts in the box: 1
Nuclear reactor status: DANGER - MELTDOWN IMMINENT
$ adb shell dumpsys snpp --dump-priority CRITICAL
Donuts in the box: 1
$ adb shell dumpsys snpp --dump-priority NORMAL
Nuclear reactor status: DANGER - MELTDOWN IMMINENT
$ adb shell dumpsys snpp --dump-priority CRITICAL --proto
//binary output
*
*
*
*
* To run the unit tests: *
*
atest FrameworksServicesTests:PriorityDumpTest
*
*
*
* @hide
*/
public final class PriorityDump {
public static final String PRIORITY_ARG = "--dump-priority";
public static final String PROTO_ARG = "--proto";
public static final String PRIORITY_ARG_CRITICAL = "CRITICAL";
public static final String PRIORITY_ARG_HIGH = "HIGH";
public static final String PRIORITY_ARG_NORMAL = "NORMAL";
private PriorityDump() {
throw new UnsupportedOperationException();
}
/** Enum to switch through supported priority types */
@Retention(RetentionPolicy.SOURCE)
@IntDef({PRIORITY_TYPE_INVALID, PRIORITY_TYPE_CRITICAL, PRIORITY_TYPE_HIGH,
PRIORITY_TYPE_NORMAL})
private @interface PriorityType { }
private static final int PRIORITY_TYPE_INVALID = 0;
private static final int PRIORITY_TYPE_CRITICAL = 1;
private static final int PRIORITY_TYPE_HIGH = 2;
private static final int PRIORITY_TYPE_NORMAL = 3;
/**
* Parses {@code args} matching {@code --dump-priority} and/or {@code --proto}. The matching
* arguments are stripped.
* * If priority args are passed as an argument, it will call the appropriate method and if proto * args are passed then the {@code asProto} flag is set. *
* For example, if called as {@code --dump-priority HIGH arg1 arg2 arg3}, it will call
* dumper.dumpHigh(fd, pw, {"arg1", "arg2", "arg3"}, false)
*
* If the {@code --dump-priority} is not set, it calls * {@link PriorityDumper#dump(FileDescriptor, PrintWriter, String[], boolean)} passing the whole * {@code args} instead. */ public static void dump(PriorityDumper dumper, FileDescriptor fd, PrintWriter pw, String[] args) { boolean asProto = false; @PriorityType int priority = PRIORITY_TYPE_INVALID; if (args == null) { dumper.dump(fd, pw, args, asProto); return; } String[] strippedArgs = new String[args.length]; int strippedCount = 0; for (int argIndex = 0; argIndex < args.length; argIndex++) { if (args[argIndex].equals(PROTO_ARG)) { asProto = true; } else if (args[argIndex].equals(PRIORITY_ARG)) { if (argIndex + 1 < args.length) { argIndex++; priority = getPriorityType(args[argIndex]); } } else { strippedArgs[strippedCount++] = args[argIndex]; } } if (strippedCount < args.length) { strippedArgs = Arrays.copyOf(strippedArgs, strippedCount); } switch (priority) { case PRIORITY_TYPE_CRITICAL: { dumper.dumpCritical(fd, pw, strippedArgs, asProto); return; } case PRIORITY_TYPE_HIGH: { dumper.dumpHigh(fd, pw, strippedArgs, asProto); return; } case PRIORITY_TYPE_NORMAL: { dumper.dumpNormal(fd, pw, strippedArgs, asProto); return; } default: { dumper.dump(fd, pw, strippedArgs, asProto); return; } } } /** * Converts priority argument type to enum. */ private static @PriorityType int getPriorityType(String arg) { switch (arg) { case PRIORITY_ARG_CRITICAL: { return PRIORITY_TYPE_CRITICAL; } case PRIORITY_ARG_HIGH: { return PRIORITY_TYPE_HIGH; } case PRIORITY_ARG_NORMAL: { return PRIORITY_TYPE_NORMAL; } default: { return PRIORITY_TYPE_INVALID; } } } /** * Helper for {@link android.os.Binder#dump(java.io.FileDescriptor, String[])} that supports the * {@link #PRIORITY_ARG} and {@link #PROTO_ARG} arguments. * * @hide */ public interface PriorityDumper { /** * Dumps only the critical section. */ @SuppressWarnings("unused") default void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) { } /** * Dumps only the high-priority section. */ @SuppressWarnings("unused") default void dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) { } /** * Dumps only the normal section. */ @SuppressWarnings("unused") default void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) { } /** * Dumps all sections. *
* This method is called when * {@link PriorityDump#dump(PriorityDumper, FileDescriptor, PrintWriter, String[])} * is called without priority arguments. By default, it calls the 3 {@code dumpTYPE} * methods, so sub-classes just need to implement the priority types they support. */ @SuppressWarnings("unused") default void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) { dumpCritical(fd, pw, args, asProto); dumpHigh(fd, pw, args, asProto); dumpNormal(fd, pw, args, asProto); } } }