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 
17 package com.android.server.utils;
18 
19 import android.annotation.IntDef;
20 
21 import java.io.FileDescriptor;
22 import java.io.PrintWriter;
23 import java.lang.annotation.Retention;
24 import java.lang.annotation.RetentionPolicy;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import 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>
36 public 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  */
103 public 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 
PriorityDump()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      */
dump(PriorityDumper dumper, FileDescriptor fd, PrintWriter pw, String[] args)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      */
getPriorityType(String arg)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")
dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto)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")
dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto)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")
dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto)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")
dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto)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