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; |
| 32 | import com.android.preload.classdataretrieval.ClassDataRetriever; |
| 33 | import com.android.preload.classdataretrieval.hprof.Hprof; |
| 34 | import com.android.preload.classdataretrieval.jdwp.JDWPClassDataRetriever; |
| 35 | import com.android.preload.ui.UI; |
| 36 | |
| 37 | import java.util.ArrayList; |
| 38 | import java.util.Collection; |
| 39 | import java.util.List; |
| 40 | import java.util.Map; |
| 41 | |
| 42 | import javax.swing.Action; |
| 43 | import javax.swing.DefaultListModel; |
| 44 | |
| 45 | public class Main { |
| 46 | |
| 47 | /** |
| 48 | * Enable tracing mode. This is a work-in-progress to derive compiled-methods data, so it is |
| 49 | * off for now. |
| 50 | */ |
| 51 | public final static boolean ENABLE_TRACING = false; |
| 52 | |
| 53 | /** |
| 54 | * Ten-second timeout. |
| 55 | */ |
| 56 | public final static int DEFAULT_TIMEOUT_MILLIS = 10 * 1000; |
| 57 | |
| 58 | /** |
| 59 | * Hprof timeout. Two minutes. |
| 60 | */ |
| 61 | public final static int HPROF_TIMEOUT_MILLIS = 120 * 1000; |
| 62 | |
| 63 | private IDevice device; |
| 64 | private static ClientUtils clientUtils; |
| 65 | |
| 66 | private DumpTableModel dataTableModel; |
| 67 | private DefaultListModel<Client> clientListModel; |
| 68 | |
| 69 | private UI ui; |
| 70 | |
| 71 | // Actions that need to be updated once a device is selected. |
| 72 | private Collection<DeviceSpecific> deviceSpecificActions; |
| 73 | |
| 74 | // Current main instance. |
| 75 | private static Main top; |
| 76 | private static boolean useJdwpClassDataRetriever = false; |
| 77 | |
| 78 | public final static String CLASS_PRELOAD_BLACKLIST = "android.app.AlarmManager$" + "|" |
| 79 | + "android.app.SearchManager$" + "|" + "android.os.FileObserver$" + "|" |
| 80 | + "com.android.server.PackageManagerService\\$AppDirObserver$" + "|" + |
| 81 | |
| 82 | |
| 83 | // Threads |
| 84 | "android.os.AsyncTask$" + "|" + "android.pim.ContactsAsyncHelper$" + "|" |
| 85 | + "android.webkit.WebViewClassic\\$1$" + "|" + "java.lang.ProcessManager$" + "|" |
| 86 | + "(.*\\$NoPreloadHolder$)"; |
| 87 | |
| 88 | /** |
| 89 | * @param args |
| 90 | */ |
| 91 | public static void main(String[] args) { |
| 92 | Main m = new Main(); |
| 93 | top = m; |
| 94 | |
| 95 | m.startUp(); |
| 96 | } |
| 97 | |
| 98 | public Main() { |
| 99 | clientListModel = new DefaultListModel<Client>(); |
| 100 | dataTableModel = new DumpTableModel(); |
| 101 | |
| 102 | clientUtils = new ClientUtils(DEFAULT_TIMEOUT_MILLIS); // Client utils with 10s timeout. |
| 103 | |
| 104 | List<Action> actions = new ArrayList<Action>(); |
| 105 | actions.add(new ReloadListAction(clientUtils, null, clientListModel)); |
| 106 | actions.add(new ClearTableAction(dataTableModel)); |
| 107 | actions.add(new RunMonkeyAction(null, dataTableModel)); |
| 108 | actions.add(new ScanPackageAction(clientUtils, null, dataTableModel)); |
| 109 | actions.add(new ScanAllPackagesAction(clientUtils, null, dataTableModel)); |
| 110 | actions.add(new ComputeThresholdAction("Compute preloaded-classes", dataTableModel, 2, |
| 111 | CLASS_PRELOAD_BLACKLIST)); |
| 112 | actions.add(new ComputeThresholdAction("Compute compiled-classes", dataTableModel, 1, |
| 113 | null)); |
| 114 | actions.add(new ComputeThresholdXAction("Compute(X)", dataTableModel, |
| 115 | CLASS_PRELOAD_BLACKLIST)); |
| 116 | actions.add(new ShowDataAction(dataTableModel)); |
| 117 | actions.add(new ImportAction(dataTableModel)); |
| 118 | actions.add(new ExportAction(dataTableModel)); |
| 119 | |
| 120 | deviceSpecificActions = new ArrayList<DeviceSpecific>(); |
| 121 | for (Action a : actions) { |
| 122 | if (a instanceof DeviceSpecific) { |
| 123 | deviceSpecificActions.add((DeviceSpecific)a); |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | ui = new UI(clientListModel, dataTableModel, actions); |
| 128 | ui.setVisible(true); |
| 129 | } |
| 130 | |
| 131 | public static UI getUI() { |
| 132 | return top.ui; |
| 133 | } |
| 134 | |
| 135 | public static ClassDataRetriever getClassDataRetriever() { |
| 136 | if (useJdwpClassDataRetriever) { |
| 137 | return new JDWPClassDataRetriever(); |
| 138 | } else { |
| 139 | return new Hprof(HPROF_TIMEOUT_MILLIS); |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | public IDevice getDevice() { |
| 144 | return device; |
| 145 | } |
| 146 | |
| 147 | public void setDevice(IDevice device) { |
| 148 | this.device = device; |
| 149 | for (DeviceSpecific ds : deviceSpecificActions) { |
| 150 | ds.setDevice(device); |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | public DefaultListModel<Client> getClientListModel() { |
| 155 | return clientListModel; |
| 156 | } |
| 157 | |
| 158 | static class DeviceWrapper { |
| 159 | IDevice device; |
| 160 | |
| 161 | public DeviceWrapper(IDevice d) { |
| 162 | device = d; |
| 163 | } |
| 164 | |
| 165 | @Override |
| 166 | public String toString() { |
| 167 | return device.getName() + " (#" + device.getSerialNumber() + ")"; |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | private void startUp() { |
| 172 | getUI().showWaitDialog(); |
| 173 | initDevice(); |
| 174 | |
| 175 | // Load clients. |
| 176 | new ReloadListAction(clientUtils, getDevice(), clientListModel).run(); |
| 177 | |
| 178 | getUI().hideWaitDialog(); |
| 179 | } |
| 180 | |
| 181 | private void initDevice() { |
| 182 | DeviceUtils.init(DEFAULT_TIMEOUT_MILLIS); |
| 183 | |
| 184 | IDevice devices[] = DeviceUtils.findDevices(DEFAULT_TIMEOUT_MILLIS); |
| 185 | if (devices == null || devices.length == 0) { |
| 186 | throw new RuntimeException("Could not find any devices..."); |
| 187 | } |
| 188 | |
| 189 | getUI().hideWaitDialog(); |
| 190 | |
| 191 | DeviceWrapper deviceWrappers[] = new DeviceWrapper[devices.length]; |
| 192 | for (int i = 0; i < devices.length; i++) { |
| 193 | deviceWrappers[i] = new DeviceWrapper(devices[i]); |
| 194 | } |
| 195 | |
| 196 | DeviceWrapper ret = Main.getUI().showChoiceDialog("Choose a device", "Choose device", |
| 197 | deviceWrappers); |
| 198 | if (ret != null) { |
| 199 | setDevice(ret.device); |
| 200 | } else { |
| 201 | System.exit(0); |
| 202 | } |
| 203 | |
| 204 | boolean prepare = Main.getUI().showConfirmDialog("Prepare device?", |
| 205 | "Do you want to prepare the device? This is highly recommended."); |
| 206 | if (prepare) { |
| 207 | String buildType = DeviceUtils.getBuildType(device); |
| 208 | if (buildType == null || (!buildType.equals("userdebug") && !buildType.equals("eng"))) { |
| 209 | Main.getUI().showMessageDialog("Need a userdebug or eng build! (Found " + buildType |
| 210 | + ")"); |
| 211 | return; |
| 212 | } |
| 213 | if (DeviceUtils.hasPrebuiltBootImage(device)) { |
| 214 | Main.getUI().showMessageDialog("Cannot prepare a device with pre-optimized boot " |
| 215 | + "image!"); |
| 216 | return; |
| 217 | } |
| 218 | |
| 219 | if (ENABLE_TRACING) { |
| 220 | DeviceUtils.enableTracing(device); |
| 221 | } |
| 222 | |
| 223 | Main.getUI().showMessageDialog("The device will reboot. This will potentially take a " |
| 224 | + "long time. Please be patient."); |
| 225 | if (!DeviceUtils.removePreloaded(device, 15 * 60) /* 15m timeout */) { |
| 226 | Main.getUI().showMessageDialog("Removing preloaded-classes failed unexpectedly!"); |
| 227 | } |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | public static Map<String, String> findAndGetClassData(IDevice device, String packageName) |
| 232 | throws Exception { |
| 233 | Client client = clientUtils.findClient(device, packageName, -1); |
| 234 | if (client == null) { |
| 235 | throw new RuntimeException("Could not find client..."); |
| 236 | } |
| 237 | System.out.println("Found client: " + client); |
| 238 | |
| 239 | return getClassDataRetriever().getClassData(client); |
| 240 | } |
| 241 | |
| 242 | } |