PriorityDump.java revision e78b01ad2d7779ad3a6acfe5acd0068e4840665c
1/**
2 * Copyright (c) 2016, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.utils;
18
19import android.annotation.IntDef;
20
21import java.io.FileDescriptor;
22import java.io.PrintWriter;
23import java.lang.annotation.Retention;
24import java.lang.annotation.RetentionPolicy;
25import java.util.ArrayList;
26import java.util.Arrays;
27import java.util.Iterator;
28
29/**
30 * Helper for {@link android.os.Binder#dump(java.io.FileDescriptor, String[])} that supports the
31 * {@link #PRIORITY_ARG} and {@link #PROTO_ARG} arguments.
32 * <p>
33 * Typical usage:
34 *
35 * <pre><code>
36public class SpringfieldNuclearPowerPlant extends Binder {
37
38 private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() {
39
40     @Override
41     public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
42       if (asProto) {
43         ProtoOutputStream proto = new ProtoOutputStream(fd);
44         proto.write(SpringfieldProto.DONUTS, 1);
45         proto.flush();
46       } else {
47         pw.println("Donuts in the box: 1");
48       }
49     }
50
51     @Override
52     public void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args) {
53        if (asProto) {
54          ProtoOutputStream proto = new ProtoOutputStream(fd);
55          proto.write(SpringfieldProto.REACTOR_STATUS, DANGER_MELTDOWN_IMMINENT);
56          proto.flush();
57        } else {
58          pw.println("Nuclear reactor status: DANGER - MELTDOWN IMMINENT");
59        }
60     }
61  };
62
63  @Override
64  protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
65      PriorityDump.dump(mPriorityDumper, fd, pw, args);
66  }
67}
68
69 * </code></pre>
70 *
71 * <strong>Disclaimer</strong>: a real-life service should prioritize core status over donuts :-)
72 *
73 * <p>Then to invoke it:
74 *
75 * <pre><code>
76 *
77    $ adb shell dumpsys snpp
78    Donuts in the box: 1
79    Nuclear reactor status: DANGER - MELTDOWN IMMINENT
80
81    $ adb shell dumpsys snpp --dump-priority CRITICAL
82    Donuts in the box: 1
83
84    $ adb shell dumpsys snpp --dump-priority NORMAL
85    Nuclear reactor status: DANGER - MELTDOWN IMMINENT
86
87    $ adb shell dumpsys snpp --dump-priority CRITICAL --proto
88    //binary output
89
90 * </code></pre>
91 *
92 *
93 *
94 * <p>To run the unit tests:
95 * <pre><code>
96 *
97 mmm -j32 frameworks/base/services/tests/servicestests/ && \
98 adb install -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && \
99 adb shell am instrument -e class "com.android.server.utils.PriorityDumpTest" \
100 -w "com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner"
101
102 * </code></pre>
103 *
104 *
105 * @hide
106 */
107public final class PriorityDump {
108
109    public static final String PRIORITY_ARG = "--dump-priority";
110    public static final String PROTO_ARG = "--proto";
111
112    private PriorityDump() {
113        throw new UnsupportedOperationException();
114    }
115
116    /** Enum to switch through supported priority types */
117    @Retention(RetentionPolicy.SOURCE)
118    @IntDef({PRIORITY_TYPE_INVALID, PRIORITY_TYPE_CRITICAL, PRIORITY_TYPE_HIGH,
119            PRIORITY_TYPE_NORMAL})
120    private @interface PriorityType { }
121    private static final int PRIORITY_TYPE_INVALID = 0;
122    private static final int PRIORITY_TYPE_CRITICAL = 1;
123    private static final int PRIORITY_TYPE_HIGH = 2;
124    private static final int PRIORITY_TYPE_NORMAL = 3;
125
126    /**
127     * Parses {@code args} matching {@code --dump-priority} and/or {@code --proto}. The matching
128     * arguments are stripped.
129     * <p>
130     * If priority args are passed as an argument, it will call the appropriate method and if proto
131     * args are passed then the {@code asProto} flag is set.
132     * <p>
133     * For example, if called as {@code --dump-priority HIGH arg1 arg2 arg3}, it will call
134     * <code>dumper.dumpHigh(fd, pw, {"arg1", "arg2", "arg3"}, false) </code>
135     * <p>
136     * If the {@code --dump-priority} is not set, it calls
137     * {@link PriorityDumper#dump(FileDescriptor, PrintWriter, String[], boolean)} passing the whole
138     * {@code args} instead.
139     */
140    public static void dump(PriorityDumper dumper, FileDescriptor fd, PrintWriter pw,
141            String[] args) {
142        boolean asProto = false;
143        @PriorityType int priority = PRIORITY_TYPE_INVALID;
144
145        if (args == null) {
146            dumper.dump(fd, pw, args, asProto);
147            return;
148        }
149
150        String[] strippedArgs = new String[args.length];
151        int strippedCount = 0;
152        for (int argIndex = 0; argIndex < args.length; argIndex++) {
153            if (args[argIndex].equals(PROTO_ARG)) {
154                asProto = true;
155            } else if (args[argIndex].equals(PRIORITY_ARG)) {
156                if (argIndex + 1 < args.length) {
157                    argIndex++;
158                    priority = getPriorityType(args[argIndex]);
159                }
160            } else {
161                strippedArgs[strippedCount++] = args[argIndex];
162            }
163        }
164
165        if (strippedCount < args.length) {
166            strippedArgs = Arrays.copyOf(strippedArgs, strippedCount);
167        }
168
169        switch (priority) {
170            case PRIORITY_TYPE_CRITICAL: {
171                dumper.dumpCritical(fd, pw, strippedArgs, asProto);
172                return;
173            }
174            case PRIORITY_TYPE_HIGH: {
175                dumper.dumpHigh(fd, pw, strippedArgs, asProto);
176                return;
177            }
178            case PRIORITY_TYPE_NORMAL: {
179                dumper.dumpNormal(fd, pw, strippedArgs, asProto);
180                return;
181            }
182            default: {
183                dumper.dump(fd, pw, strippedArgs, asProto);
184                return;
185            }
186        }
187    }
188
189    /**
190     * Converts priority argument type to enum.
191     */
192    private static @PriorityType int getPriorityType(String arg) {
193        switch (arg) {
194            case "CRITICAL": {
195                return PRIORITY_TYPE_CRITICAL;
196            }
197            case "HIGH": {
198                return PRIORITY_TYPE_HIGH;
199            }
200            case "NORMAL": {
201                return PRIORITY_TYPE_NORMAL;
202            }
203        }
204        return PRIORITY_TYPE_INVALID;
205    }
206
207    /**
208     * Helper for {@link android.os.Binder#dump(java.io.FileDescriptor, String[])} that supports the
209     * {@link #PRIORITY_ARG} and {@link #PROTO_ARG} arguments.
210     *
211     * @hide
212     */
213    public interface PriorityDumper {
214
215        /**
216         * Dumps only the critical section.
217         */
218        @SuppressWarnings("unused")
219        default void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args,
220                boolean asProto) {
221        }
222
223        /**
224         * Dumps only the high-priority section.
225         */
226        @SuppressWarnings("unused")
227        default void dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
228        }
229
230        /**
231         * Dumps only the normal section.
232         */
233        @SuppressWarnings("unused")
234        default void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
235        }
236
237        /**
238         * Dumps all sections.
239         * <p>
240         * This method is called when
241         * {@link PriorityDump#dump(PriorityDumper, FileDescriptor, PrintWriter, String[], boolean)}
242         * is called without priority arguments. By default, it calls the 3 {@code dumpTYPE}
243         * methods, so sub-classes just need to implement the priority types they support.
244         */
245        @SuppressWarnings("unused")
246        default void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
247            dumpCritical(fd, pw, args, asProto);
248            dumpHigh(fd, pw, args, asProto);
249            dumpNormal(fd, pw, args, asProto);
250        }
251    }
252}
253