content: /*
   * Copyright (C) 2008 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.
   */
  
  import java.io.BufferedWriter;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.OutputStreamWriter;
  import java.io.Writer;
  import java.nio.charset.Charset;
  import java.util.Set;
  import java.util.TreeSet;
  
  /**
   * Writes /frameworks/base/preloaded-classes. Also updates
   * {@link LoadedClass#preloaded} fields and writes over compiled log file.
   */
  public class WritePreloadedClassFile {
  
      /**
       * Preload any class that take longer to load than MIN_LOAD_TIME_MICROS us.
       */
      static final int MIN_LOAD_TIME_MICROS = 1250;
  
      /**
       * Preload any class that was loaded by at least MIN_PROCESSES processes.
       */
      static final int MIN_PROCESSES = 10;
  
      public static void main(String[] args) throws IOException,
              ClassNotFoundException {
          if (args.length != 1) {
              System.err.println("Usage: WritePreloadedClassFile [compiled log]");
              System.exit(-1);
          }
          String rootFile = args[0];
          Root root = Root.fromFile(rootFile);
  
          // No classes are preloaded to start.
          for (LoadedClass loadedClass : root.loadedClasses.values()) {
              loadedClass.preloaded = false;
          }
  
          // Open preloaded-classes file for output.
          Writer out = new BufferedWriter(new OutputStreamWriter(
                  new FileOutputStream(Policy.PRELOADED_CLASS_FILE),
                  Charset.forName("US-ASCII")));
  
          out.write("# Classes which are preloaded by"
                  + " com.android.internal.os.ZygoteInit.\n");
          out.write("# Automatically generated by frameworks/base/tools/preload/"
              + WritePreloadedClassFile.class.getSimpleName() + ".java.\n");
          out.write("# MIN_LOAD_TIME_MICROS=" + MIN_LOAD_TIME_MICROS + "\n");
          out.write("# MIN_PROCESSES=" + MIN_PROCESSES + "\n");
  
          /*
           * The set of classes to preload. We preload a class if:
           *
           *  a) it's loaded in the bootclasspath (i.e., is a system class)
           *  b) it takes > MIN_LOAD_TIME_MICROS us to load, and
           *  c) it's loaded by more than one process, or it's loaded by an
           *     application (i.e., not a long running service)
           */
          Set<LoadedClass> toPreload = new TreeSet<LoadedClass>();
  
          // Preload classes that were loaded by at least 2 processes. Hopefully,
          // the memory associated with these classes will be shared.
          for (LoadedClass loadedClass : root.loadedClasses.values()) {
              Set<String> names = loadedClass.processNames();
              if (!Policy.isPreloadable(loadedClass)) {
                  continue;
              }
  
              if (names.size() >= MIN_PROCESSES ||
                      (loadedClass.medianTimeMicros() > MIN_LOAD_TIME_MICROS && names.size() > 1)) {
                  toPreload.add(loadedClass);
              }
          }
  
          int initialSize = toPreload.size();
          System.out.println(initialSize
                  + " classses were loaded by more than one app.");
  
          // Preload eligable classes from applications (not long-running
          // services).
          for (Proc proc : root.processes.values()) {
              if (proc.fromZygote() && !Policy.isService(proc.name)) {
                  for (Operation operation : proc.operations) {
                      LoadedClass loadedClass = operation.loadedClass;
                      if (shouldPreload(loadedClass)) {
                          toPreload.add(loadedClass);
                      }
                  }
              }
          }
  
          System.out.println("Added " + (toPreload.size() - initialSize)
                  + " more to speed up applications.");
  
          System.out.println(toPreload.size()
                  + " total classes will be preloaded.");
  
          // Make classes that were implicitly loaded by the zygote explicit.
          // This adds minimal overhead but avoid confusion about classes not
          // appearing in the list.
          addAllClassesFrom("zygote", root, toPreload);
  
          for (LoadedClass loadedClass : toPreload) {
              out.write(loadedClass.name + "\n");
          }
  
          out.close();
  
          // Update data to reflect LoadedClass.preloaded changes.
          for (LoadedClass loadedClass : toPreload) {
              loadedClass.preloaded = true;
          }
          root.toFile(rootFile);
      }
  
      private static void addAllClassesFrom(String processName, Root root,
              Set<LoadedClass> toPreload) {
          for (Proc proc : root.processes.values()) {
              if (proc.name.equals(processName)) {
                  for (Operation operation : proc.operations) {
                      boolean preloadable
                              = Policy.isPreloadable(operation.loadedClass);
                      if (preloadable) {
                          toPreload.add(operation.loadedClass);
                      }
                  }
              }
          }
      }
  
      /**
       * Returns true if the class should be preloaded.
       */
      private static boolean shouldPreload(LoadedClass clazz) {
          return Policy.isPreloadable(clazz)
                  && clazz.medianTimeMicros() > MIN_LOAD_TIME_MICROS;
      }
  }
  