blob: c42a19b58811c753eb29060f10cd7c5d34fba014 [file] [log] [blame]
Andreas Gampe554d7ee2015-09-15 08:57:12 -07001/*
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
17package com.android.preload;
18
19import com.android.ddmlib.Client;
20import com.android.ddmlib.IDevice;
21import com.android.preload.actions.ClearTableAction;
22import com.android.preload.actions.ComputeThresholdAction;
23import com.android.preload.actions.ComputeThresholdXAction;
24import com.android.preload.actions.DeviceSpecific;
25import com.android.preload.actions.ExportAction;
26import com.android.preload.actions.ImportAction;
27import com.android.preload.actions.ReloadListAction;
28import com.android.preload.actions.RunMonkeyAction;
29import com.android.preload.actions.ScanAllPackagesAction;
30import com.android.preload.actions.ScanPackageAction;
31import com.android.preload.actions.ShowDataAction;
32import com.android.preload.classdataretrieval.ClassDataRetriever;
33import com.android.preload.classdataretrieval.hprof.Hprof;
34import com.android.preload.classdataretrieval.jdwp.JDWPClassDataRetriever;
Andreas Gampe5cb89982016-11-28 13:15:10 -080035import com.android.preload.ui.IUI;
Andreas Gampe43747492016-11-28 15:07:39 -080036import com.android.preload.ui.SequenceUI;
Andreas Gampe5cb89982016-11-28 13:15:10 -080037import com.android.preload.ui.SwingUI;
Michael Rosenfeldc37b0a42016-11-28 18:08:00 -080038
Andreas Gampe43747492016-11-28 15:07:39 -080039import java.io.File;
Andreas Gampe554d7ee2015-09-15 08:57:12 -070040import java.util.ArrayList;
Andreas Gampe43747492016-11-28 15:07:39 -080041import java.util.Arrays;
Andreas Gampe554d7ee2015-09-15 08:57:12 -070042import java.util.Collection;
Andreas Gampe43747492016-11-28 15:07:39 -080043import java.util.Iterator;
Andreas Gampe554d7ee2015-09-15 08:57:12 -070044import java.util.List;
45import java.util.Map;
Michael Rosenfeldc37b0a42016-11-28 18:08:00 -080046import java.util.NoSuchElementException;
Andreas Gampe554d7ee2015-09-15 08:57:12 -070047
48import javax.swing.Action;
49import javax.swing.DefaultListModel;
50
51public class Main {
52
53 /**
54 * Enable tracing mode. This is a work-in-progress to derive compiled-methods data, so it is
55 * off for now.
56 */
57 public final static boolean ENABLE_TRACING = false;
58
59 /**
60 * Ten-second timeout.
61 */
62 public final static int DEFAULT_TIMEOUT_MILLIS = 10 * 1000;
63
64 /**
65 * Hprof timeout. Two minutes.
66 */
67 public final static int HPROF_TIMEOUT_MILLIS = 120 * 1000;
68
69 private IDevice device;
70 private static ClientUtils clientUtils;
71
72 private DumpTableModel dataTableModel;
73 private DefaultListModel<Client> clientListModel;
74
Andreas Gampe5cb89982016-11-28 13:15:10 -080075 private IUI ui;
Andreas Gampe554d7ee2015-09-15 08:57:12 -070076
77 // Actions that need to be updated once a device is selected.
78 private Collection<DeviceSpecific> deviceSpecificActions;
79
80 // Current main instance.
81 private static Main top;
82 private static boolean useJdwpClassDataRetriever = false;
83
84 public final static String CLASS_PRELOAD_BLACKLIST = "android.app.AlarmManager$" + "|"
85 + "android.app.SearchManager$" + "|" + "android.os.FileObserver$" + "|"
86 + "com.android.server.PackageManagerService\\$AppDirObserver$" + "|" +
87
88
89 // Threads
90 "android.os.AsyncTask$" + "|" + "android.pim.ContactsAsyncHelper$" + "|"
91 + "android.webkit.WebViewClassic\\$1$" + "|" + "java.lang.ProcessManager$" + "|"
92 + "(.*\\$NoPreloadHolder$)";
93
Michael Rosenfeldc37b0a42016-11-28 18:08:00 -080094 public final static String SCAN_ALL_CMD = "scan-all";
95 public final static String SCAN_PACKAGE_CMD = "scan";
96 public final static String COMPUTE_FILE_CMD = "comp";
97 public final static String EXPORT_CMD = "export";
98 public final static String IMPORT_CMD = "import";
99
Andreas Gampe554d7ee2015-09-15 08:57:12 -0700100 /**
101 * @param args
102 */
103 public static void main(String[] args) {
Andreas Gampe43747492016-11-28 15:07:39 -0800104 Main m;
105 if (args.length > 0 && args[0].equals("--seq")) {
106 m = createSequencedMain(args);
107 } else {
108 m = new Main(new SwingUI());
109 }
Andreas Gampe554d7ee2015-09-15 08:57:12 -0700110
Andreas Gampe43747492016-11-28 15:07:39 -0800111 top = m;
Andreas Gampe554d7ee2015-09-15 08:57:12 -0700112 m.startUp();
113 }
114
Andreas Gampe5cb89982016-11-28 13:15:10 -0800115 public Main(IUI ui) {
116 this.ui = ui;
117
Andreas Gampe554d7ee2015-09-15 08:57:12 -0700118 clientListModel = new DefaultListModel<Client>();
119 dataTableModel = new DumpTableModel();
120
121 clientUtils = new ClientUtils(DEFAULT_TIMEOUT_MILLIS); // Client utils with 10s timeout.
122
123 List<Action> actions = new ArrayList<Action>();
124 actions.add(new ReloadListAction(clientUtils, null, clientListModel));
125 actions.add(new ClearTableAction(dataTableModel));
126 actions.add(new RunMonkeyAction(null, dataTableModel));
127 actions.add(new ScanPackageAction(clientUtils, null, dataTableModel));
128 actions.add(new ScanAllPackagesAction(clientUtils, null, dataTableModel));
129 actions.add(new ComputeThresholdAction("Compute preloaded-classes", dataTableModel, 2,
130 CLASS_PRELOAD_BLACKLIST));
131 actions.add(new ComputeThresholdAction("Compute compiled-classes", dataTableModel, 1,
132 null));
133 actions.add(new ComputeThresholdXAction("Compute(X)", dataTableModel,
134 CLASS_PRELOAD_BLACKLIST));
135 actions.add(new ShowDataAction(dataTableModel));
136 actions.add(new ImportAction(dataTableModel));
137 actions.add(new ExportAction(dataTableModel));
138
139 deviceSpecificActions = new ArrayList<DeviceSpecific>();
140 for (Action a : actions) {
141 if (a instanceof DeviceSpecific) {
142 deviceSpecificActions.add((DeviceSpecific)a);
143 }
144 }
145
Andreas Gampe5cb89982016-11-28 13:15:10 -0800146 ui.prepare(clientListModel, dataTableModel, actions);
Andreas Gampe554d7ee2015-09-15 08:57:12 -0700147 }
148
Andreas Gampe43747492016-11-28 15:07:39 -0800149 /**
150 * @param args
151 * @return
152 */
153 private static Main createSequencedMain(String[] args) {
154 SequenceUI ui = new SequenceUI();
155 Main main = new Main(ui);
156
157 Iterator<String> it = Arrays.asList(args).iterator();
158 it.next(); // --seq
Michael Rosenfeldc37b0a42016-11-28 18:08:00 -0800159 // Setup
Andreas Gampe43747492016-11-28 15:07:39 -0800160 ui.choice("#" + it.next()); // Device.
161 ui.confirmNo(); // Prepare: no.
Michael Rosenfeldc37b0a42016-11-28 18:08:00 -0800162 // Actions
163 try {
164 while (it.hasNext()) {
165 String op = it.next();
166 // Operation: Scan a single package
167 if (SCAN_PACKAGE_CMD.equals(op)) {
168 System.out.println("Scanning package.");
169 ui.action(ScanPackageAction.class);
170 ui.client(it.next());
171 // Operation: Scan all packages
172 } else if (SCAN_ALL_CMD.equals(op)) {
173 System.out.println("Scanning all packages.");
174 ui.action(ScanAllPackagesAction.class);
175 // Operation: Export the output to a file
176 } else if (EXPORT_CMD.equals(op)) {
177 System.out.println("Exporting data.");
178 ui.action(ExportAction.class);
179 ui.output(new File(it.next()));
180 // Operation: Import the input from a file or directory
181 } else if (IMPORT_CMD.equals(op)) {
182 System.out.println("Importing data.");
183 File file = new File(it.next());
184 if (!file.exists()) {
185 throw new RuntimeException(
186 String.format("File does not exist, %s.", file.getAbsolutePath()));
187 } else if (file.isFile()) {
188 ui.action(ImportAction.class);
189 ui.input(file);
190 } else if (file.isDirectory()) {
191 for (File content : file.listFiles()) {
192 ui.action(ImportAction.class);
193 ui.input(content);
194 }
195 }
196 // Operation: Compute preloaded classes with specific threshold
197 } else if (COMPUTE_FILE_CMD.equals(op)) {
198 System.out.println("Compute preloaded classes.");
199 ui.action(ComputeThresholdXAction.class);
200 ui.input(it.next());
201 ui.confirmYes();
202 ui.output(new File(it.next()));
203 }
204 }
205 } catch (NoSuchElementException e) {
206 System.out.println("Failed to parse action sequence correctly.");
207 throw e;
208 }
Andreas Gampe43747492016-11-28 15:07:39 -0800209
210 return main;
211 }
212
Andreas Gampe5cb89982016-11-28 13:15:10 -0800213 public static IUI getUI() {
Andreas Gampe554d7ee2015-09-15 08:57:12 -0700214 return top.ui;
215 }
216
217 public static ClassDataRetriever getClassDataRetriever() {
218 if (useJdwpClassDataRetriever) {
219 return new JDWPClassDataRetriever();
220 } else {
221 return new Hprof(HPROF_TIMEOUT_MILLIS);
222 }
223 }
224
225 public IDevice getDevice() {
226 return device;
227 }
228
229 public void setDevice(IDevice device) {
230 this.device = device;
231 for (DeviceSpecific ds : deviceSpecificActions) {
232 ds.setDevice(device);
233 }
234 }
235
236 public DefaultListModel<Client> getClientListModel() {
237 return clientListModel;
238 }
239
240 static class DeviceWrapper {
241 IDevice device;
242
243 public DeviceWrapper(IDevice d) {
244 device = d;
245 }
246
247 @Override
248 public String toString() {
249 return device.getName() + " (#" + device.getSerialNumber() + ")";
250 }
251 }
252
253 private void startUp() {
254 getUI().showWaitDialog();
255 initDevice();
256
257 // Load clients.
258 new ReloadListAction(clientUtils, getDevice(), clientListModel).run();
259
260 getUI().hideWaitDialog();
Andreas Gampe5cb89982016-11-28 13:15:10 -0800261 getUI().ready();
Andreas Gampe554d7ee2015-09-15 08:57:12 -0700262 }
263
264 private void initDevice() {
265 DeviceUtils.init(DEFAULT_TIMEOUT_MILLIS);
266
267 IDevice devices[] = DeviceUtils.findDevices(DEFAULT_TIMEOUT_MILLIS);
268 if (devices == null || devices.length == 0) {
269 throw new RuntimeException("Could not find any devices...");
270 }
271
272 getUI().hideWaitDialog();
273
274 DeviceWrapper deviceWrappers[] = new DeviceWrapper[devices.length];
275 for (int i = 0; i < devices.length; i++) {
276 deviceWrappers[i] = new DeviceWrapper(devices[i]);
277 }
278
279 DeviceWrapper ret = Main.getUI().showChoiceDialog("Choose a device", "Choose device",
280 deviceWrappers);
281 if (ret != null) {
282 setDevice(ret.device);
283 } else {
284 System.exit(0);
285 }
286
287 boolean prepare = Main.getUI().showConfirmDialog("Prepare device?",
288 "Do you want to prepare the device? This is highly recommended.");
289 if (prepare) {
290 String buildType = DeviceUtils.getBuildType(device);
291 if (buildType == null || (!buildType.equals("userdebug") && !buildType.equals("eng"))) {
292 Main.getUI().showMessageDialog("Need a userdebug or eng build! (Found " + buildType
293 + ")");
294 return;
295 }
296 if (DeviceUtils.hasPrebuiltBootImage(device)) {
297 Main.getUI().showMessageDialog("Cannot prepare a device with pre-optimized boot "
298 + "image!");
299 return;
300 }
301
302 if (ENABLE_TRACING) {
303 DeviceUtils.enableTracing(device);
304 }
305
306 Main.getUI().showMessageDialog("The device will reboot. This will potentially take a "
307 + "long time. Please be patient.");
308 if (!DeviceUtils.removePreloaded(device, 15 * 60) /* 15m timeout */) {
309 Main.getUI().showMessageDialog("Removing preloaded-classes failed unexpectedly!");
310 }
311 }
312 }
313
314 public static Map<String, String> findAndGetClassData(IDevice device, String packageName)
315 throws Exception {
316 Client client = clientUtils.findClient(device, packageName, -1);
317 if (client == null) {
318 throw new RuntimeException("Could not find client...");
319 }
320 System.out.println("Found client: " + client);
321
322 return getClassDataRetriever().getClassData(client);
323 }
324
325}