content: /*
   * Copyright (C) 2016 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.server.coverage;
  
  import android.os.Binder;
  import android.os.ParcelFileDescriptor;
  import android.os.ShellCallback;
  import android.os.ShellCommand;
  import android.os.ResultReceiver;
  
  import org.jacoco.agent.rt.RT;
  
  import java.io.BufferedOutputStream;
  import java.io.File;
  import java.io.FileDescriptor;
  import java.io.IOException;
  import java.io.PrintWriter;
  
  /**
   * A service that responds to `cmd coverage ...` and provides a mechanism for dumping code coverage
   * information from the system server process.
   * @hide
   */
  public class CoverageService extends Binder {
  
      public static final String COVERAGE_SERVICE = "coverage";
      public static final boolean ENABLED;
  
      static {
          // This service should only be enabled if org.jacoco.agent.rt.RT was added to the build
          boolean shouldEnable = true;
          try {
              Class.forName("org.jacoco.agent.rt.RT");
          } catch (ClassNotFoundException e) {
              shouldEnable = false;
          }
          ENABLED = shouldEnable;
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
              String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
          new CoverageCommand().exec(this, in, out, err, args, callback, resultReceiver);
      }
  
      /**
       * A {@link ShellCommand} implementation for performing coverage shell commands.
       */
      private static class CoverageCommand extends ShellCommand {
  
          /**
           * {@inheritDoc}
           */
          @Override
          public int onCommand(String cmd) {
              if ("dump".equals(cmd)) {
                  return onDump();
              } else if ("reset".equals(cmd)) {
                  return onReset();
              } else {
                  return handleDefaultCommands(cmd);
              }
          }
  
          /**
           * {@inheritDoc}
           */
          @Override
          public void onHelp() {
              PrintWriter pw = getOutPrintWriter();
              pw.println("Coverage commands:");
              pw.println("  help");
              pw.println("    Print this help text.");
              pw.println("  dump [FILE]");
              pw.println("    Dump code coverage to FILE.");
              pw.println("  reset");
              pw.println("    Reset coverage information.");
          }
  
          /**
           * Perform the "dump" command to write the collected execution data to a file.
           *
           * @return The command result.
           */
          private int onDump() {
              // Figure out where to dump the coverage data
              String dest = getNextArg();
              if (dest == null) {
                  dest = "/data/local/tmp/coverage.ec";
              } else {
                  File f = new File(dest);
                  if (f.isDirectory()) {
                      dest = new File(f, "coverage.ec").getAbsolutePath();
                  }
              }
  
              // Try to open the destination file
              ParcelFileDescriptor fd = openFileForSystem(dest, "w");
              if (fd == null) {
                  return -1;
              }
  
              // Write the execution data to the file
              try (BufferedOutputStream output = new BufferedOutputStream(
                      new ParcelFileDescriptor.AutoCloseOutputStream(fd))) {
                  output.write(RT.getAgent().getExecutionData(false));
                  output.flush();
                  getOutPrintWriter().println(String.format("Dumped coverage data to %s", dest));
              } catch (IOException e) {
                  getErrPrintWriter().println("Failed to dump coverage data: " + e.getMessage());
                  return -1;
              }
  
              return 0;
          }
  
          /**
           * Perform the "reset" command to clear the collected execution data.
           *
           * @return The command result.
           */
          private int onReset() {
              RT.getAgent().reset();
              getOutPrintWriter().println("Reset coverage data");
              return 0;
          }
      }
  }
  