Andreas Gampe | 554d7ee | 2015-09-15 08:57:12 -0700 | [diff] [blame] | 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; |
Michael Rosenfeld | 286839e | 2017-02-14 17:52:23 -0800 | [diff] [blame] | 32 | import com.android.preload.actions.WritePreloadedClassesAction; |
Andreas Gampe | 554d7ee | 2015-09-15 08:57:12 -0700 | [diff] [blame] | 33 | import com.android.preload.classdataretrieval.ClassDataRetriever; |
| 34 | import com.android.preload.classdataretrieval.hprof.Hprof; |
| 35 | import com.android.preload.classdataretrieval.jdwp.JDWPClassDataRetriever; |
Andreas Gampe | 5cb8998 | 2016-11-28 13:15:10 -0800 | [diff] [blame] | 36 | import com.android.preload.ui.IUI; |
Andreas Gampe | 4374749 | 2016-11-28 15:07:39 -0800 | [diff] [blame] | 37 | import com.android.preload.ui.SequenceUI; |
Andreas Gampe | 5cb8998 | 2016-11-28 13:15:10 -0800 | [diff] [blame] | 38 | import com.android.preload.ui.SwingUI; |
Michael Rosenfeld | c37b0a4 | 2016-11-28 18:08:00 -0800 | [diff] [blame] | 39 | |
Andreas Gampe | 4374749 | 2016-11-28 15:07:39 -0800 | [diff] [blame] | 40 | import java.io.File; |
Andreas Gampe | 554d7ee | 2015-09-15 08:57:12 -0700 | [diff] [blame] | 41 | import java.util.ArrayList; |
Andreas Gampe | 4374749 | 2016-11-28 15:07:39 -0800 | [diff] [blame] | 42 | import java.util.Arrays; |
Andreas Gampe | 554d7ee | 2015-09-15 08:57:12 -0700 | [diff] [blame] | 43 | import java.util.Collection; |
Andreas Gampe | 4374749 | 2016-11-28 15:07:39 -0800 | [diff] [blame] | 44 | import java.util.Iterator; |
Andreas Gampe | 554d7ee | 2015-09-15 08:57:12 -0700 | [diff] [blame] | 45 | import java.util.List; |
| 46 | import java.util.Map; |
Michael Rosenfeld | c37b0a4 | 2016-11-28 18:08:00 -0800 | [diff] [blame] | 47 | import java.util.NoSuchElementException; |
Andreas Gampe | 554d7ee | 2015-09-15 08:57:12 -0700 | [diff] [blame] | 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 | |
Andreas Gampe | 5cb8998 | 2016-11-28 13:15:10 -0800 | [diff] [blame] | 76 | private IUI ui; |
Andreas Gampe | 554d7ee | 2015-09-15 08:57:12 -0700 | [diff] [blame] | 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 | |
Michael Rosenfeld | c37b0a4 | 2016-11-28 18:08:00 -0800 | [diff] [blame] | 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"; |
Michael Rosenfeld | 286839e | 2017-02-14 17:52:23 -0800 | [diff] [blame] | 100 | public final static String WRITE_CMD = "write"; |
Michael Rosenfeld | c37b0a4 | 2016-11-28 18:08:00 -0800 | [diff] [blame] | 101 | |
Andreas Gampe | 554d7ee | 2015-09-15 08:57:12 -0700 | [diff] [blame] | 102 | /** |
| 103 | * @param args |
| 104 | */ |
| 105 | public static void main(String[] args) { |
Andreas Gampe | 4374749 | 2016-11-28 15:07:39 -0800 | [diff] [blame] | 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 | } |
Andreas Gampe | 554d7ee | 2015-09-15 08:57:12 -0700 | [diff] [blame] | 112 | |
Andreas Gampe | 4374749 | 2016-11-28 15:07:39 -0800 | [diff] [blame] | 113 | top = m; |
Andreas Gampe | 554d7ee | 2015-09-15 08:57:12 -0700 | [diff] [blame] | 114 | m.startUp(); |
| 115 | } |
| 116 | |
Andreas Gampe | 5cb8998 | 2016-11-28 13:15:10 -0800 | [diff] [blame] | 117 | public Main(IUI ui) { |
| 118 | this.ui = ui; |
| 119 | |
Andreas Gampe | 554d7ee | 2015-09-15 08:57:12 -0700 | [diff] [blame] | 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)); |
Michael Rosenfeld | 286839e | 2017-02-14 17:52:23 -0800 | [diff] [blame] | 137 | actions.add(new WritePreloadedClassesAction(clientUtils, null, dataTableModel)); |
Andreas Gampe | 554d7ee | 2015-09-15 08:57:12 -0700 | [diff] [blame] | 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 | |
Andreas Gampe | 5cb8998 | 2016-11-28 13:15:10 -0800 | [diff] [blame] | 149 | ui.prepare(clientListModel, dataTableModel, actions); |
Andreas Gampe | 554d7ee | 2015-09-15 08:57:12 -0700 | [diff] [blame] | 150 | } |
| 151 | |
Andreas Gampe | 4374749 | 2016-11-28 15:07:39 -0800 | [diff] [blame] | 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 |
Michael Rosenfeld | c37b0a4 | 2016-11-28 18:08:00 -0800 | [diff] [blame] | 162 | // Setup |
Andreas Gampe | 4374749 | 2016-11-28 15:07:39 -0800 | [diff] [blame] | 163 | ui.choice("#" + it.next()); // Device. |
| 164 | ui.confirmNo(); // Prepare: no. |
Michael Rosenfeld | c37b0a4 | 2016-11-28 18:08:00 -0800 | [diff] [blame] | 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())); |
Michael Rosenfeld | 286839e | 2017-02-14 17:52:23 -0800 | [diff] [blame] | 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())); |
Michael Rosenfeld | c37b0a4 | 2016-11-28 18:08:00 -0800 | [diff] [blame] | 211 | } |
| 212 | } |
| 213 | } catch (NoSuchElementException e) { |
| 214 | System.out.println("Failed to parse action sequence correctly."); |
| 215 | throw e; |
| 216 | } |
Andreas Gampe | 4374749 | 2016-11-28 15:07:39 -0800 | [diff] [blame] | 217 | |
| 218 | return main; |
| 219 | } |
| 220 | |
Andreas Gampe | 5cb8998 | 2016-11-28 13:15:10 -0800 | [diff] [blame] | 221 | public static IUI getUI() { |
Andreas Gampe | 554d7ee | 2015-09-15 08:57:12 -0700 | [diff] [blame] | 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(); |
Andreas Gampe | 5cb8998 | 2016-11-28 13:15:10 -0800 | [diff] [blame] | 269 | getUI().ready(); |
Andreas Gampe | 554d7ee | 2015-09-15 08:57:12 -0700 | [diff] [blame] | 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."); |
Michael Rosenfeld | 286839e | 2017-02-14 17:52:23 -0800 | [diff] [blame] | 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 | } |
Andreas Gampe | 554d7ee | 2015-09-15 08:57:12 -0700 | [diff] [blame] | 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 | } |