Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c9f96948 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Remove preload2 as it is not used"

parents 5fc20bb9 0dec3802
Loading
Loading
Loading
Loading

tools/preload2/Android.bp

deleted100644 → 0
+0 −50
Original line number Diff line number Diff line
// Copyright (C) 2015 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.

java_library_host {
    name: "preload2",

    srcs: ["src/**/*.java"],

    // To connect to devices (and take hprof dumps).
    static_libs: [
        "ddmlib-prebuilt",
        "tools-common-prebuilt",

        // To process hprof dumps.
        "perflib-prebuilt",

        "trove-prebuilt",
        "guavalib",

        // For JDWP access we use the framework in the JDWP tests from Apache Harmony, for
        // convenience (and to not depend on internal JDK APIs).
        "apache-harmony-jdwp-tests",
        "junit",
    ],

    // Copy to build artifacts
    dist: {
        targets: [
            "dist_files",
        ],
    },
}

// Copy the preload-tool shell script to the host's bin directory.
sh_binary_host {
    name: "preload-tool",
    src: "preload-tool",
    required: ["preload2"],
}

tools/preload2/preload-tool

deleted100644 → 0
+0 −37
Original line number Diff line number Diff line
# Copyright (C) 2015 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.

# This script is used on the host only. It uses a common subset
# shell dialect that should work well. It is partially derived
# from art/tools/art.

function follow_links() {
  if [ z"$BASH_SOURCE" != z ]; then
    file="$BASH_SOURCE"
  else
    file="$0"
  fi
  while [ -h "$file" ]; do
    # On Mac OS, readlink -f doesn't work.
    file="$(readlink "$file")"
  done
  echo "$file"
}


PROG_NAME="$(follow_links)"
PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
ANDROID_ROOT=$PROG_DIR/..

java -cp $ANDROID_ROOT/framework/preload2.jar com.android.preload.Main $@
+0 −224
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.preload;

import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
import com.android.ddmlib.Client;
import com.android.ddmlib.IDevice;

/**
 * Helper class for common communication with a Client (the ddms name for a running application).
 *
 * Instances take a default timeout parameter that's applied to all functions without explicit
 * timeout. Timeouts are in milliseconds.
 */
public class ClientUtils {

    private int defaultTimeout;

    public ClientUtils() {
        this(10000);
    }

    public ClientUtils(int defaultTimeout) {
        this.defaultTimeout = defaultTimeout;
    }

    /**
     * Shortcut for findClient with default timeout.
     */
    public Client findClient(IDevice device, String processName, int processPid) {
        return findClient(device, processName, processPid, defaultTimeout);
    }

    /**
     * Find the client with the given process name or process id. The name takes precedence over
     * the process id (if valid). Stop looking after the given timeout.
     *
     * @param device The device to communicate with.
     * @param processName The name of the process. May be null.
     * @param processPid The pid of the process. Values less than or equal to zero are ignored.
     * @param timeout The amount of milliseconds to wait, at most.
     * @return The client, if found. Otherwise null.
     */
    public Client findClient(IDevice device, String processName, int processPid, int timeout) {
        WaitForClient wfc = new WaitForClient(device, processName, processPid, timeout);
        return wfc.get();
    }

    /**
     * Shortcut for findAllClients with default timeout.
     */
    public Client[] findAllClients(IDevice device) {
        return findAllClients(device, defaultTimeout);
    }

    /**
     * Retrieve all clients known to the given device. Wait at most the given timeout.
     *
     * @param device The device to investigate.
     * @param timeout The amount of milliseconds to wait, at most.
     * @return An array of clients running on the given device. May be null depending on the
     *         device implementation.
     */
    public Client[] findAllClients(IDevice device, int timeout) {
        if (device.hasClients()) {
            return device.getClients();
        }
        WaitForClients wfc = new WaitForClients(device, timeout);
        return wfc.get();
    }

    private static class WaitForClient implements IClientChangeListener {

        private IDevice device;
        private String processName;
        private int processPid;
        private long timeout;
        private Client result;

        public WaitForClient(IDevice device, String processName, int processPid, long timeout) {
            this.device = device;
            this.processName = processName;
            this.processPid = processPid;
            this.timeout = timeout;
            this.result = null;
        }

        public Client get() {
            synchronized (this) {
                AndroidDebugBridge.addClientChangeListener(this);

                // Maybe it's already there.
                if (result == null) {
                    result = searchForClient(device);
                }

                if (result == null) {
                    try {
                        wait(timeout);
                    } catch (InterruptedException e) {
                        // Note: doesn't guard for spurious wakeup.
                    }
                }
            }

            AndroidDebugBridge.removeClientChangeListener(this);
            return result;
        }

