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 */ main(String[] args)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 Main(IUI ui)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 */ createSequencedMain(String[] args)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 getUI()221 public static IUI getUI() { 222 return top.ui; 223 } 224 getClassDataRetriever()225 public static ClassDataRetriever getClassDataRetriever() { 226 if (useJdwpClassDataRetriever) { 227 return new JDWPClassDataRetriever(); 228 } else { 229 return new Hprof(HPROF_TIMEOUT_MILLIS); 230 } 231 } 232 getDevice()233 public IDevice getDevice() { 234 return device; 235 } 236 setDevice(IDevice device)237 public void setDevice(IDevice device) { 238 this.device = device; 239 for (DeviceSpecific ds : deviceSpecificActions) { 240 ds.setDevice(device); 241 } 242 } 243 getClientListModel()244 public DefaultListModel<Client> getClientListModel() { 245 return clientListModel; 246 } 247 248 static class DeviceWrapper { 249 IDevice device; 250 DeviceWrapper(IDevice d)251 public DeviceWrapper(IDevice d) { 252 device = d; 253 } 254 255 @Override toString()256 public String toString() { 257 return device.getName() + " (#" + device.getSerialNumber() + ")"; 258 } 259 } 260 startUp()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 initDevice()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 findAndGetClassData(IDevice device, String packageName)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