summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/aapt/Images.cpp49
-rw-r--r--tools/aapt/Main.cpp6
-rw-r--r--tools/aapt/Package.cpp10
-rw-r--r--tools/aapt/ResourceTable.cpp5
-rw-r--r--tools/aapt/XMLNode.cpp4
-rw-r--r--tools/aidl/aidl.cpp70
-rw-r--r--tools/aidl/options.cpp23
-rw-r--r--tools/aidl/options.h1
-rw-r--r--tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java57
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Paint.java1
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java177
-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.java38
-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/Android.mk14
-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
25 files changed, 784 insertions, 225 deletions
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index a516a5a..0a4c68b 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -332,8 +332,8 @@ static status_t do_9patch(const char* imageName, image_info* image)
int H = image->height;
int i, j;
- int maxSizeXDivs = (W / 2 + 1) * sizeof(int32_t);
- int maxSizeYDivs = (H / 2 + 1) * sizeof(int32_t);
+ int maxSizeXDivs = W * sizeof(int32_t);
+ int maxSizeYDivs = H * sizeof(int32_t);
int32_t* xDivs = (int32_t*) malloc(maxSizeXDivs);
int32_t* yDivs = (int32_t*) malloc(maxSizeYDivs);
uint8_t numXDivs = 0;
@@ -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,8 +857,16 @@ 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);
+
+ // If the image is a 9-patch, we need to preserve it as a ARGB file to make
+ // sure the pixels will not be pre-dithered/clamped until we decide they are
+ if (imageInfo.is9Patch && (color_type == PNG_COLOR_TYPE_RGB ||
+ color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE)) {
+ color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ }
+
switch (color_type) {
case PNG_COLOR_TYPE_PALETTE:
NOISY(printf("Image %s has %d colors%s, using PNG_COLOR_TYPE_PALETTE\n",
@@ -910,21 +930,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/Package.cpp b/tools/aapt/Package.cpp
index 5d9e140..eb7d6f5 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -5,6 +5,7 @@
//
#include "Main.h"
#include "AaptAssets.h"
+#include "ResourceTable.h"
#include <utils.h>
#include <utils/ZipFile.h>
@@ -190,11 +191,20 @@ bail:
ssize_t processAssets(Bundle* bundle, ZipFile* zip,
const sp<AaptAssets>& assets)
{
+ ResourceFilter filter;
+ status_t status = filter.parse(bundle->getConfigurations());
+ if (status != NO_ERROR) {
+ return -1;
+ }
+
ssize_t count = 0;
const size_t N = assets->getGroupEntries().size();
for (size_t i=0; i<N; i++) {
const AaptGroupEntry& ge = assets->getGroupEntries()[i];
+ if (!filter.match(ge.toParams())) {
+ continue;
+ }
ssize_t res = processAssets(bundle, zip, assets, ge);
if (res < 0) {
return res;
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/aidl/aidl.cpp b/tools/aidl/aidl.cpp
index dc61567..fc658f5 100644
--- a/tools/aidl/aidl.cpp
+++ b/tools/aidl/aidl.cpp
@@ -610,6 +610,62 @@ generate_dep_file(const Options& options)
}
// ==========================================================
+static string
+generate_outputFileName(const Options& options, const document_item_type* items)
+{
+ string result;
+
+ // items has already been checked to have only one interface.
+ if (items->item_type == INTERFACE_TYPE) {
+ interface_type* type = (interface_type*)items;
+
+ // create the path to the destination folder based on the
+ // interface package name
+ result = options.outputBaseFolder;
+ result += OS_PATH_SEPARATOR;
+
+ string package = type->package;
+ size_t len = package.length();
+ for (size_t i=0; i<len; i++) {
+ if (package[i] == '.') {
+ package[i] = OS_PATH_SEPARATOR;
+ }
+ }
+
+ result += package;
+
+ // add the filename by replacing the .aidl extension to .java
+ const char* p = strchr(type->name.data, '.');
+ len = p ? p-type->name.data : strlen(type->name.data);
+
+ result += OS_PATH_SEPARATOR;
+ result.append(type->name.data, len);
+ result += ".java";
+ }
+
+ return result;
+}
+
+// ==========================================================
+static void
+check_outputFileName(const string& path) {
+ size_t len = path.length();
+ for (size_t i=0; i<len ; i++) {
+ if (path[i] == OS_PATH_SEPARATOR) {
+ string p = path.substr(0, i);
+ if (access(path.data(), F_OK) != 0) {
+#ifdef HAVE_MS_C_RUNTIME
+ _mkdir(p.data());
+#else
+ mkdir(p.data(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
+#endif
+ }
+ }
+ }
+}
+
+
+// ==========================================================
static int
parse_preprocessed_file(const string& filename)
{
@@ -617,7 +673,7 @@ parse_preprocessed_file(const string& filename)
FILE* f = fopen(filename.c_str(), "rb");
if (f == NULL) {
- fprintf(stderr, "aidl: can't open preprocessd file: %s\n",
+ fprintf(stderr, "aidl: can't open preprocessed file: %s\n",
filename.c_str());
return 1;
}
@@ -804,7 +860,17 @@ compile_aidl(const Options& options)
generate_dep_file(options);
}
- err = generate_java(options.outputFileName, options.inputFileName.c_str(),
+ // if needed, generate the outputFileName from the outputBaseFolder
+ string outputFileName = options.outputFileName;
+ if (outputFileName.length() == 0 &&
+ options.outputBaseFolder.length() > 0) {
+ outputFileName = generate_outputFileName(options, mainDoc);
+ }
+
+ // make sure the folders of the output file all exists
+ check_outputFileName(outputFileName);
+
+ err = generate_java(outputFileName, options.inputFileName.c_str(),
(interface_type*)mainDoc);
return err;
diff --git a/tools/aidl/options.cpp b/tools/aidl/options.cpp
index 57b10ae..0aa7db2 100644
--- a/tools/aidl/options.cpp
+++ b/tools/aidl/options.cpp
@@ -13,16 +13,19 @@ usage()
" aidl --preprocess OUTPUT INPUT...\n"
"\n"
"OPTIONS:\n"
- " -I<DIR> search path for import statements.\n"
- " -d<FILE> generate dependency file.\n"
- " -p<FILE> file created by --preprocess to import.\n"
- " -b fail when trying to compile a parcelable.\n"
+ " -I<DIR> search path for import statements.\n"
+ " -d<FILE> generate dependency file.\n"
+ " -p<FILE> file created by --preprocess to import.\n"
+ " -o<FOLDER> base output folder for generated files.\n"
+ " -b fail when trying to compile a parcelable.\n"
"\n"
"INPUT:\n"
" An aidl interface file.\n"
"\n"
"OUTPUT:\n"
- " The generated interface files. If omitted, the input filename is used, with the .aidl extension changed to a .java extension.\n"
+ " The generated interface files.\n"
+ " If omitted and the -o option is not used, the input filename is used, with the .aidl extension changed to a .java extension.\n"
+ " If the -o option is used, the generated files will be placed in the base output folder, under their package folder\n"
);
return 1;
}
@@ -78,6 +81,14 @@ parse_options(int argc, const char* const* argv, Options *options)
return usage();
}
}
+ else if (s[1] == 'o') {
+ if (len > 2) {
+ options->outputBaseFolder = s+2;
+ } else {
+ fprintf(stderr, "-o option (%d) requires a path.\n", i);
+ return usage();
+ }
+ }
else if (len == 2 && s[1] == 'b') {
options->failOnParcelable = true;
}
@@ -111,7 +122,7 @@ parse_options(int argc, const char* const* argv, Options *options)
if (i < argc) {
options->outputFileName = argv[i];
i++;
- } else {
+ } else if (options->outputBaseFolder.length() == 0) {
// copy input into output and change the extension from .aidl to .java
options->outputFileName = options->inputFileName;
string::size_type pos = options->outputFileName.size()-5;
diff --git a/tools/aidl/options.h b/tools/aidl/options.h
index e7e62ec..d88d988 100644
--- a/tools/aidl/options.h
+++ b/tools/aidl/options.h
@@ -21,6 +21,7 @@ struct Options
vector<string> preprocessedFiles;
string inputFileName;
string outputFileName;
+ string outputBaseFolder;
string depFileName;
vector<string> filesToPreprocess;
diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java
index 0810d29..df1876d 100644
--- a/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java
+++ b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java
@@ -24,28 +24,35 @@ import java.util.Map;
* <p/>
* <p/>{@link #getApiLevel()} gives the ability to know which methods are available.
* <p/>
+ * Changes in API level 3:
+ * <ul>
+ * <li>{@link #computeLayout(IXmlPullParser, Object, int, int, int, float, float, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
+ * <li> deprecated {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
+ * </ul>
* Changes in API level 2:
* <ul>
* <li>{@link #getApiLevel()}</li>
- * <li>{@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)},
- * deprecated {@link #computeLayout(IXmlPullParser, Object, int, int, String, Map, Map, IProjectCallback, ILayoutLog)}.</li>
+ * <li>{@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
+ * <li>deprecated {@link #computeLayout(IXmlPullParser, Object, int, int, String, Map, Map, IProjectCallback, ILayoutLog)}</li>
* </ul>
*/
public interface ILayoutBridge {
- final int API_CURRENT = 2;
+ final int API_CURRENT = 3;
/**
* Returns the API level of the layout library.
* While no methods will ever be removed, some may become deprecated, and some new ones
* will appear.
+ * <p/>If calling this method throws an {@link AbstractMethodError}, then the API level
+ * should be considered to be 1.
*/
int getApiLevel();
/**
* Initializes the Bridge object.
* @param fontOsLocation the location of the fonts.
- * @param enumValueMap map attrName => { map enumFlagName => Integer value }.
+ * @param enumValueMap map attrName => { map enumFlagName => Integer value }.
* @return true if success.
* @since 1
*/
@@ -56,8 +63,11 @@ public interface ILayoutBridge {
* @param layoutDescription the {@link IXmlPullParser} letting the LayoutLib Bridge visit the
* layout file.
* @param projectKey An Object identifying the project. This is used for the cache mechanism.
- * @param screenWidth
- * @param screenHeight
+ * @param screenWidth the screen width
+ * @param screenHeight the screen height
+ * @param density the density factor for the screen.
+ * @param xdpi the screen actual dpi in X
+ * @param ydpi the screen actual dpi in Y
* @param themeName The name of the theme to use.
* @param isProjectTheme true if the theme is a project theme, false if it is a framework theme.
* @param projectResources the resources of the project. The map contains (String, map) pairs
@@ -72,8 +82,41 @@ public interface ILayoutBridge {
* the project.
* @param logger the object responsible for displaying warning/errors to the user.
* @return an {@link ILayoutResult} object that contains the result of the layout.
+ * @since 3
+ */
+ ILayoutResult computeLayout(IXmlPullParser layoutDescription,
+ Object projectKey,
+ int screenWidth, int screenHeight, int density, float xdpi, float ydpi,
+ String themeName, boolean isProjectTheme,
+ Map<String, Map<String, IResourceValue>> projectResources,
+ Map<String, Map<String, IResourceValue>> frameworkResources,
+ IProjectCallback projectCallback, ILayoutLog logger);
+
+ /**
+ * Computes and renders a layout
+ * @param layoutDescription the {@link IXmlPullParser} letting the LayoutLib Bridge visit the
+ * layout file.
+ * @param projectKey An Object identifying the project. This is used for the cache mechanism.
+ * @param screenWidth the screen width
+ * @param screenHeight the screen height
+ * @param themeName The name of the theme to use.
+ * @param isProjectTheme true if the theme is a project theme, false if it is a framework theme.
+ * @param projectResources the resources of the project. The map contains (String, map) pairs
+ * where the string is the type of the resource reference used in the layout file, and the
+ * map contains (String, {@link IResourceValue}) pairs where the key is the resource name,
+ * and the value is the resource value.
+ * @param frameworkResources the framework resources. The map contains (String, map) pairs
+ * where the string is the type of the resource reference used in the layout file, and the map
+ * contains (String, {@link IResourceValue}) pairs where the key is the resource name, and the
+ * value is the resource value.
+ * @param projectCallback The {@link IProjectCallback} object to get information from
+ * the project.
+ * @param logger the object responsible for displaying warning/errors to the user.
+ * @return an {@link ILayoutResult} object that contains the result of the layout.
+ * @deprecated Use {@link #computeLayout(IXmlPullParser, Object, int, int, int, float, float, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}
* @since 2
*/
+ @Deprecated
ILayoutResult computeLayout(IXmlPullParser layoutDescription,
Object projectKey,
int screenWidth, int screenHeight, String themeName, boolean isProjectTheme,
@@ -102,7 +145,7 @@ public interface ILayoutBridge {
* the project.
* @param logger the object responsible for displaying warning/errors to the user.
* @return an {@link ILayoutResult} object that contains the result of the layout.
- * @deprecated Use {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}.
+ * @deprecated Use {@link #computeLayout(IXmlPullParser, Object, int, int, int, float, float, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}
* @since 1
*/
@Deprecated
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..47a7ec0 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;
@@ -40,6 +40,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.BridgeInflater;
import android.view.IWindow;
@@ -55,6 +56,7 @@ import android.view.View.MeasureSpec;
import android.view.WindowManager.LayoutParams;
import android.widget.FrameLayout;
+import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
@@ -72,6 +74,8 @@ public final class Bridge implements ILayoutBridge {
private static final int DEFAULT_STATUS_BAR_HEIGHT = 25;
public static class StaticMethodNotImplementedException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
public StaticMethodNotImplementedException(String msg) {
super(msg);
}
@@ -91,14 +95,15 @@ public final class Bridge implements ILayoutBridge {
private final static Map<String, Map<String, Integer>> sRFullMap =
new HashMap<String, Map<String,Integer>>();
- private final static Map<Object, Map<String, Bitmap>> sProjectBitmapCache =
- new HashMap<Object, Map<String, Bitmap>>();
- private final static Map<Object, Map<String, NinePatch>> sProject9PatchCache =
- new HashMap<Object, Map<String, NinePatch>>();
-
- private final static Map<String, Bitmap> sFrameworkBitmapCache = new HashMap<String, Bitmap>();
- private final static Map<String, NinePatch> sFramework9PatchCache =
- new HashMap<String, NinePatch>();
+ private final static Map<Object, Map<String, SoftReference<Bitmap>>> sProjectBitmapCache =
+ new HashMap<Object, Map<String, SoftReference<Bitmap>>>();
+ private final static Map<Object, Map<String, SoftReference<NinePatch>>> sProject9PatchCache =
+ new HashMap<Object, Map<String, SoftReference<NinePatch>>>();
+
+ private final static Map<String, SoftReference<Bitmap>> sFrameworkBitmapCache =
+ new HashMap<String, SoftReference<Bitmap>>();
+ private final static Map<String, SoftReference<NinePatch>> sFramework9PatchCache =
+ new HashMap<String, SoftReference<NinePatch>>();
private static Map<String, Map<String, Integer>> sEnumValueMap;
@@ -277,20 +282,40 @@ public final class Bridge implements ILayoutBridge {
isProjectTheme = true;
}
- return computeLayout(layoutDescription, projectKey, screenWidth, screenHeight, themeName, isProjectTheme,
+ return computeLayout(layoutDescription, projectKey,
+ screenWidth, screenHeight, DisplayMetrics.DEFAULT_DENSITY,
+ DisplayMetrics.DEFAULT_DENSITY, DisplayMetrics.DEFAULT_DENSITY,
+ themeName, isProjectTheme,
projectResources, frameworkResources, customViewLoader, logger);
}
/*
+ * For compatilibty purposes, we implement the old deprecated version of computeLayout.
* (non-Javadoc)
* @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog)
*/
- public ILayoutResult computeLayout(IXmlPullParser layoutDescription,
- Object projectKey,
+ public ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey,
int screenWidth, int screenHeight, String themeName, boolean isProjectTheme,
Map<String, Map<String, IResourceValue>> projectResources,
Map<String, Map<String, IResourceValue>> frameworkResources,
IProjectCallback customViewLoader, ILayoutLog logger) {
+ return computeLayout(layoutDescription, projectKey,
+ screenWidth, screenHeight, DisplayMetrics.DEFAULT_DENSITY,
+ DisplayMetrics.DEFAULT_DENSITY, DisplayMetrics.DEFAULT_DENSITY,
+ themeName, isProjectTheme,
+ projectResources, frameworkResources, customViewLoader, logger);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, int, float, float, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog)
+ */
+ public ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey,
+ int screenWidth, int screenHeight, int density, float xdpi, float ydpi,
+ String themeName, boolean isProjectTheme,
+ Map<String, Map<String, IResourceValue>> projectResources,
+ Map<String, Map<String, IResourceValue>> frameworkResources,
+ IProjectCallback customViewLoader, ILayoutLog logger) {
if (logger == null) {
logger = sDefaultLogger;
}
@@ -298,7 +323,7 @@ public final class Bridge implements ILayoutBridge {
synchronized (sDefaultLogger) {
sLogger = logger;
}
-
+
// find the current theme and compute the style inheritance map
Map<IStyleResourceValue, IStyleResourceValue> styleParentMap =
new HashMap<IStyleResourceValue, IStyleResourceValue>();
@@ -307,32 +332,42 @@ 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 {
+ // setup the display Metrics.
+ DisplayMetrics metrics = new DisplayMetrics();
+ metrics.density = density / (float) DisplayMetrics.DEFAULT_DENSITY;
+ metrics.scaledDensity = metrics.density;
+ metrics.widthPixels = screenWidth;
+ metrics.heightPixels = screenHeight;
+ metrics.xdpi = xdpi;
+ metrics.ydpi = ydpi;
+
+ context = new BridgeContext(projectKey, metrics, 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 +416,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;
@@ -680,15 +719,21 @@ public final class Bridge implements ILayoutBridge {
*/
static Bitmap getCachedBitmap(String value, Object projectKey) {
if (projectKey != null) {
- Map<String, Bitmap> map = sProjectBitmapCache.get(projectKey);
+ Map<String, SoftReference<Bitmap>> map = sProjectBitmapCache.get(projectKey);
if (map != null) {
- return map.get(value);
+ SoftReference<Bitmap> ref = map.get(value);
+ if (ref != null) {
+ return ref.get();
+ }
+ }
+ } else {
+ SoftReference<Bitmap> ref = sFrameworkBitmapCache.get(value);
+ if (ref != null) {
+ return ref.get();
}
-
- return null;
}
-
- return sFrameworkBitmapCache.get(value);
+
+ return null;
}
/**
@@ -699,17 +744,17 @@ public final class Bridge implements ILayoutBridge {
*/
static void setCachedBitmap(String value, Bitmap bmp, Object projectKey) {
if (projectKey != null) {
- Map<String, Bitmap> map = sProjectBitmapCache.get(projectKey);
+ Map<String, SoftReference<Bitmap>> map = sProjectBitmapCache.get(projectKey);
if (map == null) {
- map = new HashMap<String, Bitmap>();
+ map = new HashMap<String, SoftReference<Bitmap>>();
sProjectBitmapCache.put(projectKey, map);
}
- map.put(value, bmp);
+ map.put(value, new SoftReference<Bitmap>(bmp));
+ } else {
+ sFrameworkBitmapCache.put(value, new SoftReference<Bitmap>(bmp));
}
-
- sFrameworkBitmapCache.put(value, bmp);
}
/**
@@ -721,16 +766,22 @@ public final class Bridge implements ILayoutBridge {
*/
static NinePatch getCached9Patch(String value, Object projectKey) {
if (projectKey != null) {
- Map<String, NinePatch> map = sProject9PatchCache.get(projectKey);
+ Map<String, SoftReference<NinePatch>> map = sProject9PatchCache.get(projectKey);
if (map != null) {
- return map.get(value);
+ SoftReference<NinePatch> ref = map.get(value);
+ if (ref != null) {
+ return ref.get();
+ }
+ }
+ } else {
+ SoftReference<NinePatch> ref = sFramework9PatchCache.get(value);
+ if (ref != null) {
+ return ref.get();
}
-
- return null;
}
- return sFramework9PatchCache.get(value);
+ return null;
}
/**
@@ -741,19 +792,19 @@ public final class Bridge implements ILayoutBridge {
*/
static void setCached9Patch(String value, NinePatch ninePatch, Object projectKey) {
if (projectKey != null) {
- Map<String, NinePatch> map = sProject9PatchCache.get(projectKey);
+ Map<String, SoftReference<NinePatch>> map = sProject9PatchCache.get(projectKey);
if (map == null) {
- map = new HashMap<String, NinePatch>();
+ map = new HashMap<String, SoftReference<NinePatch>>();
sProject9PatchCache.put(projectKey, map);
}
- map.put(value, ninePatch);
+ map.put(value, new SoftReference<NinePatch>(ninePatch));
+ } else {
+ sFramework9PatchCache.put(value, new SoftReference<NinePatch>(ninePatch));
}
-
- sFramework9PatchCache.put(value, ninePatch);
}
-
+
/**
* Implementation of {@link IWindowSession} so that mSession is not null in
* the {@link SurfaceView}.
@@ -784,6 +835,12 @@ public final class Bridge implements ILayoutBridge {
}
@SuppressWarnings("unused")
+ public boolean performHapticFeedback(IWindow window, int effectId, boolean always) {
+ // pass for now.
+ return false;
+ }
+
+ @SuppressWarnings("unused")
public MotionEvent getPendingPointerMove(IWindow arg0) throws RemoteException {
// pass for now.
return null;
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..baa3d53 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
@@ -84,16 +84,24 @@ public final class BridgeContext extends Context {
private final IProjectCallback mProjectCallback;
private final ILayoutLog mLogger;
+ private BridgeContentResolver mContentResolver;
/**
- * @param projectKey
- * @param currentTheme
- * @param projectResources
- * @param frameworkResources
+ * @param projectKey An Object identifying the project. This is used for the cache mechanism.
+ * @param metrics the {@link DisplayMetrics}.
+ * @param themeName The name of the theme to use.
+ * @param projectResources the resources of the project. The map contains (String, map) pairs
+ * where the string is the type of the resource reference used in the layout file, and the
+ * map contains (String, {@link IResourceValue}) pairs where the key is the resource name,
+ * and the value is the resource value.
+ * @param frameworkResources the framework resources. The map contains (String, map) pairs
+ * where the string is the type of the resource reference used in the layout file, and the map
+ * contains (String, {@link IResourceValue}) pairs where the key is the resource name, and the
+ * value is the resource value.
* @param styleInheritanceMap
* @param customViewLoader
*/
- public BridgeContext(Object projectKey,
+ public BridgeContext(Object projectKey, DisplayMetrics metrics,
IStyleResourceValue currentTheme,
Map<String, Map<String, IResourceValue>> projectResources,
Map<String, Map<String, IResourceValue>> frameworkResources,
@@ -104,12 +112,10 @@ public final class BridgeContext extends Context {
mLogger = logger;
Configuration config = new Configuration();
- 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 +174,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 +911,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/Android.mk b/tools/preload/Android.mk
index d3457fe..e6fa103 100644
--- a/tools/preload/Android.mk
+++ b/tools/preload/Android.mk
@@ -2,7 +2,19 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := *.java
+LOCAL_SRC_FILES := \
+ ClassRank.java \
+ Compile.java \
+ LoadedClass.java \
+ MemoryUsage.java \
+ Operation.java \
+ Policy.java \
+ PrintCsv.java \
+ PrintPsTree.java \
+ Proc.java \
+ Record.java \
+ Root.java \
+ WritePreloadedClassFile.java
LOCAL_MODULE:= preload
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);
}
}