        private Client searchForClient(IDevice device) {
            if (processName != null) {
                Client tmp = device.getClient(processName);
                if (tmp != null) {
                    return tmp;
                }
            }
            if (processPid > 0) {
                String name = device.getClientName(processPid);
                if (name != null && !name.isEmpty()) {
                    Client tmp = device.getClient(name);
                    if (tmp != null) {
                        return tmp;
                    }
                }
            }
            if (processPid > 0) {
                // Try manual search.
                for (Client cl : device.getClients()) {
                    if (cl.getClientData().getPid() == processPid
                            && cl.getClientData().getClientDescription() != null) {
                        return cl;
                    }
                }
            }
            return null;
        }

        private boolean isTargetClient(Client c) {
            if (processPid > 0 && c.getClientData().getPid() == processPid) {
                return true;
            }
            if (processName != null
                    && processName.equals(c.getClientData().getClientDescription())) {
                return true;
            }
            return false;
        }

        @Override
        public void clientChanged(Client arg0, int arg1) {
            synchronized (this) {
                if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
                    if (isTargetClient(arg0)) {
                        result = arg0;
                        notifyAll();
                    }
                }
            }
        }
    }

    private static class WaitForClients implements IClientChangeListener {

        private IDevice device;
        private long timeout;

        public WaitForClients(IDevice device, long timeout) {
            this.device = device;
            this.timeout = timeout;
        }

        public Client[] get() {
            synchronized (this) {
                AndroidDebugBridge.addClientChangeListener(this);

                if (device.hasClients()) {
                    return device.getClients();
                }

                try {
                    wait(timeout); // Note: doesn't guard for spurious wakeup.
                } catch (InterruptedException exc) {
                }

                // We will be woken up when the first client data arrives. Sleep a little longer
                // to give (hopefully all of) the rest of the clients a chance to become available.
                // Note: a loop with timeout is brittle as well and complicated, just accept this
                //       for now.
                try {
                    Thread.sleep(500);
                } catch (InterruptedException exc) {
                }
            }

            AndroidDebugBridge.removeClientChangeListener(this);

            return device.getClients();
        }

        @Override
        public void clientChanged(Client arg0, int arg1) {
            synchronized (this) {
                if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
                    notifyAll();
                }
            }
        }
    }
}
+0 −420
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.preload;

import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
import com.android.preload.classdataretrieval.hprof.Hprof;
import com.android.ddmlib.DdmPreferences;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.IShellOutputReceiver;
import com.android.ddmlib.SyncException;
import com.android.ddmlib.TimeoutException;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * Helper class for some device routines.
 */
public class DeviceUtils {

  // Locations
  private static final String PRELOADED_CLASSES_FILE = "/etc/preloaded-classes";
  // Shell commands
  private static final String CREATE_EMPTY_PRELOADED_CMD = "touch " + PRELOADED_CLASSES_FILE;
  private static final String DELETE_CACHE_CMD = "rm /data/dalvik-cache/*/*boot.art";
  private static final String DELETE_PRELOADED_CMD = "rm " + PRELOADED_CLASSES_FILE;
  private static final String READ_PRELOADED_CMD = "cat " + PRELOADED_CLASSES_FILE;
  private static final String START_SHELL_CMD = "start";
  private static final String STOP_SHELL_CMD = "stop";
  private static final String REMOUNT_SYSTEM_CMD = "mount -o rw,remount /system";
  private static final String UNSET_BOOTCOMPLETE_CMD = "setprop dev.bootcomplete \"0\"";

  public static void init(int debugPort) {
    DdmPreferences.setSelectedDebugPort(debugPort);

    Hprof.init();

    AndroidDebugBridge.init(true);

    AndroidDebugBridge.createBridge();
  }

  /**
   * Run a command in the shell on the device.
   */
  public static void doShell(IDevice device, String cmdline, long timeout, TimeUnit unit) {
    doShell(device, cmdline, new NullShellOutputReceiver(), timeout, unit);
  }

  /**
   * Run a command in the shell on the device. Collects and returns the console output.
   */
  public static String doShellReturnString(IDevice device, String cmdline, long timeout,
      TimeUnit unit) {
    CollectStringShellOutputReceiver rec = new CollectStringShellOutputReceiver();
    doShell(device, cmdline, rec, timeout, unit);
    return rec.toString();
  }

