//
#include "AaptAssets.h"
+#include "ResourceFilter.h"
#include "Main.h"
#include <utils/misc.h>
static const char* kWildcardName = "any";
static const char* kAssetDir = "assets";
static const char* kResourceDir = "res";
+static const char* kValuesDir = "values";
+static const char* kMipmapDir = "mipmap";
static const char* kInvalidChars = "/\\:";
static const size_t kMaxAssetFileName = 100;
return 0;
}
+ // smallest screen dp width
+ if (getSmallestScreenWidthDpName(part.string(), &config)) {
+ *axis = AXIS_SMALLESTSCREENWIDTHDP;
+ *value = config.smallestScreenWidthDp;
+ return 0;
+ }
+
+ // screen dp width
+ if (getScreenWidthDpName(part.string(), &config)) {
+ *axis = AXIS_SCREENWIDTHDP;
+ *value = config.screenWidthDp;
+ return 0;
+ }
+
+ // screen dp height
+ if (getScreenHeightDpName(part.string(), &config)) {
+ *axis = AXIS_SCREENHEIGHTDP;
+ *value = config.screenHeightDp;
+ return 0;
+ }
+
// screen layout size
if (getScreenLayoutSizeName(part.string(), &config)) {
*axis = AXIS_SCREENLAYOUTSIZE;
return 1;
}
+uint32_t
+AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
+{
+ switch (axis) {
+ case AXIS_MCC:
+ return config.mcc;
+ case AXIS_MNC:
+ return config.mnc;
+ case AXIS_LANGUAGE:
+ return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16)
+ | (((uint32_t)config.language[1]) << 8) | (config.language[0]);
+ case AXIS_SCREENLAYOUTSIZE:
+ return config.screenLayout&ResTable_config::MASK_SCREENSIZE;
+ case AXIS_ORIENTATION:
+ return config.orientation;
+ case AXIS_UIMODETYPE:
+ return (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
+ case AXIS_UIMODENIGHT:
+ return (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
+ case AXIS_DENSITY:
+ return config.density;
+ case AXIS_TOUCHSCREEN:
+ return config.touchscreen;
+ case AXIS_KEYSHIDDEN:
+ return config.inputFlags;
+ case AXIS_KEYBOARD:
+ return config.keyboard;
+ case AXIS_NAVIGATION:
+ return config.navigation;
+ case AXIS_SCREENSIZE:
+ return config.screenSize;
+ case AXIS_SMALLESTSCREENWIDTHDP:
+ return config.smallestScreenWidthDp;
+ case AXIS_SCREENWIDTHDP:
+ return config.screenWidthDp;
+ case AXIS_SCREENHEIGHTDP:
+ return config.screenHeightDp;
+ case AXIS_VERSION:
+ return config.version;
+ }
+ return 0;
+}
+
+bool
+AaptGroupEntry::configSameExcept(const ResTable_config& config,
+ const ResTable_config& otherConfig, int axis)
+{
+ for (int i=AXIS_START; i<=AXIS_END; i++) {
+ if (i == axis) {
+ continue;
+ }
+ if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) {
+ return false;
+ }
+ }
+ return true;
+}
+
bool
AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
{
+ mParamsChanged = true;
+
Vector<String8> parts;
String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
String8 touch, key, keysHidden, nav, navHidden, size, vers;
- String8 uiModeType, uiModeNight;
+ String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
const char *p = dir;
const char *q;
//printf("not region: %s\n", part.string());
}
+ if (getSmallestScreenWidthDpName(part.string())) {
+ smallestwidthdp = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not smallest screen width dp: %s\n", part.string());
+ }
+
+ if (getScreenWidthDpName(part.string())) {
+ widthdp = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not screen width dp: %s\n", part.string());
+ }
+
+ if (getScreenHeightDpName(part.string())) {
+ heightdp = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not screen height dp: %s\n", part.string());
+ }
+
if (getScreenLayoutSizeName(part.string())) {
layoutsize = part;
this->locale = loc;
this->screenLayoutSize = layoutsize;
this->screenLayoutLong = layoutlong;
+ this->smallestScreenWidthDp = smallestwidthdp;
+ this->screenWidthDp = widthdp;
+ this->screenHeightDp = heightdp;
this->orientation = orient;
this->uiModeType = uiModeType;
this->uiModeNight = uiModeNight;
s += ",";
s += this->locale;
s += ",";
+ s += smallestScreenWidthDp;
+ s += ",";
+ s += screenWidthDp;
+ s += ",";
+ s += screenHeightDp;
+ s += ",";
s += screenLayoutSize;
s += ",";
s += screenLayoutLong;
{
String8 s = resType;
if (this->mcc != "") {
- s += "-";
+ if (s.length() > 0) {
+ s += "-";
+ }
s += mcc;
}
if (this->mnc != "") {
- s += "-";
+ if (s.length() > 0) {
+ s += "-";
+ }
s += mnc;
}
if (this->locale != "") {
- s += "-";
+ if (s.length() > 0) {
+ s += "-";
+ }
s += locale;
}
+ if (this->smallestScreenWidthDp != "") {
+ if (s.length() > 0) {
+ s += "-";
+ }
+ s += smallestScreenWidthDp;
+ }
+ if (this->screenWidthDp != "") {
+ if (s.length() > 0) {
+ s += "-";
+ }
+ s += screenWidthDp;
+ }
+ if (this->screenHeightDp != "") {
+ if (s.length() > 0) {
+ s += "-";
+ }
+ s += screenHeightDp;
+ }
if (this->screenLayoutSize != "") {
- s += "-";
+ if (s.length() > 0) {
+ s += "-";
+ }
s += screenLayoutSize;
}
if (this->screenLayoutLong != "") {
- s += "-";
+ if (s.length() > 0) {
+ s += "-";
+ }
s += screenLayoutLong;
}
if (this->orientation != "") {
- s += "-";
+ if (s.length() > 0) {
+ s += "-";
+ }
s += orientation;
}
if (this->uiModeType != "") {
- s += "-";
+ if (s.length() > 0) {
+ s += "-";
+ }
s += uiModeType;
}
if (this->uiModeNight != "") {
- s += "-";
+ if (s.length() > 0) {
+ s += "-";
+ }
s += uiModeNight;
}
if (this->density != "") {
- s += "-";
+ if (s.length() > 0) {
+ s += "-";
+ }
s += density;
}
if (this->touchscreen != "") {
- s += "-";
+ if (s.length() > 0) {
+ s += "-";
+ }
s += touchscreen;
}
if (this->keysHidden != "") {
- s += "-";
+ if (s.length() > 0) {
+ s += "-";
+ }
s += keysHidden;
}
if (this->keyboard != "") {
- s += "-";
+ if (s.length() > 0) {
+ s += "-";
+ }
s += keyboard;
}
if (this->navHidden != "") {
- s += "-";
+ if (s.length() > 0) {
+ s += "-";
+ }
s += navHidden;
}
if (this->navigation != "") {
- s += "-";
+ if (s.length() > 0) {
+ s += "-";
+ }
s += navigation;
}
if (this->screenSize != "") {
- s += "-";
+ if (s.length() > 0) {
+ s += "-";
+ }
s += screenSize;
}
if (this->version != "") {
- s += "-";
+ if (s.length() > 0) {
+ s += "-";
+ }
s += version;
}
(out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
| ResTable_config::UI_MODE_TYPE_CAR;
return true;
+ } else if (strcmp(name, "television") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | ResTable_config::UI_MODE_TYPE_TELEVISION;
+ return true;
}
return false;
return true;
}
+ if (strcmp(name, "tvdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_TV;
+ return true;
+ }
+
if (strcmp(name, "hdpi") == 0) {
if (out) out->density = ResTable_config::DENSITY_HIGH;
return true;
return false;
}
-bool AaptGroupEntry::getScreenSizeName(const char* name,
- ResTable_config* out)
+bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
{
if (strcmp(name, kWildcardName) == 0) {
if (out) {
return true;
}
-bool AaptGroupEntry::getVersionName(const char* name,
- ResTable_config* out)
+bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
+{
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) {
+ out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
+ }
+ return true;
+ }
+
+ if (*name != 's') return false;
+ name++;
+ if (*name != 'w') return false;
+ name++;
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+ String8 xName(name, x-name);
+
+ if (out) {
+ out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
+ }
+
+ return true;
+}
+
+bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
+{
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) {
+ out->screenWidthDp = out->SCREENWIDTH_ANY;
+ }
+ return true;
+ }
+
+ if (*name != 'w') return false;
+ name++;
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+ String8 xName(name, x-name);
+
+ if (out) {
+ out->screenWidthDp = (uint16_t)atoi(xName.string());
+ }
+
+ return true;
+}
+
+bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
+{
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) {
+ out->screenHeightDp = out->SCREENWIDTH_ANY;
+ }
+ return true;
+ }
+
+ if (*name != 'h') return false;
+ name++;
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+ String8 xName(name, x-name);
+
+ if (out) {
+ out->screenHeightDp = (uint16_t)atoi(xName.string());
+ }
+
+ return true;
+}
+
+bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
{
if (strcmp(name, kWildcardName) == 0) {
if (out) {
if (v == 0) v = mnc.compare(o.mnc);
if (v == 0) v = locale.compare(o.locale);
if (v == 0) v = vendor.compare(o.vendor);
+ if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
+ if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
+ if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
if (v == 0) v = orientation.compare(o.orientation);
return v;
}
-ResTable_config AaptGroupEntry::toParams() const
+const ResTable_config& AaptGroupEntry::toParams() const
{
- ResTable_config params;
+ if (!mParamsChanged) {
+ return mParams;
+ }
+
+ mParamsChanged = false;
+ ResTable_config& params(mParams);
memset(¶ms, 0, sizeof(params));
getMccName(mcc.string(), ¶ms);
getMncName(mnc.string(), ¶ms);
getLocaleName(locale.string(), ¶ms);
+ getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), ¶ms);
+ getScreenWidthDpName(screenWidthDp.string(), ¶ms);
+ getScreenHeightDpName(screenHeightDp.string(), ¶ms);
getScreenLayoutSizeName(screenLayoutSize.string(), ¶ms);
getScreenLayoutLongName(screenLayoutLong.string(), ¶ms);
getOrientationName(orientation.string(), ¶ms);
// Fix up version number based on specified parameters.
int minSdk = 0;
- if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
+ if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
+ || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
+ || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
+ minSdk = SDK_HONEYCOMB_MR2;
+ } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
!= ResTable_config::UI_MODE_TYPE_ANY
|| (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
!= ResTable_config::UI_MODE_NIGHT_ANY) {
String8 AaptFile::getPrintableSource() const
{
if (hasData()) {
- String8 name(mGroupEntry.locale.string());
- name.appendPath(mGroupEntry.vendor.string());
+ String8 name(mGroupEntry.toDirName(String8()));
name.appendPath(mPath);
name.append(" #generated");
return name;
return NO_ERROR;
}
+#if 0
+ printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
+ file->getSourceFile().string(),
+ file->getGroupEntry().toDirName(String8()).string(),
+ mLeaf.string(), mPath.string());
+#endif
+
SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
getPrintableSource().string());
return UNKNOWN_ERROR;
mFiles.removeItemsAt(index);
}
-void AaptGroup::print() const
+void AaptGroup::print(const String8& prefix) const
{
- printf(" %s\n", getPath().string());
+ printf("%s%s\n", prefix.string(), getPath().string());
const size_t N=mFiles.size();
size_t i;
for (i=0; i<N; i++) {
sp<AaptFile> file = mFiles.valueAt(i);
const AaptGroupEntry& e = file->getGroupEntry();
if (file->hasData()) {
- printf(" Gen: (%s) %d bytes\n", e.toString().string(),
+ printf("%s Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
(int)file->getSize());
} else {
- printf(" Src: %s\n", file->getPrintableSource().string());
+ printf("%s Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
+ file->getPrintableSource().string());
}
+ //printf("%s File Group Entry: %s\n", prefix.string(),
+ // file->getGroupEntry().toDirName(String8()).string());
}
}
mDirs.removeItem(name);
}
-status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName)
-{
- sp<AaptGroup> origGroup;
-
- // Find and remove the given file with shear, brute force!
- const size_t NG = mFiles.size();
- size_t i;
- for (i=0; origGroup == NULL && i<NG; i++) {
- sp<AaptGroup> g = mFiles.valueAt(i);
- const size_t NF = g->getFiles().size();
- for (size_t j=0; j<NF; j++) {
- if (g->getFiles().valueAt(j) == file) {
- origGroup = g;
- g->removeFile(j);
- if (NF == 1) {
- mFiles.removeItemsAt(i);
- }
- break;
- }
- }
- }
-
- //printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string());
-
- // Place the file under its new name.
- if (origGroup != NULL) {
- return addLeafFile(newName, file);
- }
-
- return NO_ERROR;
-}
-
status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
{
sp<AaptGroup> group;
}
ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
- const AaptGroupEntry& kind, const String8& resType)
+ const AaptGroupEntry& kind, const String8& resType,
+ sp<FilePathStore>& fullResPaths)
{
Vector<String8> fileNames;
-
{
DIR* dir = NULL;
if (isHidden(srcDir.string(), entry->d_name))
continue;
- fileNames.add(String8(entry->d_name));
+ String8 name(entry->d_name);
+ fileNames.add(name);
+ // Add fully qualified path for dependency purposes
+ // if we're collecting them
+ if (fullResPaths != NULL) {
+ fullResPaths->add(srcDir.appendPathCopy(name));
+ }
}
-
closedir(dir);
}
notAdded = true;
}
ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
- resType);
+ resType, fullResPaths);
if (res < NO_ERROR) {
return res;
}
return NO_ERROR;
}
-void AaptDir::print() const
+void AaptDir::print(const String8& prefix) const
{
const size_t ND=getDirs().size();
size_t i;
for (i=0; i<ND; i++) {
- getDirs().valueAt(i)->print();
+ getDirs().valueAt(i)->print(prefix);
}
const size_t NF=getFiles().size();
for (i=0; i<NF; i++) {
- getFiles().valueAt(i)->print();
+ getFiles().valueAt(i)->print(prefix);
}
}
// =========================================================================
// =========================================================================
+AaptAssets::AaptAssets()
+ : AaptDir(String8(), String8()),
+ mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
+{
+}
+
+const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
+ if (mChanged) {
+ }
+ return mGroupEntries;
+}
+
+status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
+{
+ mChanged = true;
+ return AaptDir::addFile(name, file);
+}
+
sp<AaptFile> AaptAssets::addFile(
const String8& filePath, const AaptGroupEntry& entry,
const String8& srcDir, sp<AaptGroup>* outGroup,
sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
AaptGroupEntry group;
count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
- String8());
+ String8(), mFullAssetPaths);
if (count < 0) {
totalCount = count;
goto bail;
sp<AaptAssets> nextOverlay = new AaptAssets();
current->setOverlay(nextOverlay);
current = nextOverlay;
+ current->setFullResPaths(mFullResPaths);
}
count = current->slurpResourceTree(bundle, String8(res));
* guarantees about ordering, so we're okay with an inorder search
* using whatever order the OS happens to hand back to us.
*/
- count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8());
+ count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
if (count < 0) {
/* failure; report error and remove archive */
totalCount = count;
goto bail;
}
+ count = filter(bundle);
+ if (count != NO_ERROR) {
+ totalCount = count;
+ goto bail;
+ }
bail:
return totalCount;
ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
const AaptGroupEntry& kind,
- const String8& resType)
+ const String8& resType,
+ sp<FilePathStore>& fullResPaths)
{
- ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType);
+ ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
if (res > 0) {
mGroupEntries.add(kind);
}
continue;
}
- if (bundle->getMaxResVersion() != NULL && group.version.length() != 0) {
+ if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
int maxResInt = atoi(bundle->getMaxResVersion());
- const char *verString = group.version.string();
+ const char *verString = group.getVersionString().string();
int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
if (dirVersionInt > maxResInt) {
fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
FileType type = getFileType(subdirName.string());
if (type == kFileTypeDirectory) {
- sp<AaptDir> dir = makeDir(String8(entry->d_name));
+ sp<AaptDir> dir = makeDir(resType);
ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
- resType);
+ resType, mFullResPaths);
if (res < 0) {
count = res;
goto bail;
count += res;
}
- mDirs.add(dir);
+ // Only add this directory if we don't already have a resource dir
+ // for the current type. This ensures that we only add the dir once
+ // for all configs.
+ sp<AaptDir> rdir = resDir(resType);
+ if (rdir == NULL) {
+ mResDirs.add(dir);
+ }
} else {
if (bundle->getVerbose()) {
fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
return count;
}
+status_t AaptAssets::filter(Bundle* bundle)
+{
+ ResourceFilter reqFilter;
+ status_t err = reqFilter.parse(bundle->getConfigurations());
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ ResourceFilter prefFilter;
+ err = prefFilter.parse(bundle->getPreferredConfigurations());
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
+ return NO_ERROR;
+ }
+
+ if (bundle->getVerbose()) {
+ if (!reqFilter.isEmpty()) {
+ printf("Applying required filter: %s\n",
+ bundle->getConfigurations());
+ }
+ if (!prefFilter.isEmpty()) {
+ printf("Applying preferred filter: %s\n",
+ bundle->getPreferredConfigurations());
+ }
+ }
+
+ const Vector<sp<AaptDir> >& resdirs = mResDirs;
+ const size_t ND = resdirs.size();
+ for (size_t i=0; i<ND; i++) {
+ const sp<AaptDir>& dir = resdirs.itemAt(i);
+ if (dir->getLeaf() == kValuesDir) {
+ // The "value" dir is special since a single file defines
+ // multiple resources, so we can not do filtering on the
+ // files themselves.
+ continue;
+ }
+ if (dir->getLeaf() == kMipmapDir) {
+ // We also skip the "mipmap" directory, since the point of this
+ // is to include all densities without stripping. If you put
+ // other configurations in here as well they won't be stripped
+ // either... So don't do that. Seriously. What is wrong with you?
+ continue;
+ }
+
+ const size_t NG = dir->getFiles().size();
+ for (size_t j=0; j<NG; j++) {
+ sp<AaptGroup> grp = dir->getFiles().valueAt(j);
+
+ // First remove any configurations we know we don't need.
+ for (size_t k=0; k<grp->getFiles().size(); k++) {
+ sp<AaptFile> file = grp->getFiles().valueAt(k);
+ if (k == 0 && grp->getFiles().size() == 1) {
+ // If this is the only file left, we need to keep it.
+ // Otherwise the resource IDs we are using will be inconsistent
+ // with what we get when not stripping. Sucky, but at least
+ // for now we can rely on the back-end doing another filtering
+ // pass to take this out and leave us with this resource name
+ // containing no entries.
+ continue;
+ }
+ if (file->getPath().getPathExtension() == ".xml") {
+ // We can't remove .xml files at this point, because when
+ // we parse them they may add identifier resources, so
+ // removing them can cause our resource identifiers to
+ // become inconsistent.
+ continue;
+ }
+ const ResTable_config& config(file->getGroupEntry().toParams());
+ if (!reqFilter.match(config)) {
+ if (bundle->getVerbose()) {
+ printf("Pruning unneeded resource: %s\n",
+ file->getPrintableSource().string());
+ }
+ grp->removeFile(k);
+ k--;
+ }
+ }
+
+ // Quick check: no preferred filters, nothing more to do.
+ if (prefFilter.isEmpty()) {
+ continue;
+ }
+
+ // Now deal with preferred configurations.
+ for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
+ for (size_t k=0; k<grp->getFiles().size(); k++) {
+ sp<AaptFile> file = grp->getFiles().valueAt(k);
+ if (k == 0 && grp->getFiles().size() == 1) {
+ // If this is the only file left, we need to keep it.
+ // Otherwise the resource IDs we are using will be inconsistent
+ // with what we get when not stripping. Sucky, but at least
+ // for now we can rely on the back-end doing another filtering
+ // pass to take this out and leave us with this resource name
+ // containing no entries.
+ continue;
+ }
+ if (file->getPath().getPathExtension() == ".xml") {
+ // We can't remove .xml files at this point, because when
+ // we parse them they may add identifier resources, so
+ // removing them can cause our resource identifiers to
+ // become inconsistent.
+ continue;
+ }
+ const ResTable_config& config(file->getGroupEntry().toParams());
+ if (!prefFilter.match(axis, config)) {
+ // This is a resource we would prefer not to have. Check
+ // to see if have a similar variation that we would like
+ // to have and, if so, we can drop it.
+ for (size_t m=0; m<grp->getFiles().size(); m++) {
+ if (m == k) continue;
+ sp<AaptFile> mfile = grp->getFiles().valueAt(m);
+ const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
+ if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
+ if (prefFilter.match(axis, mconfig)) {
+ if (bundle->getVerbose()) {
+ printf("Pruning unneeded resource: %s\n",
+ file->getPrintableSource().string());
+ }
+ grp->removeFile(k);
+ k--;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
{
sp<AaptSymbols> sym = mSymbols.valueFor(name);
return mIncludedAssets.getResources(false);
}
-void AaptAssets::print() const
+void AaptAssets::print(const String8& prefix) const
{
- printf("Locale/Vendor pairs:\n");
+ String8 innerPrefix(prefix);
+ innerPrefix.append(" ");
+ String8 innerInnerPrefix(innerPrefix);
+ innerInnerPrefix.append(" ");
+ printf("%sConfigurations:\n", prefix.string());
const size_t N=mGroupEntries.size();
for (size_t i=0; i<N; i++) {
- printf(" %s/%s\n",
- mGroupEntries.itemAt(i).locale.string(),
- mGroupEntries.itemAt(i).vendor.string());
+ String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
+ printf("%s %s\n", prefix.string(),
+ cname != "" ? cname.string() : "(default)");
}
- printf("\nFiles:\n");
- AaptDir::print();
+ printf("\n%sFiles:\n", prefix.string());
+ AaptDir::print(innerPrefix);
+
+ printf("\n%sResource Dirs:\n", prefix.string());
+ const Vector<sp<AaptDir> >& resdirs = mResDirs;
+ const size_t NR = resdirs.size();
+ for (size_t i=0; i<NR; i++) {
+ const sp<AaptDir>& d = resdirs.itemAt(i);
+ printf("%s Type %s\n", prefix.string(), d->getLeaf().string());
+ d->print(innerInnerPrefix);
+ }
}
-sp<AaptDir> AaptAssets::resDir(const String8& name)
+sp<AaptDir> AaptAssets::resDir(const String8& name) const
{
- const Vector<sp<AaptDir> >& dirs = mDirs;
- const size_t N = dirs.size();
+ const Vector<sp<AaptDir> >& resdirs = mResDirs;
+ const size_t N = resdirs.size();
for (size_t i=0; i<N; i++) {
- const sp<AaptDir>& d = dirs.itemAt(i);
+ const sp<AaptDir>& d = resdirs.itemAt(i);
if (d->getLeaf() == name) {
return d;
}
bool valid_symbol_name(const String8& str);
+class AaptAssets;
+
enum {
AXIS_NONE = 0,
AXIS_MCC = 1,
AXIS_NAVHIDDEN,
AXIS_NAVIGATION,
AXIS_SCREENSIZE,
- AXIS_VERSION
+ AXIS_SMALLESTSCREENWIDTHDP,
+ AXIS_SCREENWIDTHDP,
+ AXIS_SCREENHEIGHTDP,
+ AXIS_VERSION,
+
+ AXIS_START = AXIS_MCC,
+ AXIS_END = AXIS_VERSION,
};
enum {
SDK_ECLAIR_0_1 = 6,
SDK_MR1 = 7,
SDK_FROYO = 8,
+ SDK_HONEYCOMB_MR2 = 13,
+ SDK_ICE_CREAM_SANDWICH = 14,
};
/**
struct AaptGroupEntry
{
public:
- AaptGroupEntry() { }
+ AaptGroupEntry() : mParamsChanged(true) { }
AaptGroupEntry(const String8& _locale, const String8& _vendor)
- : locale(_locale), vendor(_vendor) { }
-
- String8 mcc;
- String8 mnc;
- String8 locale;
- String8 vendor;
- String8 screenLayoutSize;
- String8 screenLayoutLong;
- String8 orientation;
- String8 uiModeType;
- String8 uiModeNight;
- String8 density;
- String8 touchscreen;
- String8 keysHidden;
- String8 keyboard;
- String8 navHidden;
- String8 navigation;
- String8 screenSize;
- String8 version;
+ : locale(_locale), vendor(_vendor), mParamsChanged(true) { }
bool initFromDirName(const char* dir, String8* resType);
static status_t parseNamePart(const String8& part, int* axis, uint32_t* value);
-
+
+ static uint32_t getConfigValueForAxis(const ResTable_config& config, int axis);
+
+ static bool configSameExcept(const ResTable_config& config,
+ const ResTable_config& otherConfig, int axis);
+
static bool getMccName(const char* name, ResTable_config* out = NULL);
static bool getMncName(const char* name, ResTable_config* out = NULL);
static bool getLocaleName(const char* name, ResTable_config* out = NULL);
static bool getNavigationName(const char* name, ResTable_config* out = NULL);
static bool getNavHiddenName(const char* name, ResTable_config* out = NULL);
static bool getScreenSizeName(const char* name, ResTable_config* out = NULL);
+ static bool getSmallestScreenWidthDpName(const char* name, ResTable_config* out = NULL);
+ static bool getScreenWidthDpName(const char* name, ResTable_config* out = NULL);
+ static bool getScreenHeightDpName(const char* name, ResTable_config* out = NULL);
static bool getVersionName(const char* name, ResTable_config* out = NULL);
int compare(const AaptGroupEntry& o) const;
- ResTable_config toParams() const;
+ const ResTable_config& toParams() const;
inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; }
inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; }
String8 toString() const;
String8 toDirName(const String8& resType) const;
+
+ const String8& getVersionString() const { return version; }
+
+private:
+ String8 mcc;
+ String8 mnc;
+ String8 locale;
+ String8 vendor;
+ String8 smallestScreenWidthDp;
+ String8 screenWidthDp;
+ String8 screenHeightDp;
+ String8 screenLayoutSize;
+ String8 screenLayoutLong;
+ String8 orientation;
+ String8 uiModeType;
+ String8 uiModeNight;
+ String8 density;
+ String8 touchscreen;
+ String8 keysHidden;
+ String8 keyboard;
+ String8 navHidden;
+ String8 navigation;
+ String8 screenSize;
+ String8 version;
+
+ mutable bool mParamsChanged;
+ mutable ResTable_config mParams;
};
inline int compare_type(const AaptGroupEntry& lhs, const AaptGroupEntry& rhs)
}
class AaptGroup;
+class FilePathStore;
/**
* A single asset file we know about.
status_t addFile(const sp<AaptFile>& file);
void removeFile(size_t index);
- void print() const;
+ void print(const String8& prefix) const;
String8 getPrintableSource() const;
};
/**
- * A single directory of assets, which can contain for files and other
+ * A single directory of assets, which can contain files and other
* sub-directories.
*/
class AaptDir : public RefBase
const DefaultKeyedVector<String8, sp<AaptGroup> >& getFiles() const { return mFiles; }
const DefaultKeyedVector<String8, sp<AaptDir> >& getDirs() const { return mDirs; }
- status_t addFile(const String8& name, const sp<AaptGroup>& file);
- status_t addDir(const String8& name, const sp<AaptDir>& dir);
-
- sp<AaptDir> makeDir(const String8& name);
+ virtual status_t addFile(const String8& name, const sp<AaptGroup>& file);
void removeFile(const String8& name);
void removeDir(const String8& name);
- status_t renameFile(const sp<AaptFile>& file, const String8& newName);
-
- status_t addLeafFile(const String8& leafName,
- const sp<AaptFile>& file);
-
- virtual ssize_t slurpFullTree(Bundle* bundle,
- const String8& srcDir,
- const AaptGroupEntry& kind,
- const String8& resType);
-
/*
* Perform some sanity checks on the names of files and directories here.
* In particular:
*/
status_t validate() const;
- void print() const;
+ void print(const String8& prefix) const;
String8 getPrintableSource() const;
private:
+ friend class AaptAssets;
+
+ status_t addDir(const String8& name, const sp<AaptDir>& dir);
+ sp<AaptDir> makeDir(const String8& name);
+ status_t addLeafFile(const String8& leafName,
+ const sp<AaptFile>& file);
+ virtual ssize_t slurpFullTree(Bundle* bundle,
+ const String8& srcDir,
+ const AaptGroupEntry& kind,
+ const String8& resType,
+ sp<FilePathStore>& fullResPaths);
+
String8 mLeaf;
String8 mPath;
ResourceTypeSet();
};
+// Storage for lists of fully qualified paths for
+// resources encountered during slurping.
+class FilePathStore : public RefBase,
+ public Vector<String8>
+{
+public:
+ FilePathStore();
+};
/**
* Asset hierarchy being operated on.
class AaptAssets : public AaptDir
{
public:
- AaptAssets() : AaptDir(String8(), String8()), mHaveIncludedAssets(false), mRes(NULL) { }
+ AaptAssets();
virtual ~AaptAssets() { delete mRes; }
const String8& getPackage() const { return mPackage; }
void setPackage(const String8& package) { mPackage = package; mSymbolsPrivatePackage = package; }
- const SortedVector<AaptGroupEntry>& getGroupEntries() const { return mGroupEntries; }
+ const SortedVector<AaptGroupEntry>& getGroupEntries() const;
+
+ virtual status_t addFile(const String8& name, const sp<AaptGroup>& file);
sp<AaptFile> addFile(const String8& filePath,
const AaptGroupEntry& entry,
ssize_t slurpFromArgs(Bundle* bundle);
- virtual ssize_t slurpFullTree(Bundle* bundle,
- const String8& srcDir,
- const AaptGroupEntry& kind,
- const String8& resType);
-
- ssize_t slurpResourceTree(Bundle* bundle, const String8& srcDir);
- ssize_t slurpResourceZip(Bundle* bundle, const char* filename);
-
sp<AaptSymbols> getSymbolsFor(const String8& name);
const DefaultKeyedVector<String8, sp<AaptSymbols> >& getSymbols() const { return mSymbols; }
status_t addIncludedResources(const sp<AaptFile>& file);
const ResTable& getIncludedResources() const;
- void print() const;
+ void print(const String8& prefix) const;
- inline const Vector<sp<AaptDir> >& resDirs() { return mDirs; }
- sp<AaptDir> resDir(const String8& name);
+ inline const Vector<sp<AaptDir> >& resDirs() const { return mResDirs; }
+ sp<AaptDir> resDir(const String8& name) const;
inline sp<AaptAssets> getOverlay() { return mOverlay; }
inline void setOverlay(sp<AaptAssets>& overlay) { mOverlay = overlay; }
inline void
setResources(KeyedVector<String8, sp<ResourceTypeSet> >* res) { delete mRes; mRes = res; }
+ inline sp<FilePathStore>& getFullResPaths() { return mFullResPaths; }
+ inline void
+ setFullResPaths(sp<FilePathStore>& res) { mFullResPaths = res; }
+
+ inline sp<FilePathStore>& getFullAssetPaths() { return mFullAssetPaths; }
+ inline void
+ setFullAssetPaths(sp<FilePathStore>& res) { mFullAssetPaths = res; }
+
private:
+ virtual ssize_t slurpFullTree(Bundle* bundle,
+ const String8& srcDir,
+ const AaptGroupEntry& kind,
+ const String8& resType,
+ sp<FilePathStore>& fullResPaths);
+
+ ssize_t slurpResourceTree(Bundle* bundle, const String8& srcDir);
+ ssize_t slurpResourceZip(Bundle* bundle, const char* filename);
+
+ status_t filter(Bundle* bundle);
+
String8 mPackage;
SortedVector<AaptGroupEntry> mGroupEntries;
DefaultKeyedVector<String8, sp<AaptSymbols> > mSymbols;
String8 mSymbolsPrivatePackage;
- Vector<sp<AaptDir> > mDirs;
+ Vector<sp<AaptDir> > mResDirs;
+
+ bool mChanged;
bool mHaveIncludedAssets;
AssetManager mIncludedAssets;
sp<AaptAssets> mOverlay;
KeyedVector<String8, sp<ResourceTypeSet> >* mRes;
+
+ sp<FilePathStore> mFullResPaths;
+ sp<FilePathStore> mFullAssetPaths;
};
#endif // __AAPT_ASSETS_H
LOCAL_SRC_FILES := \
AaptAssets.cpp \
Command.cpp \
+ CrunchCache.cpp \
+ FileFinder.cpp \
Main.cpp \
Package.cpp \
StringPool.cpp \
XMLNode.cpp \
+ ResourceFilter.cpp \
ResourceTable.cpp \
Images.cpp \
Resource.cpp \
libpng
ifeq ($(HOST_OS),linux)
-LOCAL_LDLIBS += -lrt -lpthread
+LOCAL_LDLIBS += -lrt -ldl -lpthread
endif
# Statically link libz for MinGW (Win SDK under Linux),
kCommandAdd,
kCommandRemove,
kCommandPackage,
+ kCommandCrunch,
} Command;
/*
mCompressionMethod(0), mOutputAPKFile(NULL),
mManifestPackageNameOverride(NULL), mInstrumentationPackageNameOverride(NULL),
mIsOverlayPackage(false),
- mAutoAddOverlay(false), mAssetSourceDir(NULL), mProguardFile(NULL),
+ mAutoAddOverlay(false), mGenDependencies(false),
+ mAssetSourceDir(NULL),
+ mCrunchedOutputDir(NULL), mProguardFile(NULL),
mAndroidManifestFile(NULL), mPublicOutputFile(NULL),
mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL),
mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
- mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL),
+ mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL), mExtraPackages(NULL),
mMaxResVersion(NULL), mDebugMode(false), mNonConstantId(false), mProduct(NULL),
- mArgc(0), mArgv(NULL)
+ mUseCrunchCache(false), mArgc(0), mArgv(NULL)
{}
~Bundle(void) {}
void setIsOverlayPackage(bool val) { mIsOverlayPackage = val; }
bool getAutoAddOverlay() { return mAutoAddOverlay; }
void setAutoAddOverlay(bool val) { mAutoAddOverlay = val; }
+ bool getGenDependencies() { return mGenDependencies; }
+ void setGenDependencies(bool val) { mGenDependencies = val; }
/*
* Input options.
*/
const char* getAssetSourceDir() const { return mAssetSourceDir; }
void setAssetSourceDir(const char* dir) { mAssetSourceDir = dir; }
+ const char* getCrunchedOutputDir() const { return mCrunchedOutputDir; }
+ void setCrunchedOutputDir(const char* dir) { mCrunchedOutputDir = dir; }
const char* getProguardFile() const { return mProguardFile; }
void setProguardFile(const char* file) { mProguardFile = file; }
const android::Vector<const char*>& getResourceSourceDirs() const { return mResourceSourceDirs; }
void setRClassDir(const char* dir) { mRClassDir = dir; }
const char* getConfigurations() const { return mConfigurations.size() > 0 ? mConfigurations.string() : NULL; }
void addConfigurations(const char* val) { if (mConfigurations.size() > 0) { mConfigurations.append(","); mConfigurations.append(val); } else { mConfigurations = val; } }
+ const char* getPreferredConfigurations() const { return mPreferredConfigurations.size() > 0 ? mPreferredConfigurations.string() : NULL; }
+ void addPreferredConfigurations(const char* val) { if (mPreferredConfigurations.size() > 0) { mPreferredConfigurations.append(","); mPreferredConfigurations.append(val); } else { mPreferredConfigurations = val; } }
const char* getResourceIntermediatesDir() const { return mResourceIntermediatesDir; }
void setResourceIntermediatesDir(const char* dir) { mResourceIntermediatesDir = dir; }
const android::Vector<const char*>& getPackageIncludes() const { return mPackageIncludes; }
void setVersionName(const char* val) { mVersionName = val; }
const char* getCustomPackage() const { return mCustomPackage; }
void setCustomPackage(const char* val) { mCustomPackage = val; }
+ const char* getExtraPackages() const { return mExtraPackages; }
+ void setExtraPackages(const char* val) { mExtraPackages = val; }
const char* getMaxResVersion() const { return mMaxResVersion; }
void setMaxResVersion(const char * val) { mMaxResVersion = val; }
bool getDebugMode() { return mDebugMode; }
void setNonConstantId(bool val) { mNonConstantId = val; }
const char* getProduct() const { return mProduct; }
void setProduct(const char * val) { mProduct = val; }
+ void setUseCrunchCache(bool val) { mUseCrunchCache = val; }
+ bool getUseCrunchCache() { return mUseCrunchCache; }
/*
* Set and get the file specification.
const char* mInstrumentationPackageNameOverride;
bool mIsOverlayPackage;
bool mAutoAddOverlay;
+ bool mGenDependencies;
const char* mAssetSourceDir;
+ const char* mCrunchedOutputDir;
const char* mProguardFile;
const char* mAndroidManifestFile;
const char* mPublicOutputFile;
const char* mRClassDir;
const char* mResourceIntermediatesDir;
android::String8 mConfigurations;
+ android::String8 mPreferredConfigurations;
android::Vector<const char*> mPackageIncludes;
android::Vector<const char*> mJarFiles;
android::Vector<const char*> mNoCompressExtensions;
const char* mVersionCode;
const char* mVersionName;
const char* mCustomPackage;
+ const char* mExtraPackages;
const char* mMaxResVersion;
bool mDebugMode;
bool mNonConstantId;
const char* mProduct;
+ bool mUseCrunchCache;
/* file specification */
int mArgc;
--- /dev/null
+//
+// Copyright 2011 The Android Open Source Project
+//
+// Abstraction of calls to system to make directories and delete files and
+// wrapper to image processing.
+
+#ifndef CACHE_UPDATER_H
+#define CACHE_UPDATER_H
+
+#include <utils/String8.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include "Images.h"
+
+using namespace android;
+
+/** CacheUpdater
+ * This is a pure virtual class that declares abstractions of functions useful
+ * for managing a cache files. This manager is set up to be used in a
+ * mirror cache where the source tree is duplicated and filled with processed
+ * images. This class is abstracted to allow for dependency injection during
+ * unit testing.
+ * Usage:
+ * To update/add a file to the cache, call processImage
+ * To remove a file from the cache, call deleteFile
+ */
+class CacheUpdater {
+public:
+ // Make sure all the directories along this path exist
+ virtual void ensureDirectoriesExist(String8 path) = 0;
+
+ // Delete a file
+ virtual void deleteFile(String8 path) = 0;
+
+ // Process an image from source out to dest
+ virtual void processImage(String8 source, String8 dest) = 0;
+private:
+};
+
+/** SystemCacheUpdater
+ * This is an implementation of the above virtual cache updater specification.
+ * This implementations hits the filesystem to manage a cache and calls out to
+ * the PNG crunching in images.h to process images out to its cache components.
+ */
+class SystemCacheUpdater : public CacheUpdater {
+public:
+ // Constructor to set bundle to pass to preProcessImage
+ SystemCacheUpdater (Bundle* b)
+ : bundle(b) { };
+
+ // Make sure all the directories along this path exist
+ virtual void ensureDirectoriesExist(String8 path)
+ {
+ // Check to see if we're dealing with a fully qualified path
+ String8 existsPath;
+ String8 toCreate;
+ String8 remains;
+ struct stat s;
+
+ // Check optomistically to see if all directories exist.
+ // If something in the path doesn't exist, then walk the path backwards
+ // and find the place to start creating directories forward.
+ if (stat(path.string(),&s) == -1) {
+ // Walk backwards to find place to start creating directories
+ existsPath = path;
+ do {
+ // As we remove the end of existsPath add it to
+ // the string of paths to create.
+ toCreate = existsPath.getPathLeaf().appendPath(toCreate);
+ existsPath = existsPath.getPathDir();
+ } while (stat(existsPath.string(),&s) == -1);
+
+ // Walk forwards and build directories as we go
+ do {
+ // Advance to the next segment of the path
+ existsPath.appendPath(toCreate.walkPath(&remains));
+ toCreate = remains;
+#ifdef HAVE_MS_C_RUNTIME
+ _mkdir(existsPath.string());
+#else
+ mkdir(existsPath.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
+#endif
+ } while (remains.length() > 0);
+ } //if
+ };
+
+ // Delete a file
+ virtual void deleteFile(String8 path)
+ {
+ if (remove(path.string()) != 0)
+ fprintf(stderr,"ERROR DELETING %s\n",path.string());
+ };
+
+ // Process an image from source out to dest
+ virtual void processImage(String8 source, String8 dest)
+ {
+ // Make sure we're trying to write to a directory that is extant
+ ensureDirectoriesExist(dest.getPathDir());
+
+ preProcessImageToCache(bundle, source, dest);
+ };
+private:
+ Bundle* bundle;
+};
+
+#endif // CACHE_UPDATER_H
\ No newline at end of file
//
#include "Main.h"
#include "Bundle.h"
+#include "ResourceFilter.h"
#include "ResourceTable.h"
#include "XMLNode.h"
if (&res == NULL) {
printf("\nNo resource table found.\n");
} else {
+#ifndef HAVE_ANDROID_OS
printf("\nResource table:\n");
res.print(false);
+#endif
}
Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
return value.data;
}
+static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
+ uint32_t attrRes, String8* outError, int32_t defValue = -1)
+{
+ ssize_t idx = indexOfAttribute(tree, attrRes);
+ if (idx < 0) {
+ return defValue;
+ }
+ Res_value value;
+ if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
+ if (value.dataType == Res_value::TYPE_REFERENCE) {
+ resTable->resolveReference(&value, 0);
+ }
+ if (value.dataType < Res_value::TYPE_FIRST_INT
+ || value.dataType > Res_value::TYPE_LAST_INT) {
+ if (outError != NULL) *outError = "attribute is not an integer value";
+ return defValue;
+ }
+ }
+ return value.data;
+}
+
static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
uint32_t attrRes, String8* outError)
{
// These are attribute resource constants for the platform, as found
// in android.R.attr
enum {
+ LABEL_ATTR = 0x01010001,
+ ICON_ATTR = 0x01010002,
NAME_ATTR = 0x01010003,
VERSION_CODE_ATTR = 0x0101021b,
VERSION_NAME_ATTR = 0x0101021c,
- LABEL_ATTR = 0x01010001,
- ICON_ATTR = 0x01010002,
+ SCREEN_ORIENTATION_ATTR = 0x0101001e,
MIN_SDK_VERSION_ATTR = 0x0101020c,
MAX_SDK_VERSION_ATTR = 0x01010271,
REQ_TOUCH_SCREEN_ATTR = 0x01010227,
REQUIRED_ATTR = 0x0101028e,
SCREEN_SIZE_ATTR = 0x010102ca,
SCREEN_DENSITY_ATTR = 0x010102cb,
+ REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
+ COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
+ LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
+ PUBLIC_KEY_ATTR = 0x010103a6,
};
const char *getComponentName(String8 &pkgName, String8 &componentName) {
return 1;
}
+ // Make a dummy config for retrieving resources... we need to supply
+ // non-default values for some configs so that we can retrieve resources
+ // in the app that don't have a default. The most important of these is
+ // the API version because key resources like icons will have an implicit
+ // version if they are using newer config types like density.
+ ResTable_config config;
+ config.language[0] = 'e';
+ config.language[1] = 'n';
+ config.country[0] = 'U';
+ config.country[1] = 'S';
+ config.orientation = ResTable_config::ORIENTATION_PORT;
+ config.density = ResTable_config::DENSITY_MEDIUM;
+ config.sdkVersion = 10000; // Very high.
+ config.screenWidthDp = 320;
+ config.screenHeightDp = 480;
+ config.smallestScreenWidthDp = 320;
+ assets.setConfiguration(config);
+
const ResTable& res = assets.getResources(false);
if (&res == NULL) {
fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
}
if (strcmp("resources", option) == 0) {
+#ifndef HAVE_ANDROID_OS
res.print(bundle->getValues());
-
+#endif
} else if (strcmp("xmltree", option) == 0) {
if (bundle->getFileSpecCount() < 3) {
fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
}
}
} else if (strcmp("badging", option) == 0) {
+ Vector<String8> locales;
+ res.getLocales(&locales);
+
+ Vector<ResTable_config> configs;
+ res.getConfigurations(&configs);
+ SortedVector<int> densities;
+ const size_t NC = configs.size();
+ for (size_t i=0; i<NC; i++) {
+ int dens = configs[i].density;
+ if (dens == 0) dens = 160;
+ densities.add(dens);
+ }
+
size_t len;
ResXMLTree::event_code_t code;
int depth = 0;
bool specTouchscreenFeature = false; // touchscreen-related
bool specMultitouchFeature = false;
bool reqDistinctMultitouchFeature = false;
+ bool specScreenPortraitFeature = false;
+ bool specScreenLandscapeFeature = false;
+ bool reqScreenPortraitFeature = false;
+ bool reqScreenLandscapeFeature = false;
// 2.2 also added some other features that apps can request, but that
// have no corresponding permission, so we cannot implement any
// back-compatibility heuristic for them. The below are thus unnecessary
int largeScreen = 1;
int xlargeScreen = 1;
int anyDensity = 1;
+ int requiresSmallestWidthDp = 0;
+ int compatibleWidthLimitDp = 0;
+ int largestWidthLimitDp = 0;
String8 pkg;
String8 activityName;
String8 activityLabel;
} else if (depth < 3) {
if (withinActivity && isMainActivity && isLauncherActivity) {
const char *aName = getComponentName(pkg, activityName);
+ printf("launchable-activity:");
if (aName != NULL) {
- printf("launchable activity name='%s'", aName);
+ printf(" name='%s' ", aName);
}
- printf("label='%s' icon='%s'\n",
+ printf(" label='%s' icon='%s'\n",
activityLabel.string(),
activityIcon.string());
}
withinApplication = false;
if (tag == "application") {
withinApplication = true;
- String8 label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
- if (error != "") {
- fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
- goto bail;
+
+ String8 label;
+ const size_t NL = locales.size();
+ for (size_t i=0; i<NL; i++) {
+ const char* localeStr = locales[i].string();
+ assets.setLocale(localeStr != NULL ? localeStr : "");
+ String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+ if (llabel != "") {
+ if (localeStr == NULL || strlen(localeStr) == 0) {
+ label = llabel;
+ printf("application-label:'%s'\n", llabel.string());
+ } else {
+ if (label == "") {
+ label = llabel;
+ }
+ printf("application-label-%s:'%s'\n", localeStr,
+ llabel.string());
+ }
+ }
}
- printf("application: label='%s' ", label.string());
+
+ ResTable_config tmpConfig = config;
+ const size_t ND = densities.size();
+ for (size_t i=0; i<ND; i++) {
+ tmpConfig.density = densities[i];
+ assets.setConfiguration(tmpConfig);
+ String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+ if (icon != "") {
+ printf("application-icon-%d:'%s'\n", densities[i], icon.string());
+ }
+ }
+ assets.setConfiguration(config);
+
String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
goto bail;
}
- printf("icon='%s'\n", icon.string());
int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
if (error != "") {
fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
goto bail;
}
+ printf("application: label='%s' ", label.string());
+ printf("icon='%s'\n", icon.string());
if (testOnly != 0) {
printf("testOnly='%d'\n", testOnly);
}
XLARGE_SCREEN_ATTR, NULL, 1);
anyDensity = getIntegerAttribute(tree,
ANY_DENSITY_ATTR, NULL, 1);
+ requiresSmallestWidthDp = getIntegerAttribute(tree,
+ REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
+ compatibleWidthLimitDp = getIntegerAttribute(tree,
+ COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
+ largestWidthLimitDp = getIntegerAttribute(tree,
+ LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
} else if (tag == "uses-feature") {
String8 name = getAttribute(tree, NAME_ATTR, &error);
// these have no corresponding permission to check for,
// but should imply the foundational telephony permission
reqTelephonySubFeature = true;
+ } else if (name == "android.hardware.screen.portrait") {
+ specScreenPortraitFeature = true;
+ } else if (name == "android.hardware.screen.landscape") {
+ specScreenLandscapeFeature = true;
}
printf("uses-feature%s:'%s'\n",
req ? "" : "-not-required", name.string());
} else if (tag == "compatible-screens") {
printCompatibleScreens(tree);
depth--;
+ } else if (tag == "package-verifier") {
+ String8 name = getAttribute(tree, NAME_ATTR, &error);
+ if (name != "" && error == "") {
+ String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
+ if (publicKey != "" && error == "") {
+ printf("package-verifier: name='%s' publicKey='%s'\n",
+ name.string(), publicKey.string());
+ }
+ }
}
} else if (depth == 3 && withinApplication) {
withinActivity = false;
fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
goto bail;
}
+
+ int32_t orien = getResolvedIntegerAttribute(&res, tree,
+ SCREEN_ORIENTATION_ATTR, &error);
+ if (error == "") {
+ if (orien == 0 || orien == 6 || orien == 8) {
+ // Requests landscape, sensorLandscape, or reverseLandscape.
+ reqScreenLandscapeFeature = true;
+ } else if (orien == 1 || orien == 7 || orien == 9) {
+ // Requests portrait, sensorPortrait, or reversePortrait.
+ reqScreenPortraitFeature = true;
+ }
+ }
} else if (tag == "uses-library") {
String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
if (error != "") {
printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
}
+ // Landscape/portrait-related compatibility logic
+ if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
+ // If the app has specified any activities in its manifest
+ // that request a specific orientation, then assume that
+ // orientation is required.
+ if (reqScreenLandscapeFeature) {
+ printf("uses-feature:'android.hardware.screen.landscape'\n");
+ }
+ if (reqScreenPortraitFeature) {
+ printf("uses-feature:'android.hardware.screen.portrait'\n");
+ }
+ }
+
if (hasMainActivity) {
printf("main\n");
}
printf("other-services\n");
}
+ // For modern apps, if screen size buckets haven't been specified
+ // but the new width ranges have, then infer the buckets from them.
+ if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
+ && requiresSmallestWidthDp > 0) {
+ int compatWidth = compatibleWidthLimitDp;
+ if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
+ if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
+ smallScreen = -1;
+ } else {
+ smallScreen = 0;
+ }
+ if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
+ normalScreen = -1;
+ } else {
+ normalScreen = 0;
+ }
+ if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
+ largeScreen = -1;
+ } else {
+ largeScreen = 0;
+ }
+ if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
+ xlargeScreen = -1;
+ } else {
+ xlargeScreen = 0;
+ }
+ }
+
// Determine default values for any unspecified screen sizes,
// based on the target SDK of the package. As of 4 (donut)
// the screen size support was introduced, so all default to
xlargeScreen = targetSdk >= 9 ? -1 : 0;
}
if (anyDensity > 0) {
- anyDensity = targetSdk >= 4 ? -1 : 0;
+ anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
+ || compatibleWidthLimitDp > 0) ? -1 : 0;
}
printf("supports-screens:");
if (smallScreen != 0) printf(" 'small'");
if (largeScreen != 0) printf(" 'large'");
if (xlargeScreen != 0) printf(" 'xlarge'");
printf("\n");
-
printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
+ if (requiresSmallestWidthDp > 0) {
+ printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
+ }
+ if (compatibleWidthLimitDp > 0) {
+ printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
+ }
+ if (largestWidthLimitDp > 0) {
+ printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
+ }
printf("locales:");
- Vector<String8> locales;
- res.getLocales(&locales);
const size_t NL = locales.size();
for (size_t i=0; i<NL; i++) {
const char* localeStr = locales[i].string();
}
printf("\n");
- Vector<ResTable_config> configs;
- res.getConfigurations(&configs);
- SortedVector<int> densities;
- const size_t NC = configs.size();
- for (size_t i=0; i<NC; i++) {
- int dens = configs[i].density;
- if (dens == 0) dens = 160;
- densities.add(dens);
- }
-
printf("densities:");
const size_t ND = densities.size();
for (size_t i=0; i<ND; i++) {
status_t err;
sp<AaptAssets> assets;
int N;
+ FILE* fp;
+ String8 dependencyFile;
// -c zz_ZZ means do pseudolocalization
ResourceFilter filter;
// Load the assets.
assets = new AaptAssets();
+
+ // Set up the resource gathering in assets if we're going to generate
+ // dependency files. Every time we encounter a resource while slurping
+ // the tree, we'll add it to these stores so we have full resource paths
+ // to write to a dependency file.
+ if (bundle->getGenDependencies()) {
+ sp<FilePathStore> resPathStore = new FilePathStore;
+ assets->setFullResPaths(resPathStore);
+ sp<FilePathStore> assetPathStore = new FilePathStore;
+ assets->setFullAssetPaths(assetPathStore);
+ }
+
err = assets->slurpFromArgs(bundle);
if (err < 0) {
goto bail;
}
if (bundle->getVerbose()) {
- assets->print();
+ assets->print(String8());
}
- // If they asked for any files that need to be compiled, do so.
+ // If they asked for any fileAs that need to be compiled, do so.
if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
err = buildResources(bundle, assets);
if (err != 0) {
goto bail;
}
+ // If we've been asked to generate a dependency file, do that here
+ if (bundle->getGenDependencies()) {
+ // If this is the packaging step, generate the dependency file next to
+ // the output apk (e.g. bin/resources.ap_.d)
+ if (outputAPKFile) {
+ dependencyFile = String8(outputAPKFile);
+ // Add the .d extension to the dependency file.
+ dependencyFile.append(".d");
+ } else {
+ // Else if this is the R.java dependency generation step,
+ // generate the dependency file in the R.java package subdirectory
+ // e.g. gen/com/foo/app/R.java.d
+ dependencyFile = String8(bundle->getRClassDir());
+ dependencyFile.appendPath("R.java.d");
+ }
+ // Make sure we have a clean dependency file to start with
+ fp = fopen(dependencyFile, "w");
+ fclose(fp);
+ }
+
// Write out R.java constants
if (assets->getPackage() == assets->getSymbolsPrivatePackage()) {
if (bundle->getCustomPackage() == NULL) {
+ // Write the R.java file into the appropriate class directory
+ // e.g. gen/com/foo/app/R.java
err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
+ // If we have library files, we're going to write our R.java file into
+ // the appropriate class directory for those libraries as well.
+ // e.g. gen/com/foo/app/lib/R.java
+ if (bundle->getExtraPackages() != NULL) {
+ // Split on colon
+ String8 libs(bundle->getExtraPackages());
+ char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
+ while (packageString != NULL) {
+ // Write the R.java file out with the correct package name
+ err = writeResourceSymbols(bundle, assets, String8(packageString), true);
+ packageString = strtok(NULL, ":");
+ }
+ libs.unlockBuffer();
+ }
} else {
const String8 customPkg(bundle->getCustomPackage());
err = writeResourceSymbols(bundle, assets, customPkg, true);
}
}
+ // If we've been asked to generate a dependency file, we need to finish up here.
+ // the writeResourceSymbols and writeAPK functions have already written the target
+ // half of the dependency file, now we need to write the prerequisites. (files that
+ // the R.java file or .ap_ file depend on)
+ if (bundle->getGenDependencies()) {
+ // Now that writeResourceSymbols or writeAPK has taken care of writing
+ // the targets to our dependency file, we'll write the prereqs
+ fp = fopen(dependencyFile, "a+");
+ fprintf(fp, " : ");
+ bool includeRaw = (outputAPKFile != NULL);
+ err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
+ // Also manually add the AndroidManifeset since it's not under res/ or assets/
+ // and therefore was not added to our pathstores during slurping
+ fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
+ fclose(fp);
+ }
+
retVal = 0;
bail:
if (SourcePos::hasErrors()) {
}
return retVal;
}
+
+/*
+ * Do PNG Crunching
+ * PRECONDITIONS
+ * -S flag points to a source directory containing drawable* folders
+ * -C flag points to destination directory. The folder structure in the
+ * source directory will be mirrored to the destination (cache) directory
+ *
+ * POSTCONDITIONS
+ * Destination directory will be updated to match the PNG files in
+ * the source directory.
+ */
+int doCrunch(Bundle* bundle)
+{
+ fprintf(stdout, "Crunching PNG Files in ");
+ fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
+ fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
+
+ updatePreProcessedCache(bundle);
+
+ return NO_ERROR;
+}
--- /dev/null
+//
+// Copyright 2011 The Android Open Source Project
+//
+// Implementation file for CrunchCache
+// This file defines functions laid out and documented in
+// CrunchCache.h
+
+#include <utils/Vector.h>
+#include <utils/String8.h>
+
+#include "DirectoryWalker.h"
+#include "FileFinder.h"
+#include "CacheUpdater.h"
+#include "CrunchCache.h"
+
+using namespace android;
+
+CrunchCache::CrunchCache(String8 sourcePath, String8 destPath, FileFinder* ff)
+ : mSourcePath(sourcePath), mDestPath(destPath), mSourceFiles(0), mDestFiles(0), mFileFinder(ff)
+{
+ // We initialize the default value to return to 0 so if a file doesn't exist
+ // then all files are automatically "newer" than it.
+
+ // Set file extensions to look for. Right now just pngs.
+ mExtensions.push(String8(".png"));
+
+ // Load files into our data members
+ loadFiles();
+}
+
+size_t CrunchCache::crunch(CacheUpdater* cu, bool forceOverwrite)
+{
+ size_t numFilesUpdated = 0;
+
+ // Iterate through the source files and compare to cache.
+ // After processing a file, remove it from the source files and
+ // from the dest files.
+ // We're done when we're out of files in source.
+ String8 relativePath;
+ while (mSourceFiles.size() > 0) {
+ // Get the full path to the source file, then convert to a c-string
+ // and offset our beginning pointer to the length of the sourcePath
+ // This efficiently strips the source directory prefix from our path.
+ // Also, String8 doesn't have a substring method so this is what we've
+ // got to work with.
+ const char* rPathPtr = mSourceFiles.keyAt(0).string()+mSourcePath.length();
+ // Strip leading slash if present
+ int offset = 0;
+ if (rPathPtr[0] == OS_PATH_SEPARATOR)
+ offset = 1;
+ relativePath = String8(rPathPtr + offset);
+
+ if (forceOverwrite || needsUpdating(relativePath)) {
+ cu->processImage(mSourcePath.appendPathCopy(relativePath),
+ mDestPath.appendPathCopy(relativePath));
+ numFilesUpdated++;
+ // crunchFile(relativePath);
+ }
+ // Delete this file from the source files and (if it exists) from the
+ // dest files.
+ mSourceFiles.removeItemsAt(0);
+ mDestFiles.removeItem(mDestPath.appendPathCopy(relativePath));
+ }
+
+ // Iterate through what's left of destFiles and delete leftovers
+ while (mDestFiles.size() > 0) {
+ cu->deleteFile(mDestFiles.keyAt(0));
+ mDestFiles.removeItemsAt(0);
+ }
+
+ // Update our knowledge of the files cache
+ // both source and dest should be empty by now.
+ loadFiles();
+
+ return numFilesUpdated;
+}
+
+void CrunchCache::loadFiles()
+{
+ // Clear out our data structures to avoid putting in duplicates
+ mSourceFiles.clear();
+ mDestFiles.clear();
+
+ // Make a directory walker that points to the system.
+ DirectoryWalker* dw = new SystemDirectoryWalker();
+
+ // Load files in the source directory
+ mFileFinder->findFiles(mSourcePath, mExtensions, mSourceFiles,dw);
+
+ // Load files in the destination directory
+ mFileFinder->findFiles(mDestPath,mExtensions,mDestFiles,dw);
+
+ delete dw;
+}
+
+bool CrunchCache::needsUpdating(String8 relativePath) const
+{
+ // Retrieve modification dates for this file entry under the source and
+ // cache directory trees. The vectors will return a modification date of 0
+ // if the file doesn't exist.
+ time_t sourceDate = mSourceFiles.valueFor(mSourcePath.appendPathCopy(relativePath));
+ time_t destDate = mDestFiles.valueFor(mDestPath.appendPathCopy(relativePath));
+ return sourceDate > destDate;
+}
\ No newline at end of file
--- /dev/null
+//
+// Copyright 2011 The Android Open Source Project
+//
+// Cache manager for pre-processed PNG files.
+// Contains code for managing which PNG files get processed
+// at build time.
+//
+
+#ifndef CRUNCHCACHE_H
+#define CRUNCHCACHE_H
+
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include "FileFinder.h"
+#include "CacheUpdater.h"
+
+using namespace android;
+
+/** CrunchCache
+ * This class is a cache manager which can pre-process PNG files and store
+ * them in a mirror-cache. It's capable of doing incremental updates to its
+ * cache.
+ *
+ * Usage:
+ * Create an instance initialized with the root of the source tree, the
+ * root location to store the cache files, and an instance of a file finder.
+ * Then update the cache by calling crunch.
+ */
+class CrunchCache {
+public:
+ // Constructor
+ CrunchCache(String8 sourcePath, String8 destPath, FileFinder* ff);
+
+ // Nobody should be calling the default constructor
+ // So this space is intentionally left blank
+
+ // Default Copy Constructor and Destructor are fine
+
+ /** crunch is the workhorse of this class.
+ * It goes through all the files found in the sourcePath and compares
+ * them to the cached versions in the destPath. If the optional
+ * argument forceOverwrite is set to true, then all source files are
+ * re-crunched even if they have not been modified recently. Otherwise,
+ * source files are only crunched when they needUpdating. Afterwards,
+ * we delete any leftover files in the cache that are no longer present
+ * in source.
+ *
+ * PRECONDITIONS:
+ * No setup besides construction is needed
+ * POSTCONDITIONS:
+ * The cache is updated to fully reflect all changes in source.
+ * The function then returns the number of files changed in cache
+ * (counting deletions).
+ */
+ size_t crunch(CacheUpdater* cu, bool forceOverwrite=false);
+
+private:
+ /** loadFiles is a wrapper to the FileFinder that places matching
+ * files into mSourceFiles and mDestFiles.
+ *
+ * POSTCONDITIONS
+ * mDestFiles and mSourceFiles are refreshed to reflect the current
+ * state of the files in the source and dest directories.
+ * Any previous contents of mSourceFiles and mDestFiles are cleared.
+ */
+ void loadFiles();
+
+ /** needsUpdating takes a file path
+ * and returns true if the file represented by this path is newer in the
+ * sourceFiles than in the cache (mDestFiles).
+ *
+ * PRECONDITIONS:
+ * mSourceFiles and mDestFiles must be initialized and filled.
+ * POSTCONDITIONS:
+ * returns true if and only if source file's modification time
+ * is greater than the cached file's mod-time. Otherwise returns false.
+ *
+ * USAGE:
+ * Should be used something like the following:
+ * if (needsUpdating(filePath))
+ * // Recrunch sourceFile out to destFile.
+ *
+ */
+ bool needsUpdating(String8 relativePath) const;
+
+ // DATA MEMBERS ====================================================
+
+ String8 mSourcePath;
+ String8 mDestPath;
+
+ Vector<String8> mExtensions;
+
+ // Each vector of paths contains one entry per PNG file encountered.
+ // Each entry consists of a path pointing to that PNG.
+ DefaultKeyedVector<String8,time_t> mSourceFiles;
+ DefaultKeyedVector<String8,time_t> mDestFiles;
+
+ // Pointer to a FileFinder to use
+ FileFinder* mFileFinder;
+};
+
+#endif // CRUNCHCACHE_H
--- /dev/null
+//
+// Copyright 2011 The Android Open Source Project
+//
+// Defines an abstraction for opening a directory on the filesystem and
+// iterating through it.
+
+#ifndef DIRECTORYWALKER_H
+#define DIRECTORYWALKER_H
+
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <utils/String8.h>
+
+#include <stdio.h>
+
+using namespace android;
+
+// Directory Walker
+// This is an abstraction for walking through a directory and getting files
+// and descriptions.
+
+class DirectoryWalker {
+public:
+ virtual ~DirectoryWalker() {};
+ virtual bool openDir(String8 path) = 0;
+ virtual bool openDir(const char* path) = 0;
+ // Advance to next directory entry
+ virtual struct dirent* nextEntry() = 0;
+ // Get the stats for the current entry
+ virtual struct stat* entryStats() = 0;
+ // Clean Up
+ virtual void closeDir() = 0;
+ // This class is able to replicate itself on the heap
+ virtual DirectoryWalker* clone() = 0;
+
+ // DATA MEMBERS
+ // Current directory entry
+ struct dirent mEntry;
+ // Stats for that directory entry
+ struct stat mStats;
+ // Base path
+ String8 mBasePath;
+};
+
+// System Directory Walker
+// This is an implementation of the above abstraction that calls
+// real system calls and is fully functional.
+// functions are inlined since they're very short and simple
+
+class SystemDirectoryWalker : public DirectoryWalker {
+
+ // Default constructor, copy constructor, and destructor are fine
+public:
+ virtual bool openDir(String8 path) {
+ mBasePath = path;
+ dir = NULL;
+ dir = opendir(mBasePath.string() );
+
+ if (dir == NULL)
+ return false;
+
+ return true;
+ };
+ virtual bool openDir(const char* path) {
+ String8 p(path);
+ openDir(p);
+ return true;
+ };
+ // Advance to next directory entry
+ virtual struct dirent* nextEntry() {
+ struct dirent* entryPtr = readdir(dir);
+ if (entryPtr == NULL)
+ return NULL;
+
+ mEntry = *entryPtr;
+ // Get stats
+ String8 fullPath = mBasePath.appendPathCopy(mEntry.d_name);
+ stat(fullPath.string(),&mStats);
+ return &mEntry;
+ };
+ // Get the stats for the current entry
+ virtual struct stat* entryStats() {
+ return &mStats;
+ };
+ virtual void closeDir() {
+ closedir(dir);
+ };
+ virtual DirectoryWalker* clone() {
+ return new SystemDirectoryWalker(*this);
+ };
+private:
+ DIR* dir;
+};
+
+#endif // DIRECTORYWALKER_H
--- /dev/null
+//
+// Copyright 2011 The Android Open Source Project
+//
+
+// File Finder implementation.
+// Implementation for the functions declared and documented in FileFinder.h
+
+#include <utils/Vector.h>
+#include <utils/String8.h>
+#include <utils/KeyedVector.h>
+
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include "DirectoryWalker.h"
+#include "FileFinder.h"
+
+//#define DEBUG
+
+using android::String8;
+
+// Private function to check whether a file is a directory or not
+bool isDirectory(const char* filename) {
+ struct stat fileStat;
+ if (stat(filename, &fileStat) == -1) {
+ return false;
+ }
+ return(S_ISDIR(fileStat.st_mode));
+}
+
+
+// Private function to check whether a file is a regular file or not
+bool isFile(const char* filename) {
+ struct stat fileStat;
+ if (stat(filename, &fileStat) == -1) {
+ return false;
+ }
+ return(S_ISREG(fileStat.st_mode));
+}
+
+bool SystemFileFinder::findFiles(String8 basePath, Vector<String8>& extensions,
+ KeyedVector<String8,time_t>& fileStore,
+ DirectoryWalker* dw)
+{
+ // Scan the directory pointed to by basePath
+ // check files and recurse into subdirectories.
+ if (!dw->openDir(basePath)) {
+ return false;
+ }
+ /*
+ * Go through all directory entries. Check each file using checkAndAddFile
+ * and recurse into sub-directories.
+ */
+ struct dirent* entry;
+ while ((entry = dw->nextEntry()) != NULL) {
+ String8 entryName(entry->d_name);
+ if (entry->d_name[0] == '.') // Skip hidden files and directories
+ continue;
+
+ String8 fullPath = basePath.appendPathCopy(entryName);
+ // If this entry is a directory we'll recurse into it
+ if (isDirectory(fullPath.string()) ) {
+ DirectoryWalker* copy = dw->clone();
+ findFiles(fullPath, extensions, fileStore,copy);
+ delete copy;
+ }
+
+ // If this entry is a file, we'll pass it over to checkAndAddFile
+ if (isFile(fullPath.string()) ) {
+ checkAndAddFile(fullPath,dw->entryStats(),extensions,fileStore);
+ }
+ }
+
+ // Clean up
+ dw->closeDir();
+
+ return true;
+}
+
+void SystemFileFinder::checkAndAddFile(String8 path, const struct stat* stats,
+ Vector<String8>& extensions,
+ KeyedVector<String8,time_t>& fileStore)
+{
+ // Loop over the extensions, checking for a match
+ bool done = false;
+ String8 ext(path.getPathExtension());
+ ext.toLower();
+ for (size_t i = 0; i < extensions.size() && !done; ++i) {
+ String8 ext2 = extensions[i].getPathExtension();
+ ext2.toLower();
+ // Compare the extensions. If a match is found, add to storage.
+ if (ext == ext2) {
+ done = true;
+ fileStore.add(path,stats->st_mtime);
+ }
+ }
+}
+
--- /dev/null
+//
+// Copyright 2011 The Android Open Source Project
+//
+
+// File Finder.
+// This is a collection of useful functions for finding paths and modification
+// times of files that match an extension pattern in a directory tree.
+// and finding files in it.
+
+#ifndef FILEFINDER_H
+#define FILEFINDER_H
+
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+
+#include "DirectoryWalker.h"
+
+using namespace android;
+
+// Abstraction to allow for dependency injection. See MockFileFinder.h
+// for the testing implementation.
+class FileFinder {
+public:
+ virtual bool findFiles(String8 basePath, Vector<String8>& extensions,
+ KeyedVector<String8,time_t>& fileStore,
+ DirectoryWalker* dw) = 0;
+
+ virtual ~FileFinder() {};
+};
+
+class SystemFileFinder : public FileFinder {
+public:
+
+ /* findFiles takes a path, a Vector of extensions, and a destination KeyedVector
+ * and places path/modification date key/values pointing to
+ * all files with matching extensions found into the KeyedVector
+ * PRECONDITIONS
+ * path is a valid system path
+ * extensions should include leading "."
+ * This is not necessary, but the comparison directly
+ * compares the end of the path string so if the "."
+ * is excluded there is a small chance you could have
+ * a false positive match. (For example: extension "png"
+ * would match a file called "blahblahpng")
+ *
+ * POSTCONDITIONS
+ * fileStore contains (in no guaranteed order) paths to all
+ * matching files encountered in subdirectories of path
+ * as keys in the KeyedVector. Each key has the modification time
+ * of the file as its value.
+ *
+ * Calls checkAndAddFile on each file encountered in the directory tree
+ * Recursively descends into subdirectories.
+ */
+ virtual bool findFiles(String8 basePath, Vector<String8>& extensions,
+ KeyedVector<String8,time_t>& fileStore,
+ DirectoryWalker* dw);
+
+private:
+ /**
+ * checkAndAddFile looks at a single file path and stat combo
+ * to determine whether it is a matching file (by looking at
+ * the extension)
+ *
+ * PRECONDITIONS
+ * no setup is needed
+ *
+ * POSTCONDITIONS
+ * If the given file has a matching extension then a new entry
+ * is added to the KeyedVector with the path as the key and the modification
+ * time as the value.
+ *
+ */
+ static void checkAndAddFile(String8 path, const struct stat* stats,
+ Vector<String8>& extensions,
+ KeyedVector<String8,time_t>& fileStore);
+
+};
+#endif // FILEFINDER_H
String8 printableName(file->getPrintableSource());
+ if (bundle->getVerbose()) {
+ printf("Processing image: %s\n", printableName.string());
+ }
+
png_structp read_ptr = NULL;
png_infop read_info = NULL;
FILE* fp;
return error;
}
+status_t preProcessImageToCache(Bundle* bundle, String8 source, String8 dest)
+{
+ png_structp read_ptr = NULL;
+ png_infop read_info = NULL;
+
+ FILE* fp;
+
+ image_info imageInfo;
+
+ png_structp write_ptr = NULL;
+ png_infop write_info = NULL;
+
+ status_t error = UNKNOWN_ERROR;
+
+ if (bundle->getVerbose()) {
+ printf("Processing image to cache: %s => %s\n", source.string(), dest.string());
+ }
+
+ // Get a file handler to read from
+ fp = fopen(source.string(),"rb");
+ if (fp == NULL) {
+ fprintf(stderr, "%s ERROR: Unable to open PNG file\n", source.string());
+ return error;
+ }
+
+ // Call libpng to get a struct to read image data into
+ read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!read_ptr) {
+ fclose(fp);
+ png_destroy_read_struct(&read_ptr, &read_info,NULL);
+ return error;
+ }
+
+ // Call libpng to get a struct to read image info into
+ read_info = png_create_info_struct(read_ptr);
+ if (!read_info) {
+ fclose(fp);
+ png_destroy_read_struct(&read_ptr, &read_info,NULL);
+ return error;
+ }
+
+ // Set a jump point for libpng to long jump back to on error
+ if (setjmp(png_jmpbuf(read_ptr))) {
+ fclose(fp);
+ png_destroy_read_struct(&read_ptr, &read_info,NULL);
+ return error;
+ }
+
+ // Set up libpng to read from our file.
+ png_init_io(read_ptr,fp);
+
+ // Actually read data from the file
+ read_png(source.string(), read_ptr, read_info, &imageInfo);
+
+ // We're done reading so we can clean up
+ // Find old file size before releasing handle
+ fseek(fp, 0, SEEK_END);
+ size_t oldSize = (size_t)ftell(fp);
+ fclose(fp);
+ png_destroy_read_struct(&read_ptr, &read_info,NULL);
+
+ // Check to see if we're dealing with a 9-patch
+ // If we are, process appropriately
+ if (source.getBasePath().getPathExtension() == ".9") {
+ if (do_9patch(source.string(), &imageInfo) != NO_ERROR) {
+ return error;
+ }
+ }
+
+ // Call libpng to create a structure to hold the processed image data
+ // that can be written to disk
+ write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!write_ptr) {
+ png_destroy_write_struct(&write_ptr, &write_info);
+ return error;
+ }
+
+ // Call libpng to create a structure to hold processed image info that can
+ // be written to disk
+ write_info = png_create_info_struct(write_ptr);
+ if (!write_info) {
+ png_destroy_write_struct(&write_ptr, &write_info);
+ return error;
+ }
+
+ // Open up our destination file for writing
+ fp = fopen(dest.string(), "wb");
+ if (!fp) {
+ fprintf(stderr, "%s ERROR: Unable to open PNG file\n", dest.string());
+ png_destroy_write_struct(&write_ptr, &write_info);
+ return error;
+ }
+ // Set up libpng to write to our file
+ png_init_io(write_ptr, fp);
+
+ // Set up a jump for libpng to long jump back on on errors
+ if (setjmp(png_jmpbuf(write_ptr))) {
+ fclose(fp);
+ png_destroy_write_struct(&write_ptr, &write_info);
+ return error;
+ }
+
+ // Actually write out to the new png
+ write_png(dest.string(), write_ptr, write_info, imageInfo,
+ bundle->getGrayscaleTolerance());
+
+ if (bundle->getVerbose()) {
+ // Find the size of our new file
+ FILE* reader = fopen(dest.string(), "rb");
+ fseek(reader, 0, SEEK_END);
+ size_t newSize = (size_t)ftell(reader);
+ fclose(reader);
+
+ float factor = ((float)newSize)/oldSize;
+ int percent = (int)(factor*100);
+ printf(" (processed image to cache entry %s: %d%% size of source)\n",
+ dest.string(), percent);
+ }
+
+ //Clean up
+ fclose(fp);
+ png_destroy_write_struct(&write_ptr, &write_info);
+
+ return NO_ERROR;
+}
status_t postProcessImage(const sp<AaptAssets>& assets,
ResourceTable* table, const sp<AaptFile>& file)
#define IMAGES_H
#include "ResourceTable.h"
+#include "Bundle.h"
+
+#include <utils/String8.h>
+#include <utils/RefBase.h>
+
+using android::String8;
status_t preProcessImage(Bundle* bundle, const sp<AaptAssets>& assets,
const sp<AaptFile>& file, String8* outNewLeafName);
+status_t preProcessImageToCache(Bundle* bundle, String8 source, String8 dest);
+
status_t postProcessImage(const sp<AaptAssets>& assets,
- ResourceTable* table, const sp<AaptFile>& file);
+ ResourceTable* table, const sp<AaptFile>& file);
#endif
" [--max-res-version VAL] \\\n"
" [-I base-package [-I base-package ...]] \\\n"
" [-A asset-source-dir] [-G class-list-file] [-P public-definitions-file] \\\n"
- " [-S resource-sources [-S resource-sources ...]] "
+ " [-S resource-sources [-S resource-sources ...]] \\\n"
" [-F apk-file] [-J R-file-dir] \\\n"
" [--product product1,product2,...] \\\n"
+ " [-c CONFIGS] [--preferred-configurations CONFIGS] \\\n"
" [-o] \\\n"
" [raw-files-dir [raw-files-dir] ...]\n"
"\n"
fprintf(stderr,
" %s a[dd] [-v] file.{zip,jar,apk} file1 [file2 ...]\n"
" Add specified files to Zip-compatible archive.\n\n", gProgName);
+ fprintf(stderr,
+ " %s c[runch] [-v] -S resource-sources ... -C output-folder ...\n"
+ " Do PNG preprocessing and store the results in output folder.\n\n", gProgName);
fprintf(stderr,
" %s v[ersion]\n"
" Print program version.\n\n", gProgName);
" inserts android:versionName in to manifest.\n"
" --custom-package\n"
" generates R.java into a different package.\n"
+ " --extra-packages\n"
+ " generate R.java for libraries. Separate libraries with ':'.\n"
+ " --generate-dependencies\n"
+ " generate dependency files in the same directories for R.java and resource package\n"
" --auto-add-overlay\n"
" Automatically add resources that are only in overlays.\n"
+ " --preferred-configurations\n"
+ " Like the -c option for filtering out unneeded configurations, but\n"
+ " only expresses a preference. If there is no resource available with\n"
+ " the preferred configuration then it will not be stripped.\n"
" --rename-manifest-package\n"
" Rewrite the manifest so that its package name is the package name\n"
" given here. Relative class names (for example .Foo) will be\n"
case kCommandAdd: return doAdd(bundle);
case kCommandRemove: return doRemove(bundle);
case kCommandPackage: return doPackage(bundle);
+ case kCommandCrunch: return doCrunch(bundle);
default:
fprintf(stderr, "%s: requested command not yet supported\n", gProgName);
return 1;
bundle.setCommand(kCommandRemove);
else if (argv[1][0] == 'p')
bundle.setCommand(kCommandPackage);
+ else if (argv[1][0] == 'c')
+ bundle.setCommand(kCommandCrunch);
else {
fprintf(stderr, "ERROR: Unknown command '%s'\n", argv[1]);
wantUsage = true;
convertPath(argv[0]);
bundle.addResourceSourceDir(argv[0]);
break;
+ case 'C':
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '-C' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ convertPath(argv[0]);
+ bundle.setCrunchedOutputDir(argv[0]);
+ break;
case '0':
argc--;
argv++;
goto bail;
}
bundle.setCustomPackage(argv[0]);
+ } else if (strcmp(cp, "-extra-packages") == 0) {
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '--extra-packages' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ bundle.setExtraPackages(argv[0]);
+ } else if (strcmp(cp, "-generate-dependencies") == 0) {
+ bundle.setGenDependencies(true);
} else if (strcmp(cp, "-utf16") == 0) {
bundle.setWantUTF16(true);
+ } else if (strcmp(cp, "-preferred-configurations") == 0) {
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '--preferred-configurations' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ bundle.addPreferredConfigurations(argv[0]);
} else if (strcmp(cp, "-rename-manifest-package") == 0) {
argc--;
argv++;
bundle.setProduct(argv[0]);
} else if (strcmp(cp, "-non-constant-id") == 0) {
bundle.setNonConstantId(true);
- } else {
+ } else if (strcmp(cp, "-no-crunch") == 0) {
+ bundle.setUseCrunchCache(true);
+ }else {
fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
wantUsage = true;
goto bail;
#include "AaptAssets.h"
#include "ZipFile.h"
+
+/* Benchmarking Flag */
+//#define BENCHMARK 1
+
+#if BENCHMARK
+ #include <time.h>
+#endif /* BENCHMARK */
+
extern int doVersion(Bundle* bundle);
extern int doList(Bundle* bundle);
extern int doDump(Bundle* bundle);
extern int doAdd(Bundle* bundle);
extern int doRemove(Bundle* bundle);
extern int doPackage(Bundle* bundle);
+extern int doCrunch(Bundle* bundle);
extern int calcPercent(long uncompressedLen, long compressedLen);
const sp<AaptAssets>& assets,
const android::String8& outputFile);
+extern android::status_t updatePreProcessedCache(Bundle* bundle);
+
extern android::status_t buildResources(Bundle* bundle,
const sp<AaptAssets>& assets);
String8 getAttribute(const ResXMLTree& tree, const char* ns,
const char* attr, String8* outError);
+status_t writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets,
+ FILE* fp, bool includeRaw);
#endif // __MAIN_H
--- /dev/null
+
+ Copyright (c) 2005-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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
#include "Main.h"
#include "AaptAssets.h"
#include "ResourceTable.h"
+#include "ResourceFilter.h"
#include <utils/Log.h>
#include <utils/threads.h>
/* fwd decls, so I can write this downward */
ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets);
-ssize_t processAssets(Bundle* bundle, ZipFile* zip,
- const sp<AaptDir>& dir, const AaptGroupEntry& ge);
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
+ const AaptGroupEntry& ge, const ResourceFilter* filter);
bool processFile(Bundle* bundle, ZipFile* zip,
const sp<AaptGroup>& group, const sp<AaptFile>& file);
bool okayToCompress(Bundle* bundle, const String8& pathName);
status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets,
const String8& outputFile)
{
+ #if BENCHMARK
+ fprintf(stdout, "BENCHMARK: Starting APK Bundling \n");
+ long startAPKTime = clock();
+ #endif /* BENCHMARK */
+
status_t result = NO_ERROR;
ZipFile* zip = NULL;
int count;
}
}
+ // If we've been asked to generate a dependency file for the .ap_ package,
+ // do so here
+ if (bundle->getGenDependencies()) {
+ // The dependency file gets output to the same directory
+ // as the specified output file with an additional .d extension.
+ // e.g. bin/resources.ap_.d
+ String8 dependencyFile = outputFile;
+ dependencyFile.append(".d");
+
+ FILE* fp = fopen(dependencyFile.string(), "a");
+ // Add this file to the dependency file
+ fprintf(fp, "%s \\\n", outputFile.string());
+ fclose(fp);
+ }
+
assert(result == NO_ERROR);
bail:
if (result == NO_ERROR && bundle->getVerbose())
printf("Done!\n");
+
+ #if BENCHMARK
+ fprintf(stdout, "BENCHMARK: End APK Bundling. Time Elapsed: %f ms \n",(clock() - startAPKTime)/1000.0);
+ #endif /* BENCHMARK */
return result;
}
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);
+
+ ssize_t res = processAssets(bundle, zip, assets, ge, &filter);
if (res < 0) {
return res;
}
+
count += res;
}
return count;
}
-ssize_t processAssets(Bundle* bundle, ZipFile* zip,
- const sp<AaptDir>& dir, const AaptGroupEntry& ge)
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
+ const AaptGroupEntry& ge, const ResourceFilter* filter)
{
ssize_t count = 0;
const size_t ND = dir->getDirs().size();
size_t i;
for (i=0; i<ND; i++) {
- ssize_t res = processAssets(bundle, zip, dir->getDirs().valueAt(i), ge);
+ const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
+
+ const bool filterable = filter != NULL && subDir->getLeaf().find("mipmap-") != 0;
+
+ if (filterable && subDir->getLeaf() != subDir->getPath() && !filter->match(ge.toParams())) {
+ continue;
+ }
+
+ ssize_t res = processAssets(bundle, zip, subDir, ge, filterable ? filter : NULL);
if (res < 0) {
return res;
}
count += res;
}
+ if (filter != NULL && !filter->match(ge.toParams())) {
+ return count;
+ }
+
const size_t NF = dir->getFiles().size();
for (i=0; i<NF; i++) {
sp<AaptGroup> gp = dir->getFiles().valueAt(i);
ssize_t processJarFiles(Bundle* bundle, ZipFile* zip)
{
- ssize_t err;
+ status_t err;
ssize_t count = 0;
const android::Vector<const char*>& jars = bundle->getJarFiles();
ZipFile jar;
err = jar.open(jars[i], ZipFile::kOpenReadOnly);
if (err != 0) {
- fprintf(stderr, "ERROR: unable to open '%s' as a zip file: %zd\n",
+ fprintf(stderr, "ERROR: unable to open '%s' as a zip file: %d\n",
jars[i], err);
return err;
}
#include "ResourceTable.h"
#include "Images.h"
+#include "CrunchCache.h"
+#include "FileFinder.h"
+#include "CacheUpdater.h"
+
+#if HAVE_PRINTF_ZD
+# define ZD "%zd"
+# define ZD_TYPE ssize_t
+#else
+# define ZD "%ld"
+# define ZD_TYPE long
+#endif
+
#define NOISY(x) // x
// ==========================================================================
{
}
+FilePathStore::FilePathStore()
+ :RefBase(),
+ Vector<String8>()
+{
+}
+
class ResourceDirIterator
{
public:
bool isValidResourceType(const String8& type)
{
- return type == "anim" || type == "drawable" || type == "layout"
+ return type == "anim" || type == "animator" || type == "interpolator"
+ || type == "drawable" || type == "layout"
|| type == "values" || type == "xml" || type == "raw"
- || type == "color" || type == "menu";
+ || type == "color" || type == "menu" || type == "mipmap";
}
static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true)
}
static status_t preProcessImages(Bundle* bundle, const sp<AaptAssets>& assets,
- const sp<ResourceTypeSet>& set)
+ const sp<ResourceTypeSet>& set, const char* type)
{
- ResourceDirIterator it(set, String8("drawable"));
- Vector<sp<AaptFile> > newNameFiles;
- Vector<String8> newNamePaths;
bool hasErrors = false;
- ssize_t res;
- while ((res=it.next()) == NO_ERROR) {
- res = preProcessImage(bundle, assets, it.getFile(), NULL);
- if (res < NO_ERROR) {
- hasErrors = true;
+ ssize_t res = NO_ERROR;
+ if (bundle->getUseCrunchCache() == false) {
+ ResourceDirIterator it(set, String8(type));
+ Vector<sp<AaptFile> > newNameFiles;
+ Vector<String8> newNamePaths;
+ while ((res=it.next()) == NO_ERROR) {
+ res = preProcessImage(bundle, assets, it.getFile(), NULL);
+ if (res < NO_ERROR) {
+ hasErrors = true;
+ }
}
}
-
return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
}
if (index < 0) {
sp<ResourceTypeSet> set = new ResourceTypeSet();
+ NOISY(printf("Creating new resource type set for leaf %s with group %s (%p)\n",
+ leafName.string(), group->getPath().string(), group.get()));
set->add(leafName, group);
resources->add(resType, set);
} else {
sp<ResourceTypeSet> set = resources->valueAt(index);
index = set->indexOfKey(leafName);
if (index < 0) {
+ NOISY(printf("Adding to resource type set for leaf %s group %s (%p)\n",
+ leafName.string(), group->getPath().string(), group.get()));
set->add(leafName, group);
} else {
sp<AaptGroup> existingGroup = set->valueAt(index);
- int M = files.size();
- for (int j=0; j<M; j++) {
- existingGroup->addFile(files.valueAt(j));
+ NOISY(printf("Extending to resource type set for leaf %s group %s (%p)\n",
+ leafName.string(), group->getPath().string(), group.get()));
+ for (size_t j=0; j<files.size(); j++) {
+ NOISY(printf("Adding file %s in group %s resType %s\n",
+ files.valueAt(j)->getSourceFile().string(),
+ files.keyAt(j).toDirName(String8()).string(),
+ resType.string()));
+ status_t err = existingGroup->addFile(files.valueAt(j));
}
}
}
for (int i=0; i<N; i++) {
sp<AaptDir> d = dirs.itemAt(i);
+ NOISY(printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(),
+ d->getLeaf().string()));
collect_files(d, resources);
// don't try to include the res dir
+ NOISY(printf("Removing dir leaf %s\n", d->getLeaf().string()));
ass->removeDir(d->getLeaf());
}
}
DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles =
baseGroup->getFiles();
for (size_t i=0; i < baseFiles.size(); i++) {
- printf("baseFile %ld has flavor %s\n", i,
+ printf("baseFile " ZD " has flavor %s\n", (ZD_TYPE) i,
baseFiles.keyAt(i).toString().string());
}
for (size_t i=0; i < overlayFiles.size(); i++) {
- printf("overlayFile %ld has flavor %s\n", i,
+ printf("overlayFile " ZD " has flavor %s\n", (ZD_TYPE) i,
overlayFiles.keyAt(i).toString().string());
}
}
size_t baseFileIndex =
baseGroup->getFiles().indexOfKey(overlayFiles.
keyAt(overlayGroupIndex));
- if(baseFileIndex < UNKNOWN_ERROR) {
+ if (baseFileIndex < UNKNOWN_ERROR) {
if (bundle->getVerbose()) {
- printf("found a match (%ld) for overlay file %s, for flavor %s\n",
- baseFileIndex,
+ printf("found a match (" ZD ") for overlay file %s, for flavor %s\n",
+ (ZD_TYPE) baseFileIndex,
overlayGroup->getLeaf().string(),
overlayFiles.keyAt(overlayGroupIndex).toString().string());
}
baseGroup->removeFile(baseFileIndex);
} else {
// didn't find a match fall through and add it..
+ if (true || bundle->getVerbose()) {
+ printf("nothing matches overlay file %s, for flavor %s\n",
+ overlayGroup->getLeaf().string(),
+ overlayFiles.keyAt(overlayGroupIndex).toString().string());
+ }
}
baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex));
assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
} \
} while (0)
+status_t updatePreProcessedCache(Bundle* bundle)
+{
+ #if BENCHMARK
+ fprintf(stdout, "BENCHMARK: Starting PNG PreProcessing \n");
+ long startPNGTime = clock();
+ #endif /* BENCHMARK */
+
+ String8 source(bundle->getResourceSourceDirs()[0]);
+ String8 dest(bundle->getCrunchedOutputDir());
+
+ FileFinder* ff = new SystemFileFinder();
+ CrunchCache cc(source,dest,ff);
+
+ CacheUpdater* cu = new SystemCacheUpdater(bundle);
+ size_t numFiles = cc.crunch(cu);
+
+ if (bundle->getVerbose())
+ fprintf(stdout, "Crunched %d PNG files to update cache\n", (int)numFiles);
+
+ delete ff;
+ delete cu;
+
+ #if BENCHMARK
+ fprintf(stdout, "BENCHMARK: End PNG PreProcessing. Time Elapsed: %f ms \n"
+ ,(clock() - startPNGTime)/1000.0);
+ #endif /* BENCHMARK */
+ return 0;
+}
+
status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
{
// First, look for a package file to parse. This is required to
sp<ResourceTypeSet> drawables;
sp<ResourceTypeSet> layouts;
sp<ResourceTypeSet> anims;
+ sp<ResourceTypeSet> animators;
+ sp<ResourceTypeSet> interpolators;
sp<ResourceTypeSet> xmls;
sp<ResourceTypeSet> raws;
sp<ResourceTypeSet> colors;
sp<ResourceTypeSet> menus;
+ sp<ResourceTypeSet> mipmaps;
ASSIGN_IT(drawable);
ASSIGN_IT(layout);
ASSIGN_IT(anim);
+ ASSIGN_IT(animator);
+ ASSIGN_IT(interpolator);
ASSIGN_IT(xml);
ASSIGN_IT(raw);
ASSIGN_IT(color);
ASSIGN_IT(menu);
+ ASSIGN_IT(mipmap);
assets->setResources(resources);
// now go through any resource overlays and collect their files
if (!applyFileOverlay(bundle, assets, &drawables, "drawable") ||
!applyFileOverlay(bundle, assets, &layouts, "layout") ||
!applyFileOverlay(bundle, assets, &anims, "anim") ||
+ !applyFileOverlay(bundle, assets, &animators, "animator") ||
+ !applyFileOverlay(bundle, assets, &interpolators, "interpolator") ||
!applyFileOverlay(bundle, assets, &xmls, "xml") ||
!applyFileOverlay(bundle, assets, &raws, "raw") ||
!applyFileOverlay(bundle, assets, &colors, "color") ||
- !applyFileOverlay(bundle, assets, &menus, "menu")) {
+ !applyFileOverlay(bundle, assets, &menus, "menu") ||
+ !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) {
return UNKNOWN_ERROR;
}
if (drawables != NULL) {
if (bundle->getOutputAPKFile() != NULL) {
- err = preProcessImages(bundle, assets, drawables);
+ err = preProcessImages(bundle, assets, drawables, "drawable");
}
if (err == NO_ERROR) {
err = makeFileResources(bundle, assets, &table, drawables, "drawable");
}
}
+ if (mipmaps != NULL) {
+ if (bundle->getOutputAPKFile() != NULL) {
+ err = preProcessImages(bundle, assets, mipmaps, "mipmap");
+ }
+ if (err == NO_ERROR) {
+ err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap");
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ } else {
+ hasErrors = true;
+ }
+ }
+
if (layouts != NULL) {
err = makeFileResources(bundle, assets, &table, layouts, "layout");
if (err != NO_ERROR) {
}
}
+ if (animators != NULL) {
+ err = makeFileResources(bundle, assets, &table, animators, "animator");
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
+ if (interpolators != NULL) {
+ err = makeFileResources(bundle, assets, &table, interpolators, "interpolator");
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
if (xmls != NULL) {
err = makeFileResources(bundle, assets, &table, xmls, "xml");
if (err != NO_ERROR) {
err = NO_ERROR;
}
+ if (animators != NULL) {
+ ResourceDirIterator it(animators, String8("animator"));
+ while ((err=it.next()) == NO_ERROR) {
+ err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
+ if (err < NO_ERROR) {
+ hasErrors = true;
+ }
+ err = NO_ERROR;
+ }
+
+ if (interpolators != NULL) {
+ ResourceDirIterator it(interpolators, String8("interpolator"));
+ while ((err=it.next()) == NO_ERROR) {
+ err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
+ if (err < NO_ERROR) {
+ hasErrors = true;
+ }
+ err = NO_ERROR;
+ }
+
if (xmls != NULL) {
ResourceDirIterator it(xmls, String8("xml"));
while ((err=it.next()) == NO_ERROR) {
return err;
}
fclose(fp);
+
+ // If we were asked to generate a dependency file, we'll go ahead and add this R.java
+ // as a target in the dependency file right next to it.
+ if (bundle->getGenDependencies()) {
+ // Add this R.java to the dependency file
+ String8 dependencyFile(bundle->getRClassDir());
+ dependencyFile.appendPath("R.java.d");
+
+ fp = fopen(dependencyFile.string(), "a");
+ fprintf(fp,"%s \\\n", dest.string());
+ fclose(fp);
+ }
}
return NO_ERROR;
// tag:attribute pairs that should be checked in layout files.
KeyedVector<String8, NamespaceAttributePair> kLayoutTagAttrPairs;
addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class");
+ addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class");
addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name");
// tag:attribute pairs that should be checked in xml files.
KeyedVector<String8, NamespaceAttributePair> kXmlTagAttrPairs;
addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment");
- addTagAttrPair(&kXmlTagAttrPairs, "Header", RESOURCES_ANDROID_NAMESPACE, "fragment");
+ addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment");
const Vector<sp<AaptDir> >& dirs = assets->resDirs();
const size_t K = dirs.size();
}
}
}
+ // Handle the overlays
+ sp<AaptAssets> overlay = assets->getOverlay();
+ if (overlay.get()) {
+ return writeProguardForLayouts(keep, overlay);
+ }
return NO_ERROR;
}
return err;
}
+
+// Loops through the string paths and writes them to the file pointer
+// Each file path is written on its own line with a terminating backslash.
+status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp)
+{
+ status_t deps = -1;
+ for (size_t file_i = 0; file_i < files->size(); ++file_i) {
+ // Add the full file path to the dependency file
+ fprintf(fp, "%s \\\n", files->itemAt(file_i).string());
+ deps++;
+ }
+ return deps;
+}
+
+status_t
+writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw)
+{
+ status_t deps = -1;
+ deps += writePathsToFile(assets->getFullResPaths(), fp);
+ if (includeRaw) {
+ deps += writePathsToFile(assets->getFullAssetPaths(), fp);
+ }
+ return deps;
+}
--- /dev/null
+//
+// Copyright 2011 The Android Open Source Project
+//
+// Build resource files from raw assets.
+//
+
+#include "ResourceFilter.h"
+
+status_t
+ResourceFilter::parse(const char* arg)
+{
+ if (arg == NULL) {
+ return 0;
+ }
+
+ const char* p = arg;
+ const char* q;
+
+ while (true) {
+ q = strchr(p, ',');
+ if (q == NULL) {
+ q = p + strlen(p);
+ }
+
+ String8 part(p, q-p);
+
+ if (part == "zz_ZZ") {
+ mContainsPseudo = true;
+ }
+ int axis;
+ uint32_t value;
+ if (AaptGroupEntry::parseNamePart(part, &axis, &value)) {
+ fprintf(stderr, "Invalid configuration: %s\n", arg);
+ fprintf(stderr, " ");
+ for (int i=0; i<p-arg; i++) {
+ fprintf(stderr, " ");
+ }
+ for (int i=0; i<q-p; i++) {
+ fprintf(stderr, "^");
+ }
+ fprintf(stderr, "\n");
+ return 1;
+ }
+
+ ssize_t index = mData.indexOfKey(axis);
+ if (index < 0) {
+ mData.add(axis, SortedVector<uint32_t>());
+ }
+ SortedVector<uint32_t>& sv = mData.editValueFor(axis);
+ sv.add(value);
+ // if it's a locale with a region, also match an unmodified locale of the
+ // same language
+ if (axis == AXIS_LANGUAGE) {
+ if (value & 0xffff0000) {
+ sv.add(value & 0x0000ffff);
+ }
+ }
+ p = q;
+ if (!*p) break;
+ p++;
+ }
+
+ return NO_ERROR;
+}
+
+bool
+ResourceFilter::isEmpty() const
+{
+ return mData.size() == 0;
+}
+
+bool
+ResourceFilter::match(int axis, uint32_t value) const
+{
+ if (value == 0) {
+ // they didn't specify anything so take everything
+ return true;
+ }
+ ssize_t index = mData.indexOfKey(axis);
+ if (index < 0) {
+ // we didn't request anything on this axis so take everything
+ return true;
+ }
+ const SortedVector<uint32_t>& sv = mData.valueAt(index);
+ return sv.indexOf(value) >= 0;
+}
+
+bool
+ResourceFilter::match(int axis, const ResTable_config& config) const
+{
+ return match(axis, AaptGroupEntry::getConfigValueForAxis(config, axis));
+}
+
+bool
+ResourceFilter::match(const ResTable_config& config) const
+{
+ for (int i=AXIS_START; i<=AXIS_END; i++) {
+ if (!match(i, AaptGroupEntry::getConfigValueForAxis(config, i))) {
+ return false;
+ }
+ }
+ return true;
+}
+
+const SortedVector<uint32_t>* ResourceFilter::configsForAxis(int axis) const
+{
+ ssize_t index = mData.indexOfKey(axis);
+ if (index < 0) {
+ return NULL;
+ }
+ return &mData.valueAt(index);
+}
--- /dev/null
+//
+// Copyright 2011 The Android Open Source Project
+//
+// Build resource files from raw assets.
+//
+
+#ifndef RESOURCE_FILTER_H
+#define RESOURCE_FILTER_H
+
+#include "AaptAssets.h"
+
+/**
+ * Implements logic for parsing and handling "-c" and "--preferred-configurations"
+ * options.
+ */
+class ResourceFilter
+{
+public:
+ ResourceFilter() : mData(), mContainsPseudo(false) {}
+ status_t parse(const char* arg);
+ bool isEmpty() const;
+ bool match(int axis, uint32_t value) const;
+ bool match(int axis, const ResTable_config& config) const;
+ bool match(const ResTable_config& config) const;
+ const SortedVector<uint32_t>* configsForAxis(int axis) const;
+ inline bool containsPseudo() const { return mContainsPseudo; }
+
+private:
+ KeyedVector<int,SortedVector<uint32_t> > mData;
+ bool mContainsPseudo;
+};
+
+#endif
#include "ResourceTable.h"
#include "XMLNode.h"
+#include "ResourceFilter.h"
#include <utils/ByteOrder.h>
#include <utils/ResourceTypes.h>
if (isInProductList(product, String16(bundleProduct))) {
;
} else if (strcmp16(String16("default").string(), product.string()) == 0 &&
- !outTable->hasBagOrEntry(myPackage, curType, ident)) {
+ !outTable->hasBagOrEntry(myPackage, curType, ident, config)) {
;
} else {
return NO_ERROR;
return false;
}
+bool ResourceTable::hasBagOrEntry(const String16& package,
+ const String16& type,
+ const String16& name,
+ const ResTable_config& config) const
+{
+ // First look for this in the included resources...
+ uint32_t rid = mAssets->getIncludedResources()
+ .identifierForName(name.string(), name.size(),
+ type.string(), type.size(),
+ package.string(), package.size());
+ if (rid != 0) {
+ return true;
+ }
+
+ sp<Package> p = mPackages.valueFor(package);
+ if (p != NULL) {
+ sp<Type> t = p->getTypes().valueFor(type);
+ if (t != NULL) {
+ sp<ConfigList> c = t->getConfigs().valueFor(name);
+ if (c != NULL) {
+ sp<Entry> e = c->getEntries().valueFor(config);
+ if (e != NULL) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
bool ResourceTable::hasBagOrEntry(const String16& ref,
const String16* defType,
const String16* defPackage)
bool onlyPublic) const
{
String16 package, type, name;
+ bool refOnlyPublic = true;
if (!ResTable::expandResourceRef(
ref.string(), ref.size(), &package, &type, &name,
defType, defPackage ? defPackage:&mAssetsPackage,
- outErrorMsg)) {
+ outErrorMsg, &refOnlyPublic)) {
NOISY(printf("Expanding resource: ref=%s\n",
String8(ref).string()));
NOISY(printf("Expanding resource: defType=%s\n",
String8(name).string()));
return 0;
}
- uint32_t res = getResId(package, type, name, onlyPublic);
+ uint32_t res = getResId(package, type, name, onlyPublic && refOnlyPublic);
NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n",
String8(package).string(), String8(type).string(),
String8(name).string(), res));
if (configSet.count(defaultLocale) == 0) {
fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
- for (set<String8>::iterator locales = configSet.begin();
+ for (set<String8>::const_iterator locales = configSet.begin();
locales != configSet.end();
locales++) {
fprintf(stdout, " %s", (*locales).string());
return err;
}
-
-status_t
-ResourceFilter::parse(const char* arg)
-{
- if (arg == NULL) {
- return 0;
- }
-
- const char* p = arg;
- const char* q;
-
- while (true) {
- q = strchr(p, ',');
- if (q == NULL) {
- q = p + strlen(p);
- }
-
- String8 part(p, q-p);
-
- if (part == "zz_ZZ") {
- mContainsPseudo = true;
- }
- int axis;
- uint32_t value;
- if (AaptGroupEntry::parseNamePart(part, &axis, &value)) {
- fprintf(stderr, "Invalid configuration: %s\n", arg);
- fprintf(stderr, " ");
- for (int i=0; i<p-arg; i++) {
- fprintf(stderr, " ");
- }
- for (int i=0; i<q-p; i++) {
- fprintf(stderr, "^");
- }
- fprintf(stderr, "\n");
- return 1;
- }
-
- ssize_t index = mData.indexOfKey(axis);
- if (index < 0) {
- mData.add(axis, SortedVector<uint32_t>());
- }
- SortedVector<uint32_t>& sv = mData.editValueFor(axis);
- sv.add(value);
- // if it's a locale with a region, also match an unmodified locale of the
- // same language
- if (axis == AXIS_LANGUAGE) {
- if (value & 0xffff0000) {
- sv.add(value & 0x0000ffff);
- }
- }
- p = q;
- if (!*p) break;
- p++;
- }
-
- return NO_ERROR;
-}
-
-bool
-ResourceFilter::match(int axis, uint32_t value)
-{
- if (value == 0) {
- // they didn't specify anything so take everything
- return true;
- }
- ssize_t index = mData.indexOfKey(axis);
- if (index < 0) {
- // we didn't request anything on this axis so take everything
- return true;
- }
- const SortedVector<uint32_t>& sv = mData.valueAt(index);
- return sv.indexOf(value) >= 0;
-}
-
-bool
-ResourceFilter::match(const ResTable_config& config)
-{
- if (config.locale) {
- uint32_t locale = (config.country[1] << 24) | (config.country[0] << 16)
- | (config.language[1] << 8) | (config.language[0]);
- if (!match(AXIS_LANGUAGE, locale)) {
- return false;
- }
- }
- if (!match(AXIS_ORIENTATION, config.orientation)) {
- return false;
- }
- if (!match(AXIS_UIMODETYPE, (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE))) {
- return false;
- }
- if (!match(AXIS_UIMODENIGHT, (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT))) {
- return false;
- }
- if (!match(AXIS_DENSITY, config.density)) {
- return false;
- }
- if (!match(AXIS_TOUCHSCREEN, config.touchscreen)) {
- return false;
- }
- if (!match(AXIS_KEYSHIDDEN, config.inputFlags)) {
- return false;
- }
- if (!match(AXIS_KEYBOARD, config.keyboard)) {
- return false;
- }
- if (!match(AXIS_NAVIGATION, config.navigation)) {
- return false;
- }
- if (!match(AXIS_SCREENSIZE, config.screenSize)) {
- return false;
- }
- if (!match(AXIS_VERSION, config.version)) {
- return false;
- }
- return true;
-}
-
status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
{
ResourceFilter filter;
const size_t N = mOrderedPackages.size();
size_t pi;
+ const static String16 mipmap16("mipmap");
+
bool useUTF8 = !bundle->getWantUTF16() && bundle->isMinSdkAtLeast(SDK_FROYO);
// Iterate through all data, collecting all values (strings,
typeStrings.add(String16("<empty>"), false);
continue;
}
- typeStrings.add(t->getName(), false);
+ const String16 typeName(t->getName());
+ typeStrings.add(typeName, false);
+
+ const bool filterable = (typeName != mipmap16);
const size_t N = t->getOrderedConfigs().size();
for (size_t ci=0; ci<N; ci++) {
const size_t N = c->getEntries().size();
for (size_t ei=0; ei<N; ei++) {
ConfigDescription config = c->getEntries().keyAt(ei);
- if (!filter.match(config)) {
+ if (filterable && !filter.match(config)) {
continue;
}
sp<Entry> e = c->getEntries().valueAt(ei);
"Type name %s not found",
String8(typeName).string());
+ const bool filterable = (typeName != mipmap16);
+
const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
// First write the typeSpec chunk, containing information about
(((uint8_t*)data->editData())
+ typeSpecStart + sizeof(ResTable_typeSpec));
memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
-
+
for (size_t ei=0; ei<N; ei++) {
sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
if (cl->getPublic()) {
}
const size_t CN = cl->getEntries().size();
for (size_t ci=0; ci<CN; ci++) {
- if (!filter.match(cl->getEntries().keyAt(ci))) {
+ if (filterable && !filter.match(cl->getEntries().keyAt(ci))) {
continue;
}
for (size_t cj=ci+1; cj<CN; cj++) {
- if (!filter.match(cl->getEntries().keyAt(cj))) {
+ if (filterable && !filter.match(cl->getEntries().keyAt(cj))) {
continue;
}
typeSpecFlags[ei] |= htodl(
ConfigDescription config = t->getUniqueConfigs().itemAt(ci);
NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
- "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
+ "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
+ "sw%ddp w%ddp h%ddp\n",
ti+1,
config.mcc, config.mnc,
config.language[0] ? config.language[0] : '-',
config.inputFlags,
config.navigation,
config.screenWidth,
- config.screenHeight));
+ config.screenHeight,
+ config.smallestScreenWidthDp,
+ config.screenWidthDp,
+ config.screenHeightDp));
- if (!filter.match(config)) {
+ if (filterable && !filter.match(config)) {
continue;
}
tHeader->entriesStart = htodl(typeSize);
tHeader->config = config;
NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
- "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
+ "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
+ "sw%ddp w%ddp h%ddp\n",
ti+1,
tHeader->config.mcc, tHeader->config.mnc,
tHeader->config.language[0] ? tHeader->config.language[0] : '-',
tHeader->config.inputFlags,
tHeader->config.navigation,
tHeader->config.screenWidth,
- tHeader->config.screenHeight));
+ tHeader->config.screenHeight,
+ tHeader->config.smallestScreenWidthDp,
+ tHeader->config.screenWidthDp,
+ tHeader->config.screenHeightDp));
tHeader->config.swapHtoD();
// Build the entries inside of this type.
if (e == NULL) {
if (config != NULL) {
NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
- "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
+ "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
+ "sw%ddp w%ddp h%ddp\n",
sourcePos.file.string(), sourcePos.line,
config->mcc, config->mnc,
config->language[0] ? config->language[0] : '-',
config->inputFlags,
config->navigation,
config->screenWidth,
- config->screenHeight));
+ config->screenHeight,
+ config->smallestScreenWidthDp,
+ config->screenWidthDp,
+ config->screenHeightDp));
} else {
NOISY(printf("New entry at %s:%d: NULL config\n",
sourcePos.file.string(), sourcePos.line));
const String16& type,
const String16& name) const;
+ bool hasBagOrEntry(const String16& package,
+ const String16& type,
+ const String16& name,
+ const ResTable_config& config) const;
+
bool hasBagOrEntry(const String16& ref,
const String16* defType = NULL,
const String16* defPackage = NULL);
uint32_t getResId(const String16& package,
const String16& type,
const String16& name,
- bool onlyPublic = false) const;
+ bool onlyPublic = true) const;
uint32_t getResId(const String16& ref,
const String16* defType = NULL,
const String16* defPackage = NULL,
const char** outErrorMsg = NULL,
- bool onlyPublic = false) const;
+ bool onlyPublic = true) const;
static bool isValidResourceName(const String16& s);
map<String16, set<String8> > mLocalizations;
};
-class ResourceFilter
-{
-public:
- ResourceFilter() : mData(), mContainsPseudo(false) {}
- status_t parse(const char* arg);
- bool match(int axis, uint32_t value);
- bool match(const ResTable_config& config);
- inline bool containsPseudo() { return mContainsPseudo; }
-
-private:
- KeyedVector<int,SortedVector<uint32_t> > mData;
- bool mContainsPseudo;
-};
-
-
#endif
#include <utils/ByteOrder.h>
+#if HAVE_PRINTF_ZD
+# define ZD "%zd"
+# define ZD_TYPE ssize_t
+#else
+# define ZD "%ld"
+# define ZD_TYPE long
+#endif
+
#define NOISY(x) //x
void strcpy16_htod(uint16_t* dst, const uint16_t* src)
str = String8(pool->stringAt(s, &len)).string();
}
- printf("String #%ld: %s\n", s, str);
+ printf("String #" ZD ": %s\n", (ZD_TYPE) s, str);
}
}
namespaces.pop();
} else if (code == ResXMLTree::TEXT) {
size_t len;
- printf("%sC: \"%s\"\n", prefix.string(), ResTable::normalizeForOutput(
- String8(block->getText(&len)).string()).string());
+ printf("%sC: \"%s\"\n", prefix.string(), String8(block->getText(&len)).string());
}
}
long posn;
bool hasDD;
- //LOGV("initFromCDE ---\n");
+ //ALOGV("initFromCDE ---\n");
/* read the CDE */
result = mCDE.read(fp);
if (result != NO_ERROR) {
- LOGD("mCDE.read failed\n");
+ ALOGD("mCDE.read failed\n");
return result;
}
/* using the info in the CDE, go load up the LFH */
posn = ftell(fp);
if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
- LOGD("local header seek failed (%ld)\n",
+ ALOGD("local header seek failed (%ld)\n",
mCDE.mLocalHeaderRelOffset);
return UNKNOWN_ERROR;
}
result = mLFH.read(fp);
if (result != NO_ERROR) {
- LOGD("mLFH.read failed\n");
+ ALOGD("mLFH.read failed\n");
return result;
}
hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
if (hasDD) {
// do something clever
- //LOGD("+++ has data descriptor\n");
+ //ALOGD("+++ has data descriptor\n");
}
/*
* prefer the CDE values.)
*/
if (!hasDD && !compareHeaders()) {
- LOGW("warning: header mismatch\n");
+ ALOGW("warning: header mismatch\n");
// keep going?
}
if (padding <= 0)
return INVALID_OPERATION;
- //LOGI("HEY: adding %d pad bytes to existing %d in %s\n",
+ //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n",
// padding, mLFH.mExtraFieldLength, mCDE.mFileName);
if (mLFH.mExtraFieldLength > 0) {
bool ZipEntry::compareHeaders(void) const
{
if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
- LOGV("cmp: VersionToExtract\n");
+ ALOGV("cmp: VersionToExtract\n");
return false;
}
if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
- LOGV("cmp: GPBitFlag\n");
+ ALOGV("cmp: GPBitFlag\n");
return false;
}
if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
- LOGV("cmp: CompressionMethod\n");
+ ALOGV("cmp: CompressionMethod\n");
return false;
}
if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
- LOGV("cmp: LastModFileTime\n");
+ ALOGV("cmp: LastModFileTime\n");
return false;
}
if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
- LOGV("cmp: LastModFileDate\n");
+ ALOGV("cmp: LastModFileDate\n");
return false;
}
if (mCDE.mCRC32 != mLFH.mCRC32) {
- LOGV("cmp: CRC32\n");
+ ALOGV("cmp: CRC32\n");
return false;
}
if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
- LOGV("cmp: CompressedSize\n");
+ ALOGV("cmp: CompressedSize\n");
return false;
}
if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
- LOGV("cmp: UncompressedSize\n");
+ ALOGV("cmp: UncompressedSize\n");
return false;
}
if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
- LOGV("cmp: FileNameLength\n");
+ ALOGV("cmp: FileNameLength\n");
return false;
}
#if 0 // this seems to be used for padding, not real data
if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
- LOGV("cmp: ExtraFieldLength\n");
+ ALOGV("cmp: ExtraFieldLength\n");
return false;
}
#endif
if (mCDE.mFileName != NULL) {
if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
- LOGV("cmp: FileName\n");
+ ALOGV("cmp: FileName\n");
return false;
}
}
}
if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
- LOGD("whoops: didn't find expected signature\n");
+ ALOGD("whoops: didn't find expected signature\n");
result = UNKNOWN_ERROR;
goto bail;
}
*/
void ZipEntry::LocalFileHeader::dump(void) const
{
- LOGD(" LocalFileHeader contents:\n");
- LOGD(" versToExt=%u gpBits=0x%04x compression=%u\n",
+ ALOGD(" LocalFileHeader contents:\n");
+ ALOGD(" versToExt=%u gpBits=0x%04x compression=%u\n",
mVersionToExtract, mGPBitFlag, mCompressionMethod);
- LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
+ ALOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
mLastModFileTime, mLastModFileDate, mCRC32);
- LOGD(" compressedSize=%lu uncompressedSize=%lu\n",
+ ALOGD(" compressedSize=%lu uncompressedSize=%lu\n",
mCompressedSize, mUncompressedSize);
- LOGD(" filenameLen=%u extraLen=%u\n",
+ ALOGD(" filenameLen=%u extraLen=%u\n",
mFileNameLength, mExtraFieldLength);
if (mFileName != NULL)
- LOGD(" filename: '%s'\n", mFileName);
+ ALOGD(" filename: '%s'\n", mFileName);
}
}
if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
- LOGD("Whoops: didn't find expected signature\n");
+ ALOGD("Whoops: didn't find expected signature\n");
result = UNKNOWN_ERROR;
goto bail;
}
*/
void ZipEntry::CentralDirEntry::dump(void) const
{
- LOGD(" CentralDirEntry contents:\n");
- LOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
+ ALOGD(" CentralDirEntry contents:\n");
+ ALOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
- LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
+ ALOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
mLastModFileTime, mLastModFileDate, mCRC32);
- LOGD(" compressedSize=%lu uncompressedSize=%lu\n",
+ ALOGD(" compressedSize=%lu uncompressedSize=%lu\n",
mCompressedSize, mUncompressedSize);
- LOGD(" filenameLen=%u extraLen=%u commentLen=%u\n",
+ ALOGD(" filenameLen=%u extraLen=%u commentLen=%u\n",
mFileNameLength, mExtraFieldLength, mFileCommentLength);
- LOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
+ ALOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
mDiskNumberStart, mInternalAttrs, mExternalAttrs,
mLocalHeaderRelOffset);
if (mFileName != NULL)
- LOGD(" filename: '%s'\n", mFileName);
+ ALOGD(" filename: '%s'\n", mFileName);
if (mFileComment != NULL)
- LOGD(" comment: '%s'\n", mFileComment);
+ ALOGD(" comment: '%s'\n", mFileComment);
}
newArchive = (access(zipFileName, F_OK) != 0);
if (!(flags & kOpenCreate) && newArchive) {
/* not creating, must already exist */
- LOGD("File %s does not exist", zipFileName);
+ ALOGD("File %s does not exist", zipFileName);
return NAME_NOT_FOUND;
}
}
mZipFp = fopen(zipFileName, openflags);
if (mZipFp == NULL) {
int err = errno;
- LOGD("fopen failed: %d\n", err);
+ ALOGD("fopen failed: %d\n", err);
return errnoToStatus(err);
}
/* too small to be a ZIP archive? */
if (fileLength < EndOfCentralDir::kEOCDLen) {
- LOGD("Length is %ld -- too small\n", (long)fileLength);
+ ALOGD("Length is %ld -- too small\n", (long)fileLength);
result = INVALID_OPERATION;
goto bail;
}
buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch];
if (buf == NULL) {
- LOGD("Failure allocating %d bytes for EOCD search",
+ ALOGD("Failure allocating %d bytes for EOCD search",
EndOfCentralDir::kMaxEOCDSearch);
result = NO_MEMORY;
goto bail;
readAmount = (long) fileLength;
}
if (fseek(mZipFp, seekStart, SEEK_SET) != 0) {
- LOGD("Failure seeking to end of zip at %ld", (long) seekStart);
+ ALOGD("Failure seeking to end of zip at %ld", (long) seekStart);
result = UNKNOWN_ERROR;
goto bail;
}
/* read the last part of the file into the buffer */
if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) {
- LOGD("short file? wanted %ld\n", readAmount);
+ ALOGD("short file? wanted %ld\n", readAmount);
result = UNKNOWN_ERROR;
goto bail;
}
if (buf[i] == 0x50 &&
ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)
{
- LOGV("+++ Found EOCD at buf+%d\n", i);
+ ALOGV("+++ Found EOCD at buf+%d\n", i);
break;
}
}
if (i < 0) {
- LOGD("EOCD not found, not Zip\n");
+ ALOGD("EOCD not found, not Zip\n");
result = INVALID_OPERATION;
goto bail;
}
/* extract eocd values */
result = mEOCD.readBuf(buf + i, readAmount - i);
if (result != NO_ERROR) {
- LOGD("Failure reading %ld bytes of EOCD values", readAmount - i);
+ ALOGD("Failure reading %ld bytes of EOCD values", readAmount - i);
goto bail;
}
//mEOCD.dump();
if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 ||
mEOCD.mNumEntries != mEOCD.mTotalNumEntries)
{
- LOGD("Archive spanning not supported\n");
+ ALOGD("Archive spanning not supported\n");
result = INVALID_OPERATION;
goto bail;
}
* we're hoping to preserve.
*/
if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
- LOGD("Failure seeking to central dir offset %ld\n",
+ ALOGD("Failure seeking to central dir offset %ld\n",
mEOCD.mCentralDirOffset);
result = UNKNOWN_ERROR;
goto bail;
/*
* Loop through and read the central dir entries.
*/
- LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries);
+ ALOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries);
int entry;
for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {
ZipEntry* pEntry = new ZipEntry;
result = pEntry->initFromCDE(mZipFp);
if (result != NO_ERROR) {
- LOGD("initFromCDE failed\n");
+ ALOGD("initFromCDE failed\n");
delete pEntry;
goto bail;
}
{
unsigned char checkBuf[4];
if (fread(checkBuf, 1, 4, mZipFp) != 4) {
- LOGD("EOCD check read failed\n");
+ ALOGD("EOCD check read failed\n");
result = INVALID_OPERATION;
goto bail;
}
if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {
- LOGD("EOCD read check failed\n");
+ ALOGD("EOCD read check failed\n");
result = UNKNOWN_ERROR;
goto bail;
}
- LOGV("+++ EOCD read check passed\n");
+ ALOGV("+++ EOCD read check passed\n");
}
bail:
bool failed = false;
result = compressFpToFp(mZipFp, inputFp, data, size, &crc);
if (result != NO_ERROR) {
- LOGD("compression failed, storing\n");
+ ALOGD("compression failed, storing\n");
failed = true;
} else {
/*
long src = inputFp ? ftell(inputFp) : size;
long dst = ftell(mZipFp) - startPosn;
if (dst + (dst / 10) > src) {
- LOGD("insufficient compression (src=%ld dst=%ld), storing\n",
+ ALOGD("insufficient compression (src=%ld dst=%ld), storing\n",
src, dst);
failed = true;
}
}
if (result != NO_ERROR) {
// don't need to truncate; happens in CDE rewrite
- LOGD("failed copying data in\n");
+ ALOGD("failed copying data in\n");
goto bail;
}
}
scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen,
&compressedLen, &crc);
if (!scanResult || method != ZipEntry::kCompressDeflated) {
- LOGD("this isn't a deflated gzip file?");
+ ALOGD("this isn't a deflated gzip file?");
result = UNKNOWN_ERROR;
goto bail;
}
result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL);
if (result != NO_ERROR) {
- LOGD("failed copying gzip data in\n");
+ ALOGD("failed copying gzip data in\n");
goto bail;
}
} else {
if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)
!= NO_ERROR)
{
- LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
+ ALOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
result = UNKNOWN_ERROR;
goto bail;
}
*pCRC32 = crc32(*pCRC32, tmpBuf, count);
if (fwrite(tmpBuf, 1, count, dstFp) != count) {
- LOGD("fwrite %d bytes failed\n", (int) count);
+ ALOGD("fwrite %d bytes failed\n", (int) count);
return UNKNOWN_ERROR;
}
}
if (size > 0) {
*pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size);
if (fwrite(data, 1, size, dstFp) != size) {
- LOGD("fwrite %d bytes failed\n", (int) size);
+ ALOGD("fwrite %d bytes failed\n", (int) size);
return UNKNOWN_ERROR;
}
}
count = fread(tmpBuf, 1, readSize, srcFp);
if ((long) count != readSize) { // error or unexpected EOF
- LOGD("fread %d bytes failed\n", (int) readSize);
+ ALOGD("fread %d bytes failed\n", (int) readSize);
return UNKNOWN_ERROR;
}
*pCRC32 = crc32(*pCRC32, tmpBuf, count);
if (fwrite(tmpBuf, 1, count, dstFp) != count) {
- LOGD("fwrite %d bytes failed\n", (int) count);
+ ALOGD("fwrite %d bytes failed\n", (int) count);
return UNKNOWN_ERROR;
}
if (zerr != Z_OK) {
result = UNKNOWN_ERROR;
if (zerr == Z_VERSION_ERROR) {
- LOGE("Installed zlib is not compatible with linked version (%s)\n",
+ ALOGE("Installed zlib is not compatible with linked version (%s)\n",
ZLIB_VERSION);
} else {
- LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr);
+ ALOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr);
}
goto bail;
}
/* only read if the input buffer is empty */
if (zstream.avail_in == 0 && !atEof) {
- LOGV("+++ reading %d bytes\n", (int)kBufSize);
+ ALOGV("+++ reading %d bytes\n", (int)kBufSize);
if (data) {
getSize = size > kBufSize ? kBufSize : size;
memcpy(inBuf, data, getSize);
} else {
getSize = fread(inBuf, 1, kBufSize, srcFp);
if (ferror(srcFp)) {
- LOGD("deflate read failed (errno=%d)\n", errno);
+ ALOGD("deflate read failed (errno=%d)\n", errno);
goto z_bail;
}
}
if (getSize < kBufSize) {
- LOGV("+++ got %d bytes, EOF reached\n",
+ ALOGV("+++ got %d bytes, EOF reached\n",
(int)getSize);
atEof = true;
}
zerr = deflate(&zstream, flush);
if (zerr != Z_OK && zerr != Z_STREAM_END) {
- LOGD("zlib deflate call failed (zerr=%d)\n", zerr);
+ ALOGD("zlib deflate call failed (zerr=%d)\n", zerr);
result = UNKNOWN_ERROR;
goto z_bail;
}
if (zstream.avail_out == 0 ||
(zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize))
{
- LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf));
+ ALOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf));
if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) !=
(size_t)(zstream.next_out - outBuf))
{
- LOGD("write %d failed in deflate\n",
+ ALOGD("write %d failed in deflate\n",
(int) (zstream.next_out - outBuf));
goto z_bail;
}
* of wasted space at the end of the file. Remove it now.
*/
if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) {
- LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno));
+ ALOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno));
// not fatal
}
pEntry->getLFHOffset(), span);
if (result != NO_ERROR) {
/* this is why you use a temp file */
- LOGE("error during crunch - archive is toast\n");
+ ALOGE("error during crunch - archive is toast\n");
return result;
}
getSize = n;
if (fseek(fp, (long) src, SEEK_SET) != 0) {
- LOGD("filemove src seek %ld failed\n", (long) src);
+ ALOGD("filemove src seek %ld failed\n", (long) src);
return UNKNOWN_ERROR;
}
if (fread(readBuf, 1, getSize, fp) != getSize) {
- LOGD("filemove read %ld off=%ld failed\n",
+ ALOGD("filemove read %ld off=%ld failed\n",
(long) getSize, (long) src);
return UNKNOWN_ERROR;
}
if (fseek(fp, (long) dst, SEEK_SET) != 0) {
- LOGD("filemove dst seek %ld failed\n", (long) dst);
+ ALOGD("filemove dst seek %ld failed\n", (long) dst);
return UNKNOWN_ERROR;
}
if (fwrite(readBuf, 1, getSize, fp) != getSize) {
- LOGD("filemove write %ld off=%ld failed\n",
+ ALOGD("filemove write %ld off=%ld failed\n",
(long) getSize, (long) dst);
return UNKNOWN_ERROR;
}
struct stat sb;
if (fstat(fd, &sb) < 0) {
- LOGD("HEY: fstat on fd %d failed\n", fd);
+ ALOGD("HEY: fstat on fd %d failed\n", fd);
return (time_t) -1;
}
int fd;
fd = dup(fileno(mZipFp));
if (fd < 0) {
- LOGD("didn't work, errno=%d\n", errno);
+ ALOGD("didn't work, errno=%d\n", errno);
}
return fd;
if (len < kEOCDLen) {
/* looks like ZIP file got truncated */
- LOGD(" Zip EOCD: expected >= %d bytes, found %d\n",
+ ALOGD(" Zip EOCD: expected >= %d bytes, found %d\n",
kEOCDLen, len);
return INVALID_OPERATION;
}
if (mCommentLen > 0) {
if (kEOCDLen + mCommentLen > len) {
- LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n",
+ ALOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n",
kEOCDLen, mCommentLen, len);
return UNKNOWN_ERROR;
}
*/
void ZipFile::EndOfCentralDir::dump(void) const
{
- LOGD(" EndOfCentralDir contents:\n");
- LOGD(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n",
+ ALOGD(" EndOfCentralDir contents:\n");
+ ALOGD(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n",
mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries);
- LOGD(" centDirSize=%lu centDirOff=%lu commentLen=%u\n",
+ ALOGD(" centDirSize=%lu centDirOff=%lu commentLen=%u\n",
mCentralDirSize, mCentralDirOffset, mCommentLen);
}
/*
* Open a new or existing archive.
*/
- typedef enum {
+ enum {
kOpenReadOnly = 0x01,
kOpenReadWrite = 0x02,
kOpenCreate = 0x04, // create if it doesn't exist
--- /dev/null
+//
+// Copyright 2011 The Android Open Source Project
+//
+#include <utils/String8.h>
+#include <iostream>
+#include <errno.h>
+
+#include "CrunchCache.h"
+#include "FileFinder.h"
+#include "MockFileFinder.h"
+#include "CacheUpdater.h"
+#include "MockCacheUpdater.h"
+
+using namespace android;
+using std::cout;
+using std::endl;
+
+void expectEqual(int got, int expected, const char* desc) {
+ cout << "Checking " << desc << ": ";
+ cout << "Got " << got << ", expected " << expected << "...";
+ cout << ( (got == expected) ? "PASSED" : "FAILED") << endl;
+ errno += ((got == expected) ? 0 : 1);
+}
+
+int main() {
+
+ errno = 0;
+
+ String8 source("res");
+ String8 dest("res2");
+
+ // Create data for MockFileFinder to feed to the cache
+ KeyedVector<String8, time_t> sourceData;
+ // This shouldn't be updated
+ sourceData.add(String8("res/drawable/hello.png"),3);
+ // This should be updated
+ sourceData.add(String8("res/drawable/world.png"),5);
+ // This should cause make directory to be called
+ sourceData.add(String8("res/drawable-cool/hello.png"),3);
+
+ KeyedVector<String8, time_t> destData;
+ destData.add(String8("res2/drawable/hello.png"),3);
+ destData.add(String8("res2/drawable/world.png"),3);
+ // this should call delete
+ destData.add(String8("res2/drawable/dead.png"),3);
+
+ // Package up data and create mock file finder
+ KeyedVector<String8, KeyedVector<String8,time_t> > data;
+ data.add(source,sourceData);
+ data.add(dest,destData);
+ FileFinder* ff = new MockFileFinder(data);
+ CrunchCache cc(source,dest,ff);
+
+ MockCacheUpdater* mcu = new MockCacheUpdater();
+ CacheUpdater* cu(mcu);
+
+ cout << "Running Crunch...";
+ int result = cc.crunch(cu);
+ cout << ((result > 0) ? "PASSED" : "FAILED") << endl;
+ errno += ((result > 0) ? 0 : 1);
+
+ const int EXPECTED_RESULT = 2;
+ expectEqual(result, EXPECTED_RESULT, "number of files touched");
+
+ cout << "Checking calls to deleteFile and processImage:" << endl;
+ const int EXPECTED_DELETES = 1;
+ const int EXPECTED_PROCESSED = 2;
+ // Deletes
+ expectEqual(mcu->deleteCount, EXPECTED_DELETES, "deleteFile");
+ // processImage
+ expectEqual(mcu->processCount, EXPECTED_PROCESSED, "processImage");
+
+ const int EXPECTED_OVERWRITES = 3;
+ result = cc.crunch(cu, true);
+ expectEqual(result, EXPECTED_OVERWRITES, "number of files touched with overwrite");
+ \
+
+ if (errno == 0)
+ cout << "ALL TESTS PASSED!" << endl;
+ else
+ cout << errno << " TESTS FAILED" << endl;
+
+ delete ff;
+ delete cu;
+
+ // TESTS BELOW WILL GO AWAY SOON
+
+ String8 source2("ApiDemos/res");
+ String8 dest2("ApiDemos/res2");
+
+ FileFinder* sff = new SystemFileFinder();
+ CacheUpdater* scu = new SystemCacheUpdater();
+
+ CrunchCache scc(source2,dest2,sff);
+
+ scc.crunch(scu);
+}
\ No newline at end of file
--- /dev/null
+//
+// Copyright 2011 The Android Open Source Project
+//
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <iostream>
+#include <cassert>
+#include <utils/String8.h>
+#include <utility>
+
+#include "DirectoryWalker.h"
+#include "MockDirectoryWalker.h"
+#include "FileFinder.h"
+
+using namespace android;
+
+using std::pair;
+using std::cout;
+using std::endl;
+
+
+
+int main()
+{
+
+ cout << "\n\n STARTING FILE FINDER TESTS" << endl;
+ String8 path("ApiDemos");
+
+ // Storage to pass to findFiles()
+ KeyedVector<String8,time_t> testStorage;
+
+ // Mock Directory Walker initialization. First data, then sdw
+ Vector< pair<String8,time_t> > data;
+ data.push( pair<String8,time_t>(String8("hello.png"),3) );
+ data.push( pair<String8,time_t>(String8("world.PNG"),3) );
+ data.push( pair<String8,time_t>(String8("foo.pNg"),3) );
+ // Neither of these should be found
+ data.push( pair<String8,time_t>(String8("hello.jpg"),3) );
+ data.push( pair<String8,time_t>(String8(".hidden.png"),3));
+
+ DirectoryWalker* sdw = new StringDirectoryWalker(path,data);
+
+ // Extensions to look for
+ Vector<String8> exts;
+ exts.push(String8(".png"));
+
+ errno = 0;
+
+ // Make sure we get a valid mock directory walker
+ // Make sure we finish without errors
+ cout << "Checking DirectoryWalker...";
+ assert(sdw != NULL);
+ cout << "PASSED" << endl;
+
+ // Make sure we finish without errors
+ cout << "Running findFiles()...";
+ bool findStatus = FileFinder::findFiles(path,exts, testStorage, sdw);
+ assert(findStatus);
+ cout << "PASSED" << endl;
+
+ const size_t SIZE_EXPECTED = 3;
+ // Check to make sure we have the right number of things in our storage
+ cout << "Running size comparison: Size is " << testStorage.size() << ", ";
+ cout << "Expected " << SIZE_EXPECTED << "...";
+ if(testStorage.size() == SIZE_EXPECTED)
+ cout << "PASSED" << endl;
+ else {
+ cout << "FAILED" << endl;
+ errno++;
+ }
+
+ // Check to make sure that each of our found items has the right extension
+ cout << "Checking Returned Extensions...";
+ bool extsOkay = true;
+ String8 wrongExts;
+ for (size_t i = 0; i < SIZE_EXPECTED; ++i) {
+ String8 testExt(testStorage.keyAt(i).getPathExtension());
+ testExt.toLower();
+ if (testExt != ".png") {
+ wrongExts += testStorage.keyAt(i);
+ wrongExts += "\n";
+ extsOkay = false;
+ }
+ }
+ if (extsOkay)
+ cout << "PASSED" << endl;
+ else {
+ cout << "FAILED" << endl;
+ cout << "The following extensions didn't check out" << endl << wrongExts;
+ }
+
+ // Clean up
+ delete sdw;
+
+ if(errno == 0) {
+ cout << "ALL TESTS PASSED" << endl;
+ } else {
+ cout << errno << " TESTS FAILED" << endl;
+ }
+ return errno;
+}
\ No newline at end of file
--- /dev/null
+//
+// Copyright 2011 The Android Open Source Project
+//
+#ifndef MOCKCACHEUPDATER_H
+#define MOCKCACHEUPDATER_H
+
+#include <utils/String8.h>
+#include "CacheUpdater.h"
+
+using namespace android;
+
+class MockCacheUpdater : public CacheUpdater {
+public:
+
+ MockCacheUpdater()
+ : deleteCount(0), processCount(0) { };
+
+ // Make sure all the directories along this path exist
+ virtual void ensureDirectoriesExist(String8 path)
+ {
+ // Nothing to do
+ };
+
+ // Delete a file
+ virtual void deleteFile(String8 path) {
+ deleteCount++;
+ };
+
+ // Process an image from source out to dest
+ virtual void processImage(String8 source, String8 dest) {
+ processCount++;
+ };
+
+ // DATA MEMBERS
+ int deleteCount;
+ int processCount;
+private:
+};
+
+#endif // MOCKCACHEUPDATER_H
\ No newline at end of file
--- /dev/null
+//
+// Copyright 2011 The Android Open Source Project
+//
+#ifndef MOCKDIRECTORYWALKER_H
+#define MOCKDIRECTORYWALKER_H
+
+#include <utils/Vector.h>
+#include <utils/String8.h>
+#include <utility>
+#include "DirectoryWalker.h"
+
+using namespace android;
+using std::pair;
+
+// String8 Directory Walker
+// This is an implementation of the Directory Walker abstraction that is built
+// for testing.
+// Instead of system calls it queries a private data structure for the directory
+// entries. It takes a path and a map of filenames and their modification times.
+// functions are inlined since they are short and simple
+
+class StringDirectoryWalker : public DirectoryWalker {
+public:
+ StringDirectoryWalker(String8& path, Vector< pair<String8,time_t> >& data)
+ : mPos(0), mBasePath(path), mData(data) {
+ //fprintf(stdout,"StringDW built to mimic %s with %d files\n",
+ // mBasePath.string());
+ };
+ // Default copy constructor, and destructor are fine
+
+ virtual bool openDir(String8 path) {
+ // If the user is trying to query the "directory" that this
+ // walker was initialized with, then return success. Else fail.
+ return path == mBasePath;
+ };
+ virtual bool openDir(const char* path) {
+ String8 p(path);
+ openDir(p);
+ return true;
+ };
+ // Advance to next entry in the Vector
+ virtual struct dirent* nextEntry() {
+ // Advance position and check to see if we're done
+ if (mPos >= mData.size())
+ return NULL;
+
+ // Place data in the entry descriptor. This class only returns files.
+ mEntry.d_type = DT_REG;
+ mEntry.d_ino = mPos;
+ // Copy chars from the string name to the entry name
+ size_t i = 0;
+ for (i; i < mData[mPos].first.size(); ++i)
+ mEntry.d_name[i] = mData[mPos].first[i];
+ mEntry.d_name[i] = '\0';
+
+ // Place data in stats
+ mStats.st_ino = mPos;
+ mStats.st_mtime = mData[mPos].second;
+
+ // Get ready to move to the next entry
+ mPos++;
+
+ return &mEntry;
+ };
+ // Get the stats for the current entry
+ virtual struct stat* entryStats() {
+ return &mStats;
+ };
+ // Nothing to do in clean up
+ virtual void closeDir() {
+ // Nothing to do
+ };
+ virtual DirectoryWalker* clone() {
+ return new StringDirectoryWalker(*this);
+ };
+private:
+ // Current position in the Vector
+ size_t mPos;
+ // Base path
+ String8 mBasePath;
+ // Data to simulate a directory full of files.
+ Vector< pair<String8,time_t> > mData;
+};
+
+#endif // MOCKDIRECTORYWALKER_H
\ No newline at end of file
--- /dev/null
+//
+// Copyright 2011 The Android Open Source Project
+//
+
+#ifndef MOCKFILEFINDER_H
+#define MOCKFILEFINDER_H
+
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+
+#include "DirectoryWalker.h"
+
+using namespace android;
+
+class MockFileFinder : public FileFinder {
+public:
+ MockFileFinder (KeyedVector<String8, KeyedVector<String8,time_t> >& files)
+ : mFiles(files)
+ {
+ // Nothing left to do
+ };
+
+ /**
+ * findFiles implementation for the abstraction.
+ * PRECONDITIONS:
+ * No checking is done, so there MUST be an entry in mFiles with
+ * path matching basePath.
+ *
+ * POSTCONDITIONS:
+ * fileStore is filled with a copy of the data in mFiles corresponding
+ * to the basePath.
+ */
+
+ virtual bool findFiles(String8 basePath, Vector<String8>& extensions,
+ KeyedVector<String8,time_t>& fileStore,
+ DirectoryWalker* dw)
+ {
+ const KeyedVector<String8,time_t>* payload(&mFiles.valueFor(basePath));
+ // Since KeyedVector doesn't implement swap
+ // (who doesn't use swap??) we loop and add one at a time.
+ for (size_t i = 0; i < payload->size(); ++i) {
+ fileStore.add(payload->keyAt(i),payload->valueAt(i));
+ }
+ return true;
+ }
+
+private:
+ // Virtual mapping between "directories" and the "files" contained
+ // in them
+ KeyedVector<String8, KeyedVector<String8,time_t> > mFiles;
+};
+
+
+#endif // MOCKFILEFINDER_H
\ No newline at end of file