Home | History | Annotate | Download | only in utils
      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 
    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