  /**
   * Run a command in the shell on the device, directing all output to the given receiver.
   */
  public static void doShell(IDevice device, String cmdline, IShellOutputReceiver receiver,
      long timeout, TimeUnit unit) {
    try {
      device.executeShellCommand(cmdline, receiver, timeout, unit);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * Run am start on the device.
   */
  public static void doAMStart(IDevice device, String name, String activity) {
    doShell(device, "am start -n " + name + " /." + activity, 30, TimeUnit.SECONDS);
  }

  /**
   * Find the device with the given serial. Give up after the given timeout (in milliseconds).
   */
  public static IDevice findDevice(String serial, int timeout) {
    WaitForDevice wfd = new WaitForDevice(serial, timeout);
    return wfd.get();
  }

  /**
   * Get all devices ddms knows about. Wait at most for the given timeout.
   */
  public static IDevice[] findDevices(int timeout) {
    WaitForDevice wfd = new WaitForDevice(null, timeout);
    wfd.get();
    return AndroidDebugBridge.getBridge().getDevices();
  }

  /**
   * Return the build type of the given device. This is the value of the "ro.build.type"
   * system property.
   */
  public static String getBuildType(IDevice device) {
    try {
      Future<String> buildType = device.getSystemProperty("ro.build.type");
      return buildType.get(500, TimeUnit.MILLISECONDS);
    } catch (Exception e) {
    }
    return null;
  }

  /**
   * Check whether the given device has a pre-optimized boot image. More precisely, checks
   * whether /system/framework/ * /boot.art exists.
   */
  public static boolean hasPrebuiltBootImage(IDevice device) {
    String ret =
        doShellReturnString(device, "ls /system/framework/*/boot.art", 500, TimeUnit.MILLISECONDS);

    return !ret.contains("No such file or directory");
  }

    /**
     * Write over the preloaded-classes file with an empty or existing file and regenerate the boot
     * image as necessary.
     *
     * @param device
     * @param pcFile
     * @param bootTimeout
     * @throws AdbCommandRejectedException
     * @throws IOException
     * @throws TimeoutException
     * @throws SyncException
     * @return true if successfully overwritten, false otherwise
     */
    public static boolean overwritePreloaded(IDevice device, File pcFile, long bootTimeout)
            throws AdbCommandRejectedException, IOException, TimeoutException, SyncException {
        boolean writeEmpty = (pcFile == null);
        if (writeEmpty) {
            // Check if the preloaded-classes file is already empty.
            String oldContent =
                    doShellReturnString(device, READ_PRELOADED_CMD, 1, TimeUnit.SECONDS);
            if (oldContent.trim().equals("")) {
                System.out.println("Preloaded-classes already empty.");
                return true;
            }
        }

        // Stop the system server etc.
        doShell(device, STOP_SHELL_CMD, 1, TimeUnit.SECONDS);
        // Remount the read-only system partition
        doShell(device, REMOUNT_SYSTEM_CMD, 1, TimeUnit.SECONDS);
        // Delete the preloaded-classes file
        doShell(device, DELETE_PRELOADED_CMD, 1, TimeUnit.SECONDS);
        // Delete the dalvik cache files
        doShell(device, DELETE_CACHE_CMD, 1, TimeUnit.SECONDS);
        if (writeEmpty) {
            // Write an empty preloaded-classes file
            doShell(device, CREATE_EMPTY_PRELOADED_CMD, 500, TimeUnit.MILLISECONDS);
        } else {
            // Push the new preloaded-classes file
            device.pushFile(pcFile.getAbsolutePath(), PRELOADED_CLASSES_FILE);
        }
        // Manually reset the boot complete flag
        doShell(device, UNSET_BOOTCOMPLETE_CMD, 1, TimeUnit.SECONDS);
        // Restart system server on the device
        doShell(device, START_SHELL_CMD, 1, TimeUnit.SECONDS);
        // Wait for the boot complete flag and return the outcome.
        return waitForBootComplete(device, bootTimeout);
  }

  private static boolean waitForBootComplete(IDevice device, long timeout) {
    // Do a loop checking each second whether bootcomplete. Wait for at most the given
    // threshold.
    Date startDate = new Date();
    for (;;) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        // Ignore spurious wakeup.
      }
      // Check whether bootcomplete.
      String ret =
          doShellReturnString(device, "getprop dev.bootcomplete", 500, TimeUnit.MILLISECONDS);
      if (ret.trim().equals("1")) {
        break;
      }
      System.out.println("Still not booted: " + ret);

      // Check whether we timed out. This is a simplistic check that doesn't take into account
      // things like switches in time.
      Date endDate = new Date();
      long seconds =
          TimeUnit.SECONDS.convert(endDate.getTime() - startDate.getTime(), TimeUnit.MILLISECONDS);
      if (seconds > timeout) {
        return false;
      }
    }

    return true;
  }

  /**
   * Enable method-tracing on device. The system should be restarted after this.
   */
  public static void enableTracing(IDevice device) {
    // Disable selinux.
    doShell(device, "setenforce 0", 100, TimeUnit.MILLISECONDS);

    // Make the profile directory world-writable.
    doShell(device, "chmod 777 /data/dalvik-cache/profiles", 100, TimeUnit.MILLISECONDS);

    // Enable streaming method tracing with a small 1K buffer.
    doShell(device, "setprop dalvik.vm.method-trace true", 100, TimeUnit.MILLISECONDS);
    doShell(device, "setprop dalvik.vm.method-trace-file "
                    + "/data/dalvik-cache/profiles/zygote.trace.bin", 100, TimeUnit.MILLISECONDS);
    doShell(device, "setprop dalvik.vm.method-trace-file-siz 1024", 100, TimeUnit.MILLISECONDS);
    doShell(device, "setprop dalvik.vm.method-trace-stream true", 100, TimeUnit.MILLISECONDS);
  }

