content: /*
   * Copyright (C) 2013 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.internal.app.procstats;
  
  import android.os.UserHandle;
  import android.service.procstats.ProcessStatsEnums;
  import android.service.procstats.ProcessStatsStateProto;
  import android.util.TimeUtils;
  import android.util.proto.ProtoOutputStream;
  
  import static com.android.internal.app.procstats.ProcessStats.*;
  
  import java.io.PrintWriter;
  import java.util.ArrayList;
  
  /**
   * Utilities for dumping.
   */
  public final class DumpUtils {
      public static final String[] STATE_NAMES;
      public static final String[] STATE_LABELS;
      public static final String STATE_LABEL_TOTAL;
      public static final String STATE_LABEL_CACHED;
      public static final String[] STATE_NAMES_CSV;
      static final String[] STATE_TAGS;
      static final int[] STATE_PROTO_ENUMS;
  
      // Make the mapping easy to update.
      static {
          STATE_NAMES = new String[STATE_COUNT];
          STATE_NAMES[STATE_PERSISTENT]               = "Persist";
          STATE_NAMES[STATE_TOP]                      = "Top";
          STATE_NAMES[STATE_IMPORTANT_FOREGROUND]     = "ImpFg";
          STATE_NAMES[STATE_IMPORTANT_BACKGROUND]     = "ImpBg";
          STATE_NAMES[STATE_BACKUP]                   = "Backup";
          STATE_NAMES[STATE_SERVICE]                  = "Service";
          STATE_NAMES[STATE_SERVICE_RESTARTING]       = "ServRst";
          STATE_NAMES[STATE_RECEIVER]                 = "Receivr";
          STATE_NAMES[STATE_HEAVY_WEIGHT]             = "HeavyWt";
          STATE_NAMES[STATE_HOME]                     = "Home";
          STATE_NAMES[STATE_LAST_ACTIVITY]            = "LastAct";
          STATE_NAMES[STATE_CACHED_ACTIVITY]          = "CchAct";
          STATE_NAMES[STATE_CACHED_ACTIVITY_CLIENT]   = "CchCAct";
          STATE_NAMES[STATE_CACHED_EMPTY]             = "CchEmty";
  
          STATE_LABELS = new String[STATE_COUNT];
          STATE_LABELS[STATE_PERSISTENT]              = "Persistent";
          STATE_LABELS[STATE_TOP]                     = "       Top";
          STATE_LABELS[STATE_IMPORTANT_FOREGROUND]    = "    Imp Fg";
          STATE_LABELS[STATE_IMPORTANT_BACKGROUND]    = "    Imp Bg";
          STATE_LABELS[STATE_BACKUP]                  = "    Backup";
          STATE_LABELS[STATE_SERVICE]                 = "   Service";
          STATE_LABELS[STATE_SERVICE_RESTARTING]      = "Service Rs";
          STATE_LABELS[STATE_RECEIVER]                = "  Receiver";
          STATE_LABELS[STATE_HEAVY_WEIGHT]            = " Heavy Wgt";
          STATE_LABELS[STATE_HOME]                    = "    (Home)";
          STATE_LABELS[STATE_LAST_ACTIVITY]           = "(Last Act)";
          STATE_LABELS[STATE_CACHED_ACTIVITY]         = " (Cch Act)";
          STATE_LABELS[STATE_CACHED_ACTIVITY_CLIENT]  = "(Cch CAct)";
          STATE_LABELS[STATE_CACHED_EMPTY]            = "(Cch Emty)";
          STATE_LABEL_CACHED                          = "  (Cached)";
          STATE_LABEL_TOTAL                           = "     TOTAL";
  
          STATE_NAMES_CSV = new String[STATE_COUNT];
          STATE_NAMES_CSV[STATE_PERSISTENT]               = "pers";
          STATE_NAMES_CSV[STATE_TOP]                      = "top";
          STATE_NAMES_CSV[STATE_IMPORTANT_FOREGROUND]     = "impfg";
          STATE_NAMES_CSV[STATE_IMPORTANT_BACKGROUND]     = "impbg";
          STATE_NAMES_CSV[STATE_BACKUP]                   = "backup";
          STATE_NAMES_CSV[STATE_SERVICE]                  = "service";
          STATE_NAMES_CSV[STATE_SERVICE_RESTARTING]       = "service-rs";
          STATE_NAMES_CSV[STATE_RECEIVER]                 = "receiver";
          STATE_NAMES_CSV[STATE_HEAVY_WEIGHT]             = "heavy";
          STATE_NAMES_CSV[STATE_HOME]                     = "home";
          STATE_NAMES_CSV[STATE_LAST_ACTIVITY]            = "lastact";
          STATE_NAMES_CSV[STATE_CACHED_ACTIVITY]          = "cch-activity";
          STATE_NAMES_CSV[STATE_CACHED_ACTIVITY_CLIENT]   = "cch-aclient";
          STATE_NAMES_CSV[STATE_CACHED_EMPTY]             = "cch-empty";
  
          STATE_TAGS = new String[STATE_COUNT];
          STATE_TAGS[STATE_PERSISTENT]                = "p";
          STATE_TAGS[STATE_TOP]                       = "t";
          STATE_TAGS[STATE_IMPORTANT_FOREGROUND]      = "f";
          STATE_TAGS[STATE_IMPORTANT_BACKGROUND]      = "b";
          STATE_TAGS[STATE_BACKUP]                    = "u";
          STATE_TAGS[STATE_SERVICE]                   = "s";
          STATE_TAGS[STATE_SERVICE_RESTARTING]        = "x";
          STATE_TAGS[STATE_RECEIVER]                  = "r";
          STATE_TAGS[STATE_HEAVY_WEIGHT]              = "w";
          STATE_TAGS[STATE_HOME]                      = "h";
          STATE_TAGS[STATE_LAST_ACTIVITY]             = "l";
          STATE_TAGS[STATE_CACHED_ACTIVITY]           = "a";
          STATE_TAGS[STATE_CACHED_ACTIVITY_CLIENT]    = "c";
          STATE_TAGS[STATE_CACHED_EMPTY]              = "e";
  
          STATE_PROTO_ENUMS = new int[STATE_COUNT];
          STATE_PROTO_ENUMS[STATE_PERSISTENT] = ProcessStatsEnums.PROCESS_STATE_PERSISTENT;
          STATE_PROTO_ENUMS[STATE_TOP] = ProcessStatsEnums.PROCESS_STATE_TOP;
          STATE_PROTO_ENUMS[STATE_IMPORTANT_FOREGROUND] =
                  ProcessStatsEnums.PROCESS_STATE_IMPORTANT_FOREGROUND;
          STATE_PROTO_ENUMS[STATE_IMPORTANT_BACKGROUND] =
                  ProcessStatsEnums.PROCESS_STATE_IMPORTANT_BACKGROUND;
          STATE_PROTO_ENUMS[STATE_BACKUP] = ProcessStatsEnums.PROCESS_STATE_BACKUP;
          STATE_PROTO_ENUMS[STATE_SERVICE] = ProcessStatsEnums.PROCESS_STATE_SERVICE;
          STATE_PROTO_ENUMS[STATE_SERVICE_RESTARTING] =
                  ProcessStatsEnums.PROCESS_STATE_SERVICE_RESTARTING;
          STATE_PROTO_ENUMS[STATE_RECEIVER] = ProcessStatsEnums.PROCESS_STATE_RECEIVER;
          STATE_PROTO_ENUMS[STATE_HEAVY_WEIGHT] = ProcessStatsEnums.PROCESS_STATE_HEAVY_WEIGHT;
          STATE_PROTO_ENUMS[STATE_HOME] = ProcessStatsEnums.PROCESS_STATE_HOME;
          STATE_PROTO_ENUMS[STATE_LAST_ACTIVITY] = ProcessStatsEnums.PROCESS_STATE_LAST_ACTIVITY;
          STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY] = ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY;
          STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY_CLIENT] =
                  ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
          STATE_PROTO_ENUMS[STATE_CACHED_EMPTY] = ProcessStatsEnums.PROCESS_STATE_CACHED_EMPTY;
      }
  
      public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] {
              "off", "on"
      };
  
      public static final String[] ADJ_MEM_NAMES_CSV = new String[] {
              "norm", "mod",  "low", "crit"
      };
  
      // State enum is defined in frameworks/base/core/proto/android/service/procstats.proto
      // Update states must sync enum definition as well, the ordering must not be changed.
      static final String[] ADJ_SCREEN_TAGS = new String[] {
              "0", "1"
      };
  
      static final int[] ADJ_SCREEN_PROTO_ENUMS = new int[] {
              ProcessStatsEnums.SCREEN_STATE_OFF,
              ProcessStatsEnums.SCREEN_STATE_ON
      };
  
      static final String[] ADJ_MEM_TAGS = new String[] {
              "n", "m",  "l", "c"
      };
  
      static final int[] ADJ_MEM_PROTO_ENUMS = new int[] {
              ProcessStatsEnums.MEMORY_STATE_NORMAL,
              ProcessStatsEnums.MEMORY_STATE_MODERATE,
              ProcessStatsEnums.MEMORY_STATE_LOW,
              ProcessStatsEnums.MEMORY_STATE_CRITICAL
      };
  
      static final String CSV_SEP = "\t";
  
      /**
       * No instantiate
       */
      private DumpUtils() {
      }
  
      public static void printScreenLabel(PrintWriter pw, int offset) {
          switch (offset) {
              case ADJ_NOTHING:
                  pw.print("     ");
                  break;
              case ADJ_SCREEN_OFF:
                  pw.print("SOff/");
                  break;
              case ADJ_SCREEN_ON:
                  pw.print(" SOn/");
                  break;
              default:
                  pw.print("????/");
                  break;
          }
      }
  
      public static void printScreenLabelCsv(PrintWriter pw, int offset) {
          switch (offset) {
              case ADJ_NOTHING:
                  break;
              case ADJ_SCREEN_OFF:
                  pw.print(ADJ_SCREEN_NAMES_CSV[0]);
                  break;
              case ADJ_SCREEN_ON:
                  pw.print(ADJ_SCREEN_NAMES_CSV[1]);
                  break;
              default:
                  pw.print("???");
                  break;
          }
      }
  
      public static void printMemLabel(PrintWriter pw, int offset, char sep) {
          switch (offset) {
              case ADJ_NOTHING:
                  pw.print("    ");
                  if (sep != 0) pw.print(' ');
                  break;
              case ADJ_MEM_FACTOR_NORMAL:
                  pw.print("Norm");
                  if (sep != 0) pw.print(sep);
                  break;
              case ADJ_MEM_FACTOR_MODERATE:
                  pw.print(" Mod");
                  if (sep != 0) pw.print(sep);
                  break;
              case ADJ_MEM_FACTOR_LOW:
                  pw.print(" Low");
                  if (sep != 0) pw.print(sep);
                  break;
              case ADJ_MEM_FACTOR_CRITICAL:
                  pw.print("Crit");
                  if (sep != 0) pw.print(sep);
                  break;
              default:
                  pw.print("????");
                  if (sep != 0) pw.print(sep);
                  break;
          }
      }
  
      public static void printMemLabelCsv(PrintWriter pw, int offset) {
          if (offset >= ADJ_MEM_FACTOR_NORMAL) {
              if (offset <= ADJ_MEM_FACTOR_CRITICAL) {
                  pw.print(ADJ_MEM_NAMES_CSV[offset]);
              } else {
                  pw.print("???");
              }
          }
      }
  
      public static void printPercent(PrintWriter pw, double fraction) {
          fraction *= 100;
          if (fraction < 1) {
              pw.print(String.format("%.2f", fraction));
          } else if (fraction < 10) {
              pw.print(String.format("%.1f", fraction));
          } else {
              pw.print(String.format("%.0f", fraction));
          }
          pw.print("%");
      }
  
      public static void printProcStateTag(PrintWriter pw, int state) {
          state = printArrayEntry(pw, ADJ_SCREEN_TAGS,  state, ADJ_SCREEN_MOD*STATE_COUNT);
          state = printArrayEntry(pw, ADJ_MEM_TAGS,  state, STATE_COUNT);
          printArrayEntry(pw, STATE_TAGS,  state, 1);
      }
  
      public static void printProcStateTagProto(ProtoOutputStream proto, long screenId, long memId,
              long stateId, int state) {
          state = printProto(proto, screenId, ADJ_SCREEN_PROTO_ENUMS,
                  state, ADJ_SCREEN_MOD * STATE_COUNT);
          state = printProto(proto, memId, ADJ_MEM_PROTO_ENUMS, state, STATE_COUNT);
          printProto(proto, stateId, STATE_PROTO_ENUMS, state, 1);
      }
  
      public static void printAdjTag(PrintWriter pw, int state) {
          state = printArrayEntry(pw, ADJ_SCREEN_TAGS,  state, ADJ_SCREEN_MOD);
          printArrayEntry(pw, ADJ_MEM_TAGS, state, 1);
      }
  
      public static void printProcStateAdjTagProto(ProtoOutputStream proto, long screenId, long memId,
              int state) {
          state = printProto(proto, screenId, ADJ_SCREEN_PROTO_ENUMS,
                  state, ADJ_SCREEN_MOD * STATE_COUNT);
          printProto(proto, memId, ADJ_MEM_PROTO_ENUMS, state, STATE_COUNT);
      }
  
      public static void printProcStateDurationProto(ProtoOutputStream proto, long fieldId,
              int procState, long duration) {
          final long stateToken = proto.start(fieldId);
          DumpUtils.printProto(proto, ProcessStatsStateProto.PROCESS_STATE,
                  DumpUtils.STATE_PROTO_ENUMS, procState, 1);
          proto.write(ProcessStatsStateProto.DURATION_MS, duration);
          proto.end(stateToken);
      }
  
      public static void printProcStateTagAndValue(PrintWriter pw, int state, long value) {
          pw.print(',');
          printProcStateTag(pw, state);
          pw.print(':');
          pw.print(value);
      }
  
      public static void printAdjTagAndValue(PrintWriter pw, int state, long value) {
          pw.print(',');
          printAdjTag(pw, state);
          pw.print(':');
          pw.print(value);
      }
  
      public static long dumpSingleTime(PrintWriter pw, String prefix, long[] durations,
              int curState, long curStartTime, long now) {
          long totalTime = 0;
          int printedScreen = -1;
          for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
              int printedMem = -1;
              for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
                  int state = imem+iscreen;
                  long time = durations[state];
                  String running = "";
                  if (curState == state) {
                      time += now - curStartTime;
                      if (pw != null) {
                          running = " (running)";
                      }
                  }
                  if (time != 0) {
                      if (pw != null) {
                          pw.print(prefix);
                          printScreenLabel(pw, printedScreen != iscreen
                                  ? iscreen : STATE_NOTHING);
                          printedScreen = iscreen;
                          printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, (char)0);
                          printedMem = imem;
                          pw.print(": ");
                          TimeUtils.formatDuration(time, pw); pw.println(running);
                      }
                      totalTime += time;
                  }
              }
          }
          if (totalTime != 0 && pw != null) {
              pw.print(prefix);
              pw.print("    TOTAL: ");
              TimeUtils.formatDuration(totalTime, pw);
              pw.println();
          }
          return totalTime;
      }
  
      public static void dumpAdjTimesCheckin(PrintWriter pw, String sep, long[] durations,
              int curState, long curStartTime, long now) {
          for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
              for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
                  int state = imem+iscreen;
                  long time = durations[state];
                  if (curState == state) {
                      time += now - curStartTime;
                  }
                  if (time != 0) {
                      printAdjTagAndValue(pw, state, time);
                  }
              }
          }
      }
  
      private static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates,
              int[] memStates, int[] procStates) {
          final int NS = screenStates != null ? screenStates.length : 1;
          final int NM = memStates != null ? memStates.length : 1;
          final int NP = procStates != null ? procStates.length : 1;
          for (int is=0; is<NS; is++) {
              for (int im=0; im<NM; im++) {
                  for (int ip=0; ip<NP; ip++) {
                      pw.print(sep);
                      boolean printed = false;
                      if (screenStates != null && screenStates.length > 1) {
                          printScreenLabelCsv(pw, screenStates[is]);
                          printed = true;
                      }
                      if (memStates != null && memStates.length > 1) {
                          if (printed) {
                              pw.print("-");
                          }
                          printMemLabelCsv(pw, memStates[im]);
                          printed = true;
                      }
                      if (procStates != null && procStates.length > 1) {
                          if (printed) {
                              pw.print("-");
                          }
                          pw.print(STATE_NAMES_CSV[procStates[ip]]);
                      }
                  }
              }
          }
      }
  
      public static void dumpProcessSummaryLocked(PrintWriter pw, String prefix, String header,
              ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates,
              long now, long totalTime) {
          for (int i=procs.size()-1; i>=0; i--) {
              final ProcessState proc = procs.get(i);
              proc.dumpSummary(pw, prefix, header, screenStates, memStates, procStates, now,
                      totalTime);
          }
      }
  
      public static void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs,
              boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
              boolean sepProcStates, int[] procStates, long now) {
          pw.print("process");
          pw.print(CSV_SEP);
          pw.print("uid");
          pw.print(CSV_SEP);
          pw.print("vers");
          dumpStateHeadersCsv(pw, CSV_SEP, sepScreenStates ? screenStates : null,
                  sepMemStates ? memStates : null,
                  sepProcStates ? procStates : null);
          pw.println();
          for (int i=procs.size()-1; i>=0; i--) {
              ProcessState proc = procs.get(i);
              pw.print(proc.getName());
              pw.print(CSV_SEP);
              UserHandle.formatUid(pw, proc.getUid());
              pw.print(CSV_SEP);
              pw.print(proc.getVersion());
              proc.dumpCsv(pw, sepScreenStates, screenStates, sepMemStates,
                      memStates, sepProcStates, procStates, now);
              pw.println();
          }
      }
  
      public static int printArrayEntry(PrintWriter pw, String[] array, int value, int mod) {
          int index = value/mod;
          if (index >= 0 && index < array.length) {
              pw.print(array[index]);
          } else {
              pw.print('?');
          }
          return value - index*mod;
      }
  
      public static int printProto(ProtoOutputStream proto, long fieldId,
              int[] enums, int value, int mod) {
          int index = value/mod;
          if (index >= 0 && index < enums.length) {
              proto.write(fieldId, enums[index]);
          } // else enum default is always zero in proto3
          return value - index*mod;
      }
  
      public static String collapseString(String pkgName, String itemName) {
          if (itemName.startsWith(pkgName)) {
              final int ITEMLEN = itemName.length();
              final int PKGLEN = pkgName.length();
              if (ITEMLEN == PKGLEN) {
                  return "";
              } else if (ITEMLEN >= PKGLEN) {
                  if (itemName.charAt(PKGLEN) == '.') {
                      return itemName.substring(PKGLEN);
                  }
              }
          }
          return itemName;
      }
  }
  