summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/aapt/Images.cpp37
-rw-r--r--tools/aapt/Main.cpp6
-rw-r--r--tools/aapt/ResourceTable.cpp5
-rw-r--r--tools/aapt/XMLNode.cpp4
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Paint.java1
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java57
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java33
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java107
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java19
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java41
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java3
-rw-r--r--tools/preload/Compile.java27
-rw-r--r--tools/preload/LoadedClass.java6
-rw-r--r--tools/preload/MemoryUsage.java11
-rw-r--r--tools/preload/Operation.java18
-rw-r--r--tools/preload/Policy.java114
-rw-r--r--tools/preload/Proc.java93
-rw-r--r--tools/preload/Record.java46
-rw-r--r--tools/preload/WritePreloadedClassFile.java55
19 files changed, 518 insertions, 165 deletions
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index a516a5a..4c776fb 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -600,10 +600,22 @@ static bool patch_equals(Res_png_9patch& patch1, Res_png_9patch& patch2) {
return true;
}
-static void dump_image(int w, int h, png_bytepp rows, int bpp)
+static void dump_image(int w, int h, png_bytepp rows, int color_type)
{
int i, j, rr, gg, bb, aa;
+ int bpp;
+ if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) {
+ bpp = 1;
+ } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ bpp = 2;
+ } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
+ // We use a padding byte even when there is no alpha
+ bpp = 4;
+ } else {
+ printf("Unknown color type %d.\n", color_type);
+ }
+
for (j = 0; j < h; j++) {
png_bytep row = rows[j];
for (i = 0; i < w; i++) {
@@ -640,7 +652,7 @@ static void dump_image(int w, int h, png_bytepp rows, int bpp)
#define MAX(a,b) ((a)>(b)?(a):(b))
#define ABS(a) ((a)<0?-(a):(a))
-static void analyze_image(image_info &imageInfo, int grayscaleTolerance,
+static void analyze_image(const char *imageName, image_info &imageInfo, int grayscaleTolerance,
png_colorp rgbPalette, png_bytep alphaPalette,
int *paletteEntries, bool *hasTransparency, int *colorType,
png_bytepp outRows)
@@ -662,7 +674,7 @@ static void analyze_image(image_info &imageInfo, int grayscaleTolerance,
// 3. There are no more than 256 distinct RGBA colors
// NOISY(printf("Initial image data:\n"));
- // dump_image(w, h, imageInfo.rows, 4);
+ // dump_image(w, h, imageInfo.rows, PNG_COLOR_TYPE_RGB_ALPHA);
for (j = 0; j < h; j++) {
png_bytep row = imageInfo.rows[j];
@@ -763,7 +775,7 @@ static void analyze_image(image_info &imageInfo, int grayscaleTolerance,
*colorType = PNG_COLOR_TYPE_PALETTE;
} else {
if (maxGrayDeviation <= grayscaleTolerance) {
- NOISY(printf("Forcing image to gray (max deviation = %d)\n", maxGrayDeviation));
+ printf("%s: forcing image to gray (max deviation = %d)\n", imageName, maxGrayDeviation);
*colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
} else {
*colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
@@ -845,7 +857,7 @@ static void write_png(const char* imageName,
bool hasTransparency;
int paletteEntries;
- analyze_image(imageInfo, grayscaleTolerance, rgbPalette, alphaPalette,
+ analyze_image(imageName, imageInfo, grayscaleTolerance, rgbPalette, alphaPalette,
&paletteEntries, &hasTransparency, &color_type, outRows);
switch (color_type) {
case PNG_COLOR_TYPE_PALETTE:
@@ -910,21 +922,8 @@ static void write_png(const char* imageName,
}
png_write_image(write_ptr, rows);
-// int bpp;
-// if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) {
-// bpp = 1;
-// } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
-// bpp = 2;
-// } else if (color_type == PNG_COLOR_TYPE_RGB) {
-// bpp = 4;
-// } else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
-// bpp = 4;
-// } else {
-// printf("Uknknown color type %d, exiting.\n", color_type);
-// exit(1);
-// }
// NOISY(printf("Final image data:\n"));
-// dump_image(imageInfo.width, imageInfo.height, rows, bpp);
+// dump_image(imageInfo.width, imageInfo.height, rows, color_type);
png_write_end(write_ptr, write_info);
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index ee0dbad..71b1a3c 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -146,9 +146,11 @@ int handleCommand(Bundle* bundle)
*/
int main(int argc, char* const argv[])
{
+ char *prog = argv[0];
Bundle bundle;
bool wantUsage = false;
int result = 1; // pessimistically assume an error.
+ int tolerance = 0;
/* default to compression */
bundle.setCompressionMethod(ZipEntry::kCompressDeflated);
@@ -214,7 +216,9 @@ int main(int argc, char* const argv[])
wantUsage = true;
goto bail;
}
- bundle.setGrayscaleTolerance(atoi(argv[0]));
+ tolerance = atoi(argv[0]);
+ bundle.setGrayscaleTolerance(tolerance);
+ printf("%s: Images with deviation <= %d will be forced to grayscale.\n", prog, tolerance);
break;
case 'm':
bundle.setMakePackageDirs(true);
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index c438366..6f71a1e 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -644,6 +644,7 @@ status_t compileResourceFile(Bundle* bundle,
const String16 bool16("bool");
const String16 integer16("integer");
const String16 dimen16("dimen");
+ const String16 fraction16("fraction");
const String16 style16("style");
const String16 plurals16("plurals");
const String16 array16("array");
@@ -1022,6 +1023,10 @@ status_t compileResourceFile(Bundle* bundle,
curTag = &dimen16;
curType = dimen16;
curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_DIMENSION;
+ } else if (strcmp16(block.getElementName(&len), fraction16.string()) == 0) {
+ curTag = &fraction16;
+ curType = fraction16;
+ curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_FRACTION;
} else if (strcmp16(block.getElementName(&len), bag16.string()) == 0) {
curTag = &bag16;
curIsBag = true;
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index 2ea453c..d476567 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -220,9 +220,9 @@ moveon:
spanStack.pop();
if (empty) {
- fprintf(stderr, "%s:%d: WARNING: empty '%s' span found for at text '%s'\n",
+ fprintf(stderr, "%s:%d: WARNING: empty '%s' span found in text '%s'\n",
fileName, inXml->getLineNumber(),
- String8(*outString).string(), String8(spanTag).string());
+ String8(spanTag).string(), String8(*outString).string());
}
} else if (code == ResXMLTree::START_NAMESPACE) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint.java b/tools/layoutlib/bridge/src/android/graphics/Paint.java
index 13cc62d..ade07d6 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint.java
@@ -380,7 +380,6 @@ public class Paint extends _Original_Paint {
int filterNative = 0;
if (filter != null)
filterNative = filter.native_instance;
- native_setColorFilter(mNativePaint, filterNative);
mColorFilter = filter;
return filter;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index f5087d9..39200a1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -27,8 +27,8 @@ import com.android.layoutlib.api.IXmlPullParser;
import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo;
import com.android.layoutlib.bridge.LayoutResult.LayoutViewInfo;
import com.android.ninepatch.NinePatch;
-import com.android.tools.layoutlib.create.OverrideMethod;
import com.android.tools.layoutlib.create.MethodAdapter;
+import com.android.tools.layoutlib.create.OverrideMethod;
import android.graphics.Bitmap;
import android.graphics.Rect;
@@ -307,32 +307,33 @@ public final class Bridge implements ILayoutBridge {
projectResources.get(BridgeConstants.RES_STYLE),
frameworkResources.get(BridgeConstants.RES_STYLE), styleParentMap);
- BridgeContext context = new BridgeContext(projectKey, currentTheme, projectResources,
- frameworkResources, styleParentMap, customViewLoader, logger);
- BridgeInflater inflater = new BridgeInflater(context, customViewLoader);
- context.setBridgeInflater(inflater);
-
- IResourceValue windowBackground = null;
- int screenOffset = 0;
- if (currentTheme != null) {
- windowBackground = context.findItemInStyle(currentTheme, "windowBackground");
- windowBackground = context.resolveResValue(windowBackground);
-
- screenOffset = getScreenOffset(currentTheme, context);
- }
-
- // we need to make sure the Looper has been initialized for this thread.
- // this is required for View that creates Handler objects.
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
-
- BridgeXmlBlockParser parser = new BridgeXmlBlockParser(layoutDescription,
- context, false /* platformResourceFlag */);
-
- ViewGroup root = new FrameLayout(context);
-
+ BridgeContext context = null;
try {
+ context = new BridgeContext(projectKey, currentTheme, projectResources,
+ frameworkResources, styleParentMap, customViewLoader, logger);
+ BridgeInflater inflater = new BridgeInflater(context, customViewLoader);
+ context.setBridgeInflater(inflater);
+
+ IResourceValue windowBackground = null;
+ int screenOffset = 0;
+ if (currentTheme != null) {
+ windowBackground = context.findItemInStyle(currentTheme, "windowBackground");
+ windowBackground = context.resolveResValue(windowBackground);
+
+ screenOffset = getScreenOffset(currentTheme, context);
+ }
+
+ // we need to make sure the Looper has been initialized for this thread.
+ // this is required for View that creates Handler objects.
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ BridgeXmlBlockParser parser = new BridgeXmlBlockParser(layoutDescription,
+ context, false /* platformResourceFlag */);
+
+ ViewGroup root = new FrameLayout(context);
+
View view = inflater.inflate(parser, root);
// set the AttachInfo on the root view.
@@ -381,6 +382,10 @@ public final class Bridge implements ILayoutBridge {
return new LayoutResult(ILayoutResult.ERROR,
t.getClass().getSimpleName() + ": " + t.getMessage());
} finally {
+ // Make sure to remove static references, otherwise we could not unload the lib
+ BridgeResources.clearSystem();
+ BridgeAssetManager.clearSystem();
+
// Remove the global logger
synchronized (sDefaultLogger) {
sLogger = sDefaultLogger;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
index 02545af..1fa11af 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java
@@ -24,9 +24,36 @@ import java.util.Locale;
public class BridgeAssetManager extends AssetManager {
/**
- * Change the configuation used when retrieving resources. Not for use by
- * applications.
- * {@hide}
+ * This initializes the static field {@link AssetManager#mSystem} which is used
+ * by methods who get a global asset manager using {@link AssetManager#getSystem()}.
+ * <p/>
+ * They will end up using our bridge asset manager.
+ * <p/>
+ * {@link Bridge} calls this method after setting up a new bridge.
+ */
+ /*package*/ static AssetManager initSystem() {
+ if (!(AssetManager.mSystem instanceof BridgeAssetManager)) {
+ // Note that AssetManager() creates a system AssetManager and we override it
+ // with our BridgeAssetManager.
+ AssetManager.mSystem = new BridgeAssetManager();
+ AssetManager.mSystem.makeStringBlocks(false);
+ }
+ return AssetManager.mSystem;
+ }
+
+ /**
+ * Clears the static {@link AssetManager#mSystem} to make sure we don't leave objects
+ * around that would prevent us from unloading the library.
+ */
+ /*package*/ static void clearSystem() {
+ AssetManager.mSystem = null;
+ }
+
+ private BridgeAssetManager() {
+ }
+
+ /**
+ * Change the configuration used when retrieving resources. Not for use by applications.
*/
@Override
public void setConfiguration(int mcc, int mnc, String locale,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
new file mode 100644
index 0000000..727d6f2
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import android.content.ContentResolver;
+import android.content.ContentServiceNative;
+import android.content.Context;
+import android.content.IContentProvider;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Bundle;
+
+/**
+ * A mock content resolver for the LayoutLib Bridge.
+ * <p/>
+ * It won't serve any actual data but it's good enough for all
+ * the widgets which expect to have a content resolver available via
+ * {@link BridgeContext#getContentResolver()}.
+ */
+public class BridgeContentResolver extends ContentResolver {
+
+ public BridgeContentResolver(Context context) {
+ super(context);
+ }
+
+ @Override
+ public IContentProvider acquireProvider(Context c, String name) {
+ // ignore
+ return null;
+ }
+
+ @Override
+ public boolean releaseProvider(IContentProvider icp) {
+ // ignore
+ return false;
+ }
+
+ /**
+ * Stub for the layoutlib bridge content resolver.
+ * <p/>
+ * The super implementation accesses the {@link ContentServiceNative#getDefault()}
+ * which returns null and would make the call crash. Instead we do nothing.
+ */
+ @Override
+ public void registerContentObserver(Uri uri, boolean notifyForDescendents,
+ ContentObserver observer) {
+ // pass
+ }
+
+ /**
+ * Stub for the layoutlib bridge content resolver.
+ * <p/>
+ * The super implementation accesses the {@link ContentServiceNative#getDefault()}
+ * which returns null and would make the call crash. Instead we do nothing.
+ */
+ @Override
+ public void unregisterContentObserver(ContentObserver observer) {
+ // pass
+ }
+
+ /**
+ * Stub for the layoutlib bridge content resolver.
+ * <p/>
+ * The super implementation accesses the {@link ContentServiceNative#getDefault()}
+ * which returns null and would make the call crash. Instead we do nothing.
+ */
+ @Override
+ public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+ // pass
+ }
+
+ /**
+ * Stub for the layoutlib bridge content resolver.
+ * <p/>
+ * The super implementation accesses the {@link ContentServiceNative#getDefault()}
+ * which returns null and would make the call crash. Instead we do nothing.
+ */
+ @Override
+ public void startSync(Uri uri, Bundle extras) {
+ // pass
+ }
+
+ /**
+ * Stub for the layoutlib bridge content resolver.
+ * <p/>
+ * The super implementation accesses the {@link ContentServiceNative#getDefault()}
+ * which returns null and would make the call crash. Instead we do nothing.
+ */
+ @Override
+ public void cancelSync(Uri uri) {
+ // pass
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
index cb9509b..0df20e4 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
@@ -84,6 +84,7 @@ public final class BridgeContext extends Context {
private final IProjectCallback mProjectCallback;
private final ILayoutLog mLogger;
+ private BridgeContentResolver mContentResolver;
/**
* @param projectKey
@@ -107,9 +108,11 @@ public final class BridgeContext extends Context {
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
- mResources = new BridgeResources(
+
+ AssetManager assetManager = BridgeAssetManager.initSystem();
+ mResources = BridgeResources.initSystem(
this,
- new BridgeAssetManager(),
+ assetManager,
metrics,
config,
customViewLoader);
@@ -168,6 +171,12 @@ public final class BridgeContext extends Context {
if (LAYOUT_INFLATER_SERVICE.equals(service)) {
return mInflater;
}
+
+ // AutoCompleteTextView and MultiAutoCompleteTextView want a window
+ // service. We don't have any but it's not worth an exception.
+ if (WINDOW_SERVICE.equals(service)) {
+ return null;
+ }
throw new UnsupportedOperationException("Unsupported Service: " + service);
}
@@ -899,8 +908,10 @@ public final class BridgeContext extends Context {
@Override
public ContentResolver getContentResolver() {
- // TODO Auto-generated method stub
- return null;
+ if (mContentResolver == null) {
+ mContentResolver = new BridgeContentResolver(this);
+ }
+ return mContentResolver;
}
@Override
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java
index 6ab6e32..0bcc7fd 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java
@@ -51,11 +51,46 @@ public final class BridgeResources extends Resources {
private IProjectCallback mProjectCallback;
private boolean[] mPlatformResourceFlag = new boolean[1];
- public BridgeResources(BridgeContext context, AssetManager assets, DisplayMetrics metrics,
- Configuration config, IProjectCallback projetCallback) {
+ /**
+ * This initializes the static field {@link Resources#mSystem} which is used
+ * by methods who get global resources using {@link Resources#getSystem()}.
+ * <p/>
+ * They will end up using our bridge resources.
+ * <p/>
+ * {@link Bridge} calls this method after setting up a new bridge.
+ */
+ /*package*/ static Resources initSystem(BridgeContext context,
+ AssetManager assets,
+ DisplayMetrics metrics,
+ Configuration config,
+ IProjectCallback projectCallback) {
+ if (!(Resources.mSystem instanceof BridgeResources)) {
+ Resources.mSystem = new BridgeResources(context,
+ assets,
+ metrics,
+ config,
+ projectCallback);
+ }
+ return Resources.mSystem;
+ }
+
+ /**
+ * Clears the static {@link Resources#mSystem} to make sure we don't leave objects
+ * around that would prevent us from unloading the library.
+ */
+ /*package*/ static void clearSystem() {
+ if (Resources.mSystem instanceof BridgeResources) {
+ ((BridgeResources)(Resources.mSystem)).mContext = null;
+ ((BridgeResources)(Resources.mSystem)).mProjectCallback = null;
+ }
+ Resources.mSystem = null;
+ }
+
+ private BridgeResources(BridgeContext context, AssetManager assets, DisplayMetrics metrics,
+ Configuration config, IProjectCallback projectCallback) {
super(assets, metrics, config);
mContext = context;
- mProjectCallback = projetCallback;
+ mProjectCallback = projectCallback;
}
public BridgeTypedArray newTypeArray(int numEntries, boolean platformFile) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
index ca3c8b0..1ca3182 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
@@ -24,7 +24,8 @@ import android.widget.TextView;
/**
* Base class for mocked views.
*
- * TODO: implement onDraw and draw a rectangle in a random color with the name of the class (or better the id of the view).
+ * TODO: implement onDraw and draw a rectangle in a random color with the name of the class
+ * (or better the id of the view).
*/
public class MockView extends TextView {
diff --git a/tools/preload/Compile.java b/tools/preload/Compile.java
index 8388b12..67258ef 100644
--- a/tools/preload/Compile.java
+++ b/tools/preload/Compile.java
@@ -15,26 +15,22 @@
*/
import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.io.IOException;
import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.ObjectOutputStream;
-import java.io.BufferedOutputStream;
-import java.util.List;
+import java.io.IOException;
+import java.io.InputStreamReader;
import java.util.ArrayList;
-import java.lang.reflect.InvocationTargetException;
+import java.util.List;
/**
* Parses and analyzes a log, pulling our PRELOAD information. If you have
* an emulator or device running in the background, this class will use it
* to measure and record the memory usage of each class.
+ *
+ * TODO: Should analyze lines and select substring dynamically (instead of hardcoded 19)
*/
public class Compile {
- public static void main(String[] args)
- throws IOException, NoSuchMethodException, IllegalAccessException,
- InvocationTargetException {
+ public static void main(String[] args) throws IOException {
if (args.length != 2) {
System.err.println("Usage: Compile [log file] [output file]");
System.exit(0);
@@ -48,10 +44,17 @@ public class Compile {
new FileInputStream(args[0])));
String line;
+ int lineNumber = 0;
while ((line = in.readLine()) != null) {
+ lineNumber++;
if (line.startsWith("I/PRELOAD")) {
- line = line.substring(19);
- records.add(new Record(line));
+ try {
+ String clipped = line.substring(19);
+ records.add(new Record(clipped, lineNumber));
+ } catch (RuntimeException e) {
+ throw new RuntimeException(
+ "Exception while recording line " + lineNumber + ": " + line, e);
+ }
}
}
diff --git a/tools/preload/LoadedClass.java b/tools/preload/LoadedClass.java
index 6b3d345..5782807 100644
--- a/tools/preload/LoadedClass.java
+++ b/tools/preload/LoadedClass.java
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-import java.util.List;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Set;
-import java.util.HashSet;
-import java.io.Serializable;
/**
* A loaded class.
@@ -134,6 +133,7 @@ class LoadedClass implements Serializable, Comparable<LoadedClass> {
return name.compareTo(o.name);
}
+ @Override
public String toString() {
return name;
}
diff --git a/tools/preload/MemoryUsage.java b/tools/preload/MemoryUsage.java
index 89717eb..e5dfb2a 100644
--- a/tools/preload/MemoryUsage.java
+++ b/tools/preload/MemoryUsage.java
@@ -32,6 +32,9 @@ class MemoryUsage implements Serializable {
private static final long serialVersionUID = 0;
static final MemoryUsage NOT_AVAILABLE = new MemoryUsage();
+
+ static int errorCount = 0;
+ static final int MAXIMUM_ERRORS = 10; // give up after this many fails
final int nativeSharedPages;
final int javaSharedPages;
@@ -160,6 +163,13 @@ class MemoryUsage implements Serializable {
* Measures memory usage for the given class.
*/
static MemoryUsage forClass(String className) {
+
+ // This is a coarse approximation for determining that no device is connected,
+ // or that the communication protocol has changed, but we'll keep going and stop whining.
+ if (errorCount >= MAXIMUM_ERRORS) {
+ return NOT_AVAILABLE;
+ }
+
MeasureWithTimeout measurer = new MeasureWithTimeout(className);
new Thread(measurer).start();
@@ -237,6 +247,7 @@ class MemoryUsage implements Serializable {
if (line == null || !line.startsWith("DECAFBAD,")) {
System.err.println("Got bad response for " + className
+ ": " + line);
+ errorCount += 1;
return NOT_AVAILABLE;
}
diff --git a/tools/preload/Operation.java b/tools/preload/Operation.java
index 65c9a03..4f1938e 100644
--- a/tools/preload/Operation.java
+++ b/tools/preload/Operation.java
@@ -115,4 +115,22 @@ class Operation implements Serializable {
}
return microsInt;
}
+
+ /**
+ * Primarily for debugger support
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(type.toString());
+ sb.append(' ');
+ sb.append(loadedClass.toString());
+ if (subops.size() > 0) {
+ sb.append(" (");
+ sb.append(subops.size());
+ sb.append(" sub ops)");
+ }
+ return sb.toString();
+ }
+
}
diff --git a/tools/preload/Policy.java b/tools/preload/Policy.java
new file mode 100644
index 0000000..554966b
--- /dev/null
+++ b/tools/preload/Policy.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This is not instantiated - we just provide data for other classes to use
+ */
+public class Policy {
+
+ /**
+ * This location (in the build system) of the preloaded-classes file.
+ */
+ private static final String PRELOADED_CLASS_FILE = "frameworks/base/preloaded-classes";
+
+ /**
+ * The internal process name of the system process. Note, this also shows up as
+ * "system_process", e.g. in ddms.
+ */
+ private static final String SYSTEM_SERVER_PROCESS_NAME = "system_server";
+
+ /**
+ * Names of non-application processes - these will not be checked for preloaded classes.
+ *
+ * TODO: Replace this hardcoded list with a walk up the parent chain looking for zygote.
+ */
+ private static final Set<String> NOT_FROM_ZYGOTE = new HashSet<String>(Arrays.asList(
+ "zygote",
+ "dexopt",
+ "unknown",
+ SYSTEM_SERVER_PROCESS_NAME,
+ "com.android.development",
+ "app_process" // am & other shell commands
+ ));
+
+ /**
+ * Long running services. These are restricted in their contribution to the preloader
+ * because their launch time is less critical.
+ */
+ private static final Set<String> SERVICES = new HashSet<String>(Arrays.asList(
+ SYSTEM_SERVER_PROCESS_NAME,
+ "com.android.acore",
+ // Commented out to make sure DefaultTimeZones gets preloaded.
+ // "com.android.phone",
+ "com.google.process.content",
+ "android.process.media"
+ ));
+
+ /**
+ * Classes which we shouldn't load from the Zygote.
+ */
+ private static final Set<String> EXCLUDED_CLASSES = new HashSet<String>(Arrays.asList(
+ // Binders
+ "android.app.AlarmManager",
+ "android.app.SearchManager",
+ "android.os.FileObserver",
+ "com.android.server.PackageManagerService$AppDirObserver",
+
+ // Threads
+ "android.os.AsyncTask",
+ "android.pim.ContactsAsyncHelper",
+ "java.lang.ProcessManager"
+
+ ));
+
+ /**
+ * No constructor - use static methods only
+ */
+ private Policy() {}
+
+ /**
+ * Returns the path/file name of the preloaded classes file that will be written
+ * by WritePreloadedClassFile.
+ */
+ public static String getPreloadedClassFileName() {
+ return PRELOADED_CLASS_FILE;
+ }
+
+ /**
+ * Reports if a given process name was created from zygote
+ */
+ public static boolean isFromZygote(String processName) {
+ return !NOT_FROM_ZYGOTE.contains(processName);
+ }
+
+ /**
+ * Reports if the given process name is a "long running" process or service
+ */
+ public static boolean isService(String processName) {
+ return SERVICES.contains(processName);
+ }
+
+ /**
+ * Reports if the given class should never be preloaded
+ */
+ public static boolean isPreloadableClass(String className) {
+ return !EXCLUDED_CLASSES.contains(className);
+ }
+}
diff --git a/tools/preload/Proc.java b/tools/preload/Proc.java
index 0b27a51..22697f8 100644
--- a/tools/preload/Proc.java
+++ b/tools/preload/Proc.java
@@ -43,49 +43,6 @@ class Proc implements Serializable {
*/
static final int MAX_TO_PRELOAD = 100;
- /** Name of system server process. */
- private static final String SYSTEM_SERVER = "system_server";
-
- /** Names of non-application processes. */
- private static final Set<String> NOT_FROM_ZYGOTE
- = new HashSet<String>(Arrays.asList(
- "zygote",
- "dexopt",
- "unknown",
- SYSTEM_SERVER,
- "com.android.development",
- "app_process" // am
- ));
-
- /** Long running services. */
- private static final Set<String> SERVICES
- = new HashSet<String>(Arrays.asList(
- SYSTEM_SERVER,
- "com.android.home",
-// Commented out to make sure DefaultTimeZones gets preloaded.
-// "com.android.phone",
- "com.google.process.content",
- "com.android.process.media"
- ));
-
- /**
- * Classes which we shouldn't load from the Zygote.
- */
- static final Set<String> EXCLUDED_CLASSES
- = new HashSet<String>(Arrays.asList(
- // Binders
- "android.app.AlarmManager",
- "android.app.SearchManager",
- "android.os.FileObserver",
- "com.android.server.PackageManagerService$AppDirObserver",
-
- // Threads
- "java.lang.ProcessManager",
-
- // This class was deleted.
- "java.math.Elementary"
- ));
-
/** Parent process. */
final Proc parent;
@@ -139,17 +96,12 @@ class Proc implements Serializable {
}
/**
- * Is this a long running process?
- */
- boolean isService() {
- return SERVICES.contains(this.name);
- }
-
- /**
* Returns a list of classes which should be preloaded.
+ *
+ * @param takeAllClasses forces all classes to be taken (irrespective of ranking)
*/
- List<LoadedClass> highestRankedClasses() {
- if (NOT_FROM_ZYGOTE.contains(this.name)) {
+ List<LoadedClass> highestRankedClasses(boolean takeAllClasses) {
+ if (!isApplication()) {
return Collections.emptyList();
}
@@ -162,23 +114,30 @@ class Proc implements Serializable {
int timeToSave = totalTimeMicros() * percentageToPreload() / 100;
int timeSaved = 0;
- boolean service = isService();
+ boolean service = Policy.isService(this.name);
List<LoadedClass> highest = new ArrayList<LoadedClass>();
for (Operation operation : ranked) {
- if (highest.size() >= MAX_TO_PRELOAD) {
- System.out.println(name + " got "
- + (timeSaved * 100 / timeToSave) + "% through");
-
- break;
+
+ // These are actual ranking decisions, which can be overridden
+ if (!takeAllClasses) {
+ if (highest.size() >= MAX_TO_PRELOAD) {
+ System.out.println(name + " got "
+ + (timeSaved * 100 / timeToSave) + "% through");
+ break;
+ }
+
+ if (timeSaved >= timeToSave) {
+ break;
+ }
}
- if (timeSaved >= timeToSave) {
- break;
+ // The remaining rules apply even to wired-down processes
+ if (!Policy.isPreloadableClass(operation.loadedClass.name)) {
+ continue;
}
-
- if (EXCLUDED_CLASSES.contains(operation.loadedClass.name)
- || !operation.loadedClass.systemClass) {
+
+ if (!operation.loadedClass.systemClass) {
continue;
}
@@ -205,9 +164,13 @@ class Proc implements Serializable {
return totalTime;
}
- /** Returns true if this process is an app. */
+ /**
+ * Returns true if this process is an app.
+ *
+ * TODO: Replace the hardcoded list with a walk up the parent chain looking for zygote.
+ */
public boolean isApplication() {
- return !NOT_FROM_ZYGOTE.contains(name);
+ return Policy.isFromZygote(name);
}
/**
diff --git a/tools/preload/Record.java b/tools/preload/Record.java
index 9ffab46..b2be4d4 100644
--- a/tools/preload/Record.java
+++ b/tools/preload/Record.java
@@ -56,11 +56,14 @@ class Record {
/** Record time (ns). */
final long time;
+
+ /** Source file line# */
+ int sourceLineNumber;
/**
* Parses a line from the loaded-classes file.
*/
- Record(String line) {
+ Record(String line, int lineNum) {
char typeChar = line.charAt(0);
switch (typeChar) {
case '>': type = Type.START_LOAD; break;
@@ -70,6 +73,8 @@ class Record {
default: throw new AssertionError("Bad line: " + line);
}
+ sourceLineNumber = lineNum;
+
line = line.substring(1);
String[] parts = line.split(":");
@@ -77,20 +82,51 @@ class Record {
pid = Integer.parseInt(parts[1]);
tid = Integer.parseInt(parts[2]);
- processName = parts[3].intern();
+ processName = decode(parts[3]).intern();
classLoader = Integer.parseInt(parts[4]);
- className = vmTypeToLanguage(parts[5]).intern();
+ className = vmTypeToLanguage(decode(parts[5])).intern();
time = Long.parseLong(parts[6]);
}
+
+ /**
+ * Decode any escaping that may have been written to the log line.
+ *
+ * Supports unicode-style escaping: \\uXXXX = character in hex
+ *
+ * @param rawField the field as it was written into the log
+ * @result the same field with any escaped characters replaced
+ */
+ String decode(String rawField) {
+ String result = rawField;
+ int offset = result.indexOf("\\u");
+ while (offset >= 0) {
+ String before = result.substring(0, offset);
+ String escaped = result.substring(offset+2, offset+6);
+ String after = result.substring(offset+6);
+
+ result = String.format("%s%c%s", before, Integer.parseInt(escaped, 16), after);
+
+ // find another but don't recurse
+ offset = result.indexOf("\\u", offset + 1);
+ }
+ return result;
+ }
/**
* Converts a VM-style name to a language-style name.
*/
- static String vmTypeToLanguage(String typeName) {
+ String vmTypeToLanguage(String typeName) {
+ // if the typename is (null), just return it as-is. This is probably in dexopt and
+ // will be discarded anyway. NOTE: This corresponds to the case in dalvik/vm/oo/Class.c
+ // where dvmLinkClass() returns false and we clean up and exit.
+ if ("(null)".equals(typeName)) {
+ return typeName;
+ }
+
if (!typeName.startsWith("L") || !typeName.endsWith(";") ) {
- throw new AssertionError("Bad name: " + typeName);
+ throw new AssertionError("Bad name: " + typeName + " in line " + sourceLineNumber);
}
typeName = typeName.substring(1, typeName.length() - 1);
diff --git a/tools/preload/WritePreloadedClassFile.java b/tools/preload/WritePreloadedClassFile.java
index 5596a62..d87b1f0 100644
--- a/tools/preload/WritePreloadedClassFile.java
+++ b/tools/preload/WritePreloadedClassFile.java
@@ -14,54 +14,69 @@
* limitations under the License.
*/
-import java.io.IOException;
-import java.io.Writer;
import java.io.BufferedWriter;
-import java.io.OutputStreamWriter;
import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
import java.util.TreeSet;
-import java.util.List;
/**
- * Writes /java/android/preloaded-classes. Also updates LoadedClass.preloaded
+ * Writes /frameworks/base/preloaded-classes. Also updates LoadedClass.preloaded
* fields and writes over compiled log file.
*/
public class WritePreloadedClassFile {
- private static final String PRELOADED_CLASS_FILE
- = "java/android/preloaded-classes";
-
- public static void main(String[] args)
- throws IOException, ClassNotFoundException {
- if (args.length != 1) {
- System.err.println(
- "Usage: WritePreloadedClassFile [compiled log file]");
+ public static void main(String[] args) throws IOException, ClassNotFoundException {
+
+ // Process command-line arguments first
+ List<String> wiredProcesses = new ArrayList<String>();
+ String inputFileName = null;
+ int argOffset = 0;
+ try {
+ while ("--preload-all-process".equals(args[argOffset])) {
+ argOffset++;
+ wiredProcesses.add(args[argOffset++]);
+ }
+
+ inputFileName = args[argOffset++];
+ } catch (RuntimeException e) {
+ System.err.println("Usage: WritePreloadedClassFile " +
+ "[--preload-all-process process-name] " +
+ "[compiled log file]");
System.exit(0);
}
- Root root = Root.fromFile(args[0]);
+ Root root = Root.fromFile(inputFileName);
for (LoadedClass loadedClass : root.loadedClasses.values()) {
loadedClass.preloaded = false;
}
Writer out = new BufferedWriter(new OutputStreamWriter(
- new FileOutputStream(PRELOADED_CLASS_FILE),
+ new FileOutputStream(Policy.getPreloadedClassFileName()),
Charset.forName("US-ASCII")));
- out.write("# Classes which are preloaded by " +
- "com.android.internal.os.ZygoteInit.\n");
- out.write("# Automatically generated by /tools/preload.\n");
+ out.write("# Classes which are preloaded by com.android.internal.os.ZygoteInit.\n");
+ out.write("# Automatically generated by /frameworks/base/tools/preload.\n");
out.write("# percent=" + Proc.PERCENTAGE_TO_PRELOAD + ", weight="
+ ClassRank.SEQUENCE_WEIGHT
+ ", bucket_size=" + ClassRank.BUCKET_SIZE
+ "\n");
+ for (String wiredProcess : wiredProcesses) {
+ out.write("# forcing classes loaded by: " + wiredProcess + "\n");
+ }
Set<LoadedClass> highestRanked = new TreeSet<LoadedClass>();
for (Proc proc : root.processes.values()) {
- List<LoadedClass> highestForProc = proc.highestRankedClasses();
+ // test to see if this is one of the wired-down ("take all classes") processes
+ boolean isWired = wiredProcesses.contains(proc.name);
+
+ List<LoadedClass> highestForProc = proc.highestRankedClasses(isWired);
System.out.println(proc.name + ": " + highestForProc.size());
@@ -82,6 +97,6 @@ public class WritePreloadedClassFile {
+ " classes will be preloaded.");
// Update data to reflect LoadedClass.preloaded changes.
- root.toFile(args[0]);
+ root.toFile(inputFileName);
}
}