PriorityDump.java revision e3e4d251b9ed680e56e18684a2a24feb94b9cd21
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 atest FrameworksServicesTests:PriorityDumpTest
98 * </code></pre>
99 *
100 *
101 * @hide
102 */
103public final class PriorityDump {
104
105    public static final String PRIORITY_ARG = "--dump-priority";
106    public static final String PROTO_ARG = "--proto";
107    public static final String PRIORITY_ARG_CRITICAL = "CRITICAL";
108    public static final String PRIORITY_ARG_HIGH = "HIGH";
109    public static final String PRIORITY_ARG_NORMAL = "NORMAL";
110
111    private PriorityDump() {
112        throw new UnsupportedOperationException();
113    }
114
115    /** Enum to switch through supported priority types */
116    @Retention(RetentionPolicy.SOURCE)
117    @IntDef({PRIORITY_TYPE_INVALID, PRIORITY_TYPE_CRITICAL, PRIORITY_TYPE_HIGH,
118            PRIORITY_TYPE_NORMAL})
119    private @interface PriorityType { }
120    private static final int PRIORITY_TYPE_INVALID = 0;
121    private static final int PRIORITY_TYPE_CRITICAL = 1;
122    private static final int PRIORITY_TYPE_HIGH = 2;
123    private static final int PRIORITY_TYPE_NORMAL = 3;
124
125    /**
126     * Parses {@code args} matching {@code --dump-priority} and/or {@code --proto}. The matching
127     * arguments are stripped.
128     * <p>
129     * If priority args are passed as an argument, it will call the appropriate method and if proto
130     * args are passed then the {@code asProto} flag is set.
131     * <p>
132     * For example, if called as {@code --dump-priority HIGH arg1 arg2 arg3}, it will call
133     * <code>dumper.dumpHigh(fd, pw, {"arg1", "arg2", "arg3"}, false) </code>
134     * <p>
135     * If the {@code --dump-priority} is not set, it calls
136     * {@link PriorityDumper#dump(FileDescriptor, PrintWriter, String[], boolean)} passing the whole
137     * {@code args} instead.
138     */
139    public static void dump(PriorityDumper dumper, FileDescriptor fd, PrintWriter pw,
140            String[] args) {
141        boolean asProto = false;
142        @PriorityType int priority = PRIORITY_TYPE_INVALID;
143
144        if (args == null) {
145            dumper.dump(fd, pw, args, asProto);
146            return;
147        }
148
149        String[] strippedArgs = new String[args.length];
150        int strippedCount = 0;
151        for (int argIndex = 0; argIndex < args.length; argIndex++) {
152            if (args[argIndex].equals(PROTO_ARG)) {
153                asProto = true;
154            } else if (args[argIndex].equals(PRIORITY_ARG)) {
155                if (argIndex + 1 < args.length) {
156                    argIndex++;
157                    priority = getPriorityType(args[argIndex]);
158                }
159            } else {
160                strippedArgs[strippedCount++] = args[argIndex];
161            }
162        }
163
164        if (strippedCount < args.length) {
165            strippedArgs = Arrays.copyOf(strippedArgs, strippedCount);
166        }
167
168        switch (priority) {
169            case PRIORITY_TYPE_CRITICAL: {
170                dumper.dumpCritical(fd, pw, strippedArgs, asProto);
171                return;
172            }
173            case PRIORITY_TYPE_HIGH: {
174                dumper.dumpHigh(fd, pw, strippedArgs, asProto);
175                return;
176            }
177            case PRIORITY_TYPE_NORMAL: {
178                dumper.dumpNormal(fd, pw, strippedArgs, asProto);
179                return;
180            }
181            default: {
182                dumper.dump(fd, pw, strippedArgs, asProto);
183                return;
184            }
185        }
186    }
187
188    /**
189     * Converts priority argument type to enum.
190     */
191    private static @PriorityType int getPriorityType(String arg) {
192        switch (arg) {
193            case PRIORITY_ARG_CRITICAL: {
194                return PRIORITY_TYPE_CRITICAL;
195            }
196            case PRIORITY_ARG_HIGH: {
197                return PRIORITY_TYPE_HIGH;
198            }
199            case PRIORITY_ARG_NORMAL: {
200                return PRIORITY_TYPE_NORMAL;
201            }
202            default: {
203                return PRIORITY_TYPE_INVALID;
204            }
205        }
206    }
207
208    /**
209     * Helper for {@link android.os.Binder#dump(java.io.FileDescriptor, String[])} that supports the
210     * {@link #PRIORITY_ARG} and {@link #PROTO_ARG} arguments.
211     *
212     * @hide
213     */
214    public interface PriorityDumper {
215
216        /**
217         * Dumps only the critical section.
218         */
219        @SuppressWarnings("unused")
220        default void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args,
221                boolean asProto) {
222        }
223
224        /**
225         * Dumps only the high-priority section.
226         */
227        @SuppressWarnings("unused")
228        default void dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
229        }
230
231        /**
232         * Dumps only the normal section.
233         */
234        @SuppressWarnings("unused")
235        default void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
236        }
237
238        /**
239         * Dumps all sections.
240         * <p>
241         * This method is called when
242         * {@link PriorityDump#dump(PriorityDumper, FileDescriptor, PrintWriter, String[])}
243         * is called without priority arguments. By default, it calls the 3 {@code dumpTYPE}
244         * methods, so sub-classes just need to implement the priority types they support.
245         */
246        @SuppressWarnings("unused")
247        default void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
248            dumpCritical(fd, pw, args, asProto);
249            dumpHigh(fd, pw, args, asProto);
250            dumpNormal(fd, pw, args, asProto);
251        }
252    }
253}
254