  private static class NullShellOutputReceiver implements IShellOutputReceiver {
    @Override
    public boolean isCancelled() {
      return false;
    }

    @Override
    public void flush() {}

    @Override
    public void addOutput(byte[] arg0, int arg1, int arg2) {}
  }

  private static class CollectStringShellOutputReceiver implements IShellOutputReceiver {

    private StringBuilder builder = new StringBuilder();

    @Override
    public String toString() {
      String ret = builder.toString();
      // Strip trailing newlines. They are especially ugly because adb uses DOS line endings.
      while (ret.endsWith("\r") || ret.endsWith("\n")) {
        ret = ret.substring(0, ret.length() - 1);
      }
      return ret;
    }

    @Override
    public void addOutput(byte[] arg0, int arg1, int arg2) {
      builder.append(new String(arg0, arg1, arg2));
    }

    @Override
    public void flush() {}

    @Override
    public boolean isCancelled() {
      return false;
    }
  }

  private static class WaitForDevice {

    private String serial;
    private long timeout;
    private IDevice device;

    public WaitForDevice(String serial, long timeout) {
      this.serial = serial;
      this.timeout = timeout;
      device = null;
    }

    public IDevice get() {
      if (device == null) {
          WaitForDeviceListener wfdl = new WaitForDeviceListener(serial);
          synchronized (wfdl) {
              AndroidDebugBridge.addDeviceChangeListener(wfdl);

              // Check whether we already know about this device.
              IDevice[] devices = AndroidDebugBridge.getBridge().getDevices();
              if (serial != null) {
                  for (IDevice d : devices) {
                      if (serial.equals(d.getSerialNumber())) {
                          // Only accept if there are clients already. Else wait for the callback informing
                          // us that we now have clients.
                          if (d.hasClients()) {
                              device = d;
                          }

                          break;
                      }
                  }
              } else {
                  if (devices.length > 0) {
                      device = devices[0];
                  }
              }

              if (device == null) {
                  try {
                      wfdl.wait(timeout);
                  } catch (InterruptedException e) {
                      // Ignore spurious wakeups.
                  }
                  device = wfdl.getDevice();
              }

              AndroidDebugBridge.removeDeviceChangeListener(wfdl);
          }
      }

      if (device != null) {
          // Wait for clients.
          WaitForClientsListener wfcl = new WaitForClientsListener(device);
          synchronized (wfcl) {
              AndroidDebugBridge.addDeviceChangeListener(wfcl);

              if (!device.hasClients()) {
                  try {
                      wfcl.wait(timeout);
                  } catch (InterruptedException e) {
                      // Ignore spurious wakeups.
                  }
              }

              AndroidDebugBridge.removeDeviceChangeListener(wfcl);
          }
      }

      return device;
    }

    private static class WaitForDeviceListener implements IDeviceChangeListener {

        private String serial;
        private IDevice device;

        public WaitForDeviceListener(String serial) {
            this.serial = serial;
        }

        public IDevice getDevice() {
            return device;
        }

        @Override
        public void deviceChanged(IDevice arg0, int arg1) {
            // We may get a device changed instead of connected. Handle like a connection.
            deviceConnected(arg0);
        }

        @Override
        public void deviceConnected(IDevice arg0) {
            if (device != null) {
                // Ignore updates.
                return;
            }

            if (serial == null || serial.equals(arg0.getSerialNumber())) {
                device = arg0;
                synchronized (this) {
                    notifyAll();
                }
            }
        }

        @Override
        public void deviceDisconnected(IDevice arg0) {
            // Ignore disconnects.
        }

    }

    private static class WaitForClientsListener implements IDeviceChangeListener {

        private IDevice myDevice;

        public WaitForClientsListener(IDevice myDevice) {
            this.myDevice = myDevice;
        }

        @Override
        public void deviceChanged(IDevice arg0, int arg1) {
            if (arg0 == myDevice && (arg1 & IDevice.CHANGE_CLIENT_LIST) != 0) {
                // Got a client list, done here.
                synchronized (this) {
                    notifyAll();
                }
            }
        }

        @Override
        public void deviceConnected(IDevice arg0) {
        }

        @Override
        public void deviceDisconnected(IDevice arg0) {
        }

    }
  }

}
+0 −91

File deleted.

Preview size limit exceeded, changes collapsed.

Loading