Home | History | Annotate | Download | only in preload
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.preload;
     18 
     19 import com.android.ddmlib.Client;
     20 import com.android.ddmlib.IDevice;
     21 import com.android.preload.actions.ClearTableAction;
     22 import com.android.preload.actions.ComputeThresholdAction;
     23 import com.android.preload.actions.ComputeThresholdXAction;
     24 import com.android.preload.actions.DeviceSpecific;
     25 import com.android.preload.actions.ExportAction;
     26 import com.android.preload.actions.ImportAction;
     27 import com.android.preload.actions.ReloadListAction;
     28 import com.android.preload.actions.RunMonkeyAction;
     29 import com.android.preload.actions.ScanAllPackagesAction;
     30 import com.android.preload.actions.ScanPackageAction;
     31 import com.android.preload.actions.ShowDataAction;
     32 import com.android.preload.actions.WritePreloadedClassesAction;
     33 import com.android.preload.classdataretrieval.ClassDataRetriever;
     34 import com.android.preload.classdataretrieval.hprof.Hprof;
     35 import com.android.preload.classdataretrieval.jdwp.JDWPClassDataRetriever;
     36 import com.android.preload.ui.IUI;
     37 import com.android.preload.ui.SequenceUI;
     38 import com.android.preload.ui.SwingUI;
     39 
     40 import java.io.File;
     41 import java.util.ArrayList;
     42 import java.util.Arrays;
     43 import java.util.Collection;
     44 import java.util.Iterator;
     45 import java.util.List;
     46 import java.util.Map;
     47 import java.util.NoSuchElementException;
     48 
     49 import javax.swing.Action;
     50 import javax.swing.DefaultListModel;
     51 
     52 public class Main {
     53 
     54     /**
     55      * Enable tracing mode. This is a work-in-progress to derive compiled-methods data, so it is
     56      * off for now.
     57      */
     58     public final static boolean ENABLE_TRACING = false;
     59 
     60     /**
     61      * Ten-second timeout.
     62      */
     63     public final static int DEFAULT_TIMEOUT_MILLIS = 10 * 1000;
     64 
     65     /**
     66      * Hprof timeout. Two minutes.
     67      */
     68     public final static int HPROF_TIMEOUT_MILLIS = 120 * 1000;
     69 
     70     private IDevice device;
     71     private static ClientUtils clientUtils;
     72 
     73     private DumpTableModel dataTableModel;
     74     private DefaultListModel<Client> clientListModel;
     75 
     76     private IUI ui;
     77 
     78     // Actions that need to be updated once a device is selected.
     79     private Collection<DeviceSpecific> deviceSpecificActions;
     80 
     81     // Current main instance.
     82     private static Main top;
     83     private static boolean useJdwpClassDataRetriever = false;
     84 
     85     public final static String CLASS_PRELOAD_BLACKLIST = "android.app.AlarmManager$" + "|"
     86             + "android.app.SearchManager$" + "|" + "android.os.FileObserver$" + "|"
     87             + "com.android.server.PackageManagerService\\$AppDirObserver$" + "|" +
     88 
     89 
     90             // Threads
     91             "android.os.AsyncTask$" + "|" + "android.pim.ContactsAsyncHelper$" + "|"
     92             + "android.webkit.WebViewClassic\\$1$" + "|" + "java.lang.ProcessManager$" + "|"
     93             + "(.*\\$NoPreloadHolder$)";
     94 
     95     public final static String SCAN_ALL_CMD = "scan-all";
     96     public final static String SCAN_PACKAGE_CMD = "scan";
     97     public final static String COMPUTE_FILE_CMD = "comp";
     98     public final static String EXPORT_CMD = "export";
     99     public final static String IMPORT_CMD = "import";
    100     public final static String WRITE_CMD = "write";
    101 
    102     /**
    103      * @param args
    104      */
    105     public static void main(String[] args) {
    106         Main m;
    107         if (args.length > 0 && args[0].equals("--seq")) {
    108             m = createSequencedMain(args);
    109         } else {
    110             m = new Main(new SwingUI());
    111         }
    112 
    113         top = m;
    114         m.startUp();
    115     }
    116 
    117     public Main(IUI ui) {
    118         this.ui = ui;
    119 
    120         clientListModel = new DefaultListModel<Client>();
    121         dataTableModel = new DumpTableModel();
    122 
    123         clientUtils = new ClientUtils(DEFAULT_TIMEOUT_MILLIS);  // Client utils with 10s timeout.
    124 
    125         List<Action> actions = new ArrayList<Action>();
    126         actions.add(new ReloadListAction(clientUtils, null, clientListModel));
    127         actions.add(new ClearTableAction(dataTableModel));
    128         actions.add(new RunMonkeyAction(null, dataTableModel));
    129         actions.add(new ScanPackageAction(clientUtils, null, dataTableModel));
    130         actions.add(new ScanAllPackagesAction(clientUtils, null, dataTableModel));
    131         actions.add(new ComputeThresholdAction("Compute preloaded-classes", dataTableModel, 2,
    132                 CLASS_PRELOAD_BLACKLIST));
    133         actions.add(new ComputeThresholdAction("Compute compiled-classes", dataTableModel, 1,
    134                 null));
    135         actions.add(new ComputeThresholdXAction("Compute(X)", dataTableModel,
    136                 CLASS_PRELOAD_BLACKLIST));
    137         actions.add(new WritePreloadedClassesAction(clientUtils, null, dataTableModel));
    138         actions.add(new ShowDataAction(dataTableModel));
    139         actions.add(new ImportAction(dataTableModel));
    140         actions.add(new ExportAction(dataTableModel));
    141 
    142         deviceSpecificActions = new ArrayList<DeviceSpecific>();
    143         for (Action a : actions) {
    144             if (a instanceof DeviceSpecific) {
    145                 deviceSpecificActions.add((DeviceSpecific)a);
    146             }
    147         }
    148 
    149         ui.prepare(clientListModel, dataTableModel, actions);
    150     }
    151 
    152     /**
    153      * @param args
    154      * @return
    155      */
    156     private static Main createSequencedMain(String[] args) {
    157         SequenceUI ui = new SequenceUI();
    158         Main main = new Main(ui);
    159 
    160         Iterator<String> it = Arrays.asList(args).iterator();
    161         it.next();  // --seq
    162         // Setup
    163         ui.choice("#" + it.next());  // Device.
    164         ui.confirmNo();              // Prepare: no.
    165         // Actions
    166         try {
    167             while (it.hasNext()) {
    168                 String op = it.next();
    169                 // Operation: Scan a single package
    170                 if (SCAN_PACKAGE_CMD.equals(op)) {
    171                     System.out.println("Scanning package.");
    172                     ui.action(ScanPackageAction.class);
    173                     ui.client(it.next());
    174                 // Operation: Scan all packages
    175                 } else if (SCAN_ALL_CMD.equals(op)) {
    176                     System.out.println("Scanning all packages.");
    177                     ui.action(ScanAllPackagesAction.class);
    178                 // Operation: Export the output to a file
    179                 } else if (EXPORT_CMD.equals(op)) {
    180                     System.out.println("Exporting data.");
    181                     ui.action(ExportAction.class);
    182                     ui.output(new File(it.next()));
    183                 // Operation: Import the input from a file or directory
    184                 } else if (IMPORT_CMD.equals(op)) {
    185                     System.out.println("Importing data.");
    186                     File file = new File(it.next());
    187                     if (!file.exists()) {
    188                         throw new RuntimeException(
    189                                 String.format("File does not exist, %s.", file.getAbsolutePath()));
    190                     } else if (file.isFile()) {
    191                         ui.action(ImportAction.class);
    192                         ui.input(file);
    193                     } else if (file.isDirectory()) {
    194                         for (File content : file.listFiles()) {
    195                             ui.action(ImportAction.class);
    196                             ui.input(content);
    197                         }
    198                     }
    199                 // Operation: Compute preloaded classes with specific threshold
    200                 } else if (COMPUTE_FILE_CMD.equals(op)) {
    201                     System.out.println("Compute preloaded classes.");
    202                     ui.action(ComputeThresholdXAction.class);
    203                     ui.input(it.next());
    204                     ui.confirmYes();
    205                     ui.output(new File(it.next()));
    206                 // Operation: Write preloaded classes from a specific file
    207                 } else if (WRITE_CMD.equals(op)) {
    208                     System.out.println("Writing preloaded classes.");
    209                     ui.action(WritePreloadedClassesAction.class);
    210                     ui.input(new File(it.next()));
    211                 }
    212             }
    213         } catch (NoSuchElementException e) {
    214             System.out.println("Failed to parse action sequence correctly.");
    215             throw e;
    216         }
    217 
    218         return main;
    219     }
    220 
    221     public static IUI getUI() {
    222         return top.ui;
    223     }
    224 
    225     public static ClassDataRetriever getClassDataRetriever() {
    226         if (useJdwpClassDataRetriever) {
    227             return new JDWPClassDataRetriever();
    228         } else {
    229             return new Hprof(HPROF_TIMEOUT_MILLIS);
    230         }
    231     }
    232 
    233     public IDevice getDevice() {
    234         return device;
    235     }
    236 
    237     public void setDevice(IDevice device) {
    238         this.device = device;
    239         for (DeviceSpecific ds : deviceSpecificActions) {
    240             ds.setDevice(device);
    241         }
    242     }
    243 
    244     public DefaultListModel<Client> getClientListModel() {
    245         return clientListModel;
    246     }
    247 
    248     static class DeviceWrapper {
    249         IDevice device;
    250 
    251         public DeviceWrapper(IDevice d) {
    252             device = d;
    253         }
    254 
    255         @Override
    256         public String toString() {
    257             return device.getName() + " (#" + device.getSerialNumber() + ")";
    258         }
    259     }
    260 
    261     private void startUp() {
    262         getUI().showWaitDialog();
    263         initDevice();
    264 
    265         // Load clients.
    266         new ReloadListAction(clientUtils, getDevice(), clientListModel).run();
    267 
    268         getUI().hideWaitDialog();
    269         getUI().ready();
    270     }
    271 
    272     private void initDevice() {
    273         DeviceUtils.init(DEFAULT_TIMEOUT_MILLIS);
    274 
    275         IDevice devices[] = DeviceUtils.findDevices(DEFAULT_TIMEOUT_MILLIS);
    276         if (devices == null || devices.length == 0) {
    277             throw new RuntimeException("Could not find any devices...");
    278         }
    279 
    280         getUI().hideWaitDialog();
    281 
    282         DeviceWrapper deviceWrappers[] = new DeviceWrapper[devices.length];
    283         for (int i = 0; i < devices.length; i++) {
    284             deviceWrappers[i] = new DeviceWrapper(devices[i]);
    285         }
    286 
    287         DeviceWrapper ret = Main.getUI().showChoiceDialog("Choose a device", "Choose device",
    288                 deviceWrappers);
    289         if (ret != null) {
    290             setDevice(ret.device);
    291         } else {
    292             System.exit(0);
    293         }
    294 
    295         boolean prepare = Main.getUI().showConfirmDialog("Prepare device?",
    296                 "Do you want to prepare the device? This is highly recommended.");
    297         if (prepare) {
    298             String buildType = DeviceUtils.getBuildType(device);
    299             if (buildType == null || (!buildType.equals("userdebug") && !buildType.equals("eng"))) {
    300                 Main.getUI().showMessageDialog("Need a userdebug or eng build! (Found " + buildType
    301                         + ")");
    302                 return;
    303             }
    304             if (DeviceUtils.hasPrebuiltBootImage(device)) {
    305                 Main.getUI().showMessageDialog("Cannot prepare a device with pre-optimized boot "
    306                         + "image!");
    307                 return;
    308             }
    309 
    310             if (ENABLE_TRACING) {
    311                 DeviceUtils.enableTracing(device);
    312             }
    313 
    314             Main.getUI().showMessageDialog("The device will reboot. This will potentially take a "
    315                     + "long time. Please be patient.");
    316             boolean success = false;
    317             try {
    318                 success = DeviceUtils.overwritePreloaded(device, null, 15 * 60);
    319             } catch (Exception e) {
    320                 System.err.println(e);
    321             } finally {
    322                 if (!success) {
    323                     Main.getUI().showMessageDialog(
    324                             "Removing preloaded-classes failed unexpectedly!");
    325                 }
    326             }
    327         }
    328     }
    329 
    330     public static Map<String, String> findAndGetClassData(IDevice device, String packageName)
    331             throws Exception {
    332         Client client = clientUtils.findClient(device, packageName, -1);
    333         if (client == null) {
    334             throw new RuntimeException("Could not find client...");
    335         }
    336         System.out.println("Found client: " + client);
    337 
    338         return getClassDataRetriever().getClassData(client);
    339     }
    340 
    341 }
    342