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

Commit 9809b8fd authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add functionality to overwrite the /etc/preloaded-classes file."

parents 0b6b10d1 286839e4
Loading
Loading
Loading
Loading
+66 −36
Original line number Diff line number Diff line
@@ -16,13 +16,18 @@

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;
@@ -32,6 +37,18 @@ import java.util.concurrent.TimeUnit;
 */
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);

@@ -120,42 +137,55 @@ public class DeviceUtils {
  }

    /**
   * Remove files involved in a standard build that interfere with collecting data. This will
   * remove /etc/preloaded-classes, which determines which classes are allocated already in the
   * boot image. It also deletes any compiled boot image on the device. Then it restarts the
   * device.
     * Write over the preloaded-classes file with an empty or existing file and regenerate the boot
     * image as necessary.
     *
   * This is a potentially long-running operation, as the boot after the deletion may take a while.
   * The method will abort after the given timeout.
     * @param device
     * @param pcFile
     * @param bootTimeout
     * @throws AdbCommandRejectedException
     * @throws IOException
     * @throws TimeoutException
     * @throws SyncException
     * @return true if successfully overwritten, false otherwise
     */
  public static boolean removePreloaded(IDevice device, long preloadedWaitTimeInSeconds) {
    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 =
        DeviceUtils.doShellReturnString(device, "cat /etc/preloaded-classes", 1, TimeUnit.SECONDS);
                    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", 100, TimeUnit.MILLISECONDS);

    // Remount /system, delete /etc/preloaded-classes. It would be nice to use "adb remount,"
    // but AndroidDebugBridge doesn't expose it.
    doShell(device, "mount -o remount,rw /system", 500, TimeUnit.MILLISECONDS);
    doShell(device, "rm /etc/preloaded-classes", 100, TimeUnit.MILLISECONDS);
    // We do need an empty file.
    doShell(device, "touch /etc/preloaded-classes", 100, TimeUnit.MILLISECONDS);

    // Delete the files in the dalvik cache.
    doShell(device, "rm /data/dalvik-cache/*/*boot.art", 500, TimeUnit.MILLISECONDS);

    // We'll try to use dev.bootcomplete to know when the system server is back up. But stop
    // doesn't reset it, so do it manually.
    doShell(device, "setprop dev.bootcomplete \"0\"", 500, TimeUnit.MILLISECONDS);

    // Start the system server.
    doShell(device, "start", 100, TimeUnit.MILLISECONDS);
        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();
@@ -178,7 +208,7 @@ public class DeviceUtils {
      Date endDate = new Date();
      long seconds =
          TimeUnit.SECONDS.convert(endDate.getTime() - startDate.getTime(), TimeUnit.MILLISECONDS);
      if (seconds > preloadedWaitTimeInSeconds) {
      if (seconds > timeout) {
        return false;
      }
    }
+18 −2
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import com.android.preload.actions.RunMonkeyAction;
import com.android.preload.actions.ScanAllPackagesAction;
import com.android.preload.actions.ScanPackageAction;
import com.android.preload.actions.ShowDataAction;
import com.android.preload.actions.WritePreloadedClassesAction;
import com.android.preload.classdataretrieval.ClassDataRetriever;
import com.android.preload.classdataretrieval.hprof.Hprof;
import com.android.preload.classdataretrieval.jdwp.JDWPClassDataRetriever;
@@ -96,6 +97,7 @@ public class Main {
    public final static String COMPUTE_FILE_CMD = "comp";
    public final static String EXPORT_CMD = "export";
    public final static String IMPORT_CMD = "import";
    public final static String WRITE_CMD = "write";

    /**
     * @param args
@@ -132,6 +134,7 @@ public class Main {
                null));
        actions.add(new ComputeThresholdXAction("Compute(X)", dataTableModel,
                CLASS_PRELOAD_BLACKLIST));
        actions.add(new WritePreloadedClassesAction(clientUtils, null, dataTableModel));
        actions.add(new ShowDataAction(dataTableModel));
        actions.add(new ImportAction(dataTableModel));
        actions.add(new ExportAction(dataTableModel));
@@ -200,6 +203,11 @@ public class Main {
                    ui.input(it.next());
                    ui.confirmYes();
                    ui.output(new File(it.next()));
                // Operation: Write preloaded classes from a specific file
                } else if (WRITE_CMD.equals(op)) {
                    System.out.println("Writing preloaded classes.");
                    ui.action(WritePreloadedClassesAction.class);
                    ui.input(new File(it.next()));
                }
            }
        } catch (NoSuchElementException e) {
@@ -305,8 +313,16 @@ public class Main {

            Main.getUI().showMessageDialog("The device will reboot. This will potentially take a "
                    + "long time. Please be patient.");
            if (!DeviceUtils.removePreloaded(device, 15 * 60) /* 15m timeout */) {
                Main.getUI().showMessageDialog("Removing preloaded-classes failed unexpectedly!");
            boolean success = false;
            try {
                success = DeviceUtils.overwritePreloaded(device, null, 15 * 60);
            } catch (Exception e) {
                System.err.println(e);
            } finally {
                if (!success) {
                    Main.getUI().showMessageDialog(
                            "Removing preloaded-classes failed unexpectedly!");
                }
            }
        }
    }
+60 −0
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.actions;

import com.android.ddmlib.Client;
import com.android.ddmlib.IDevice;
import com.android.preload.ClientUtils;
import com.android.preload.DeviceUtils;
import com.android.preload.DumpData;
import com.android.preload.DumpTableModel;
import com.android.preload.Main;

import java.awt.event.ActionEvent;
import java.io.File;
import java.util.Date;
import java.util.Map;

public class WritePreloadedClassesAction extends AbstractThreadedDeviceSpecificAction {
    private File preloadedClassFile;

    public WritePreloadedClassesAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
        super("Write preloaded classes action", device);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        File[] files = Main.getUI().showOpenDialog(true);
        if (files != null && files.length > 0) {
            preloadedClassFile = files[0];
            super.actionPerformed(e);
        }
    }

    @Override
    public void run() {
        Main.getUI().showWaitDialog();
        try {
            // Write the new file with a 5-minute timeout
            DeviceUtils.overwritePreloaded(device, preloadedClassFile, 5 * 60);
        } catch (Exception e) {
            System.err.println(e);
        } finally {
            Main.getUI().hideWaitDialog();
        }
    }
}