Dianne Hackborn 93e462b79d Implement issue #1780928: Need support hiding nav keys.
This implements support for devices whose hardware can hide
their navigation keys.  It works much like the existing keyboardHidden
configuration, and for compatibility uses the same configuration
change bit.

Also add FLAG_TURN_ON_SCREEN for windows, which has the system
cause the screen to be turned on when the window is displayed.
Great fun when used with FLAG_SHOW_WHEN_LOCKED!

Change-Id: I0b867f19af85cfd8786a14cea194b34f7bdd9b7a
2009-09-15 22:50:40 -07:00

1913 lines
51 KiB
C++

//
// Copyright 2006 The Android Open Source Project
//
#include "AaptAssets.h"
#include "Main.h"
#include <utils/misc.h>
#include <utils/SortedVector.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
static const char* kDefaultLocale = "default";
static const char* kWildcardName = "any";
static const char* kAssetDir = "assets";
static const char* kResourceDir = "res";
static const char* kInvalidChars = "/\\:";
static const size_t kMaxAssetFileName = 100;
static const String8 kResString(kResourceDir);
/*
* Names of asset files must meet the following criteria:
*
* - the filename length must be less than kMaxAssetFileName bytes long
* (and can't be empty)
* - all characters must be 7-bit printable ASCII
* - none of { '/' '\\' ':' }
*
* Pass in just the filename, not the full path.
*/
static bool validateFileName(const char* fileName)
{
const char* cp = fileName;
size_t len = 0;
while (*cp != '\0') {
if ((*cp & 0x80) != 0)
return false; // reject high ASCII
if (*cp < 0x20 || *cp >= 0x7f)
return false; // reject control chars and 0x7f
if (strchr(kInvalidChars, *cp) != NULL)
return false; // reject path sep chars
cp++;
len++;
}
if (len < 1 || len > kMaxAssetFileName)
return false; // reject empty or too long
return true;
}
static bool isHidden(const char *root, const char *path)
{
const char *type = NULL;
// Skip all hidden files.
if (path[0] == '.') {
// Skip ., .. and .svn but don't chatter about it.
if (strcmp(path, ".") == 0
|| strcmp(path, "..") == 0
|| strcmp(path, ".svn") == 0) {
return true;
}
type = "hidden";
} else if (path[0] == '_') {
// skip directories starting with _ (don't chatter about it)
String8 subdirName(root);
subdirName.appendPath(path);
if (getFileType(subdirName.string()) == kFileTypeDirectory) {
return true;
}
} else if (strcmp(path, "CVS") == 0) {
// Skip CVS but don't chatter about it.
return true;
} else if (strcasecmp(path, "thumbs.db") == 0
|| strcasecmp(path, "picasa.ini") == 0) {
// Skip suspected image indexes files.
type = "index";
} else if (path[strlen(path)-1] == '~') {
// Skip suspected emacs backup files.
type = "backup";
} else {
// Let everything else through.
return false;
}
/* If we get this far, "type" should be set and the file
* should be skipped.
*/
String8 subdirName(root);
subdirName.appendPath(path);
fprintf(stderr, " (skipping %s %s '%s')\n", type,
getFileType(subdirName.string())==kFileTypeDirectory ? "dir":"file",
subdirName.string());
return true;
}
// =========================================================================
// =========================================================================
// =========================================================================
status_t
AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
{
ResTable_config config;
// IMSI - MCC
if (getMccName(part.string(), &config)) {
*axis = AXIS_MCC;
*value = config.mcc;
return 0;
}
// IMSI - MNC
if (getMncName(part.string(), &config)) {
*axis = AXIS_MNC;
*value = config.mnc;
return 0;
}
// locale - language
if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
*axis = AXIS_LANGUAGE;
*value = part[1] << 8 | part[0];
return 0;
}
// locale - language_REGION
if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1])
&& part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) {
*axis = AXIS_LANGUAGE;
*value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]);
return 0;
}
// screen layout size
if (getScreenLayoutSizeName(part.string(), &config)) {
*axis = AXIS_SCREENLAYOUTSIZE;
*value = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
return 0;
}
// screen layout long
if (getScreenLayoutLongName(part.string(), &config)) {
*axis = AXIS_SCREENLAYOUTLONG;
*value = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
return 0;
}
// orientation
if (getOrientationName(part.string(), &config)) {
*axis = AXIS_ORIENTATION;
*value = config.orientation;
return 0;
}
// density
if (getDensityName(part.string(), &config)) {
*axis = AXIS_DENSITY;
*value = config.density;
return 0;
}
// touchscreen
if (getTouchscreenName(part.string(), &config)) {
*axis = AXIS_TOUCHSCREEN;
*value = config.touchscreen;
return 0;
}
// keyboard hidden
if (getKeysHiddenName(part.string(), &config)) {
*axis = AXIS_KEYSHIDDEN;
*value = config.inputFlags;
return 0;
}
// keyboard
if (getKeyboardName(part.string(), &config)) {
*axis = AXIS_KEYBOARD;
*value = config.keyboard;
return 0;
}
// navigation hidden
if (getNavHiddenName(part.string(), &config)) {
*axis = AXIS_NAVHIDDEN;
*value = config.inputFlags;
return 0;
}
// navigation
if (getNavigationName(part.string(), &config)) {
*axis = AXIS_NAVIGATION;
*value = config.navigation;
return 0;
}
// screen size
if (getScreenSizeName(part.string(), &config)) {
*axis = AXIS_SCREENSIZE;
*value = config.screenSize;
return 0;
}
// version
if (getVersionName(part.string(), &config)) {
*axis = AXIS_VERSION;
*value = config.version;
return 0;
}
return 1;
}
bool
AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
{
Vector<String8> parts;
String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
String8 touch, key, keysHidden, nav, navHidden, size, vers;
const char *p = dir;
const char *q;
while (NULL != (q = strchr(p, '-'))) {
String8 val(p, q-p);
val.toLower();
parts.add(val);
//printf("part: %s\n", parts[parts.size()-1].string());
p = q+1;
}
String8 val(p);
val.toLower();
parts.add(val);
//printf("part: %s\n", parts[parts.size()-1].string());
const int N = parts.size();
int index = 0;
String8 part = parts[index];
// resource type
if (!isValidResourceType(part)) {
return false;
}
*resType = part;
index++;
if (index == N) {
goto success;
}
part = parts[index];
// imsi - mcc
if (getMccName(part.string())) {
mcc = part;
index++;
if (index == N) {
goto success;
}
part = parts[index];
} else {
//printf("not mcc: %s\n", part.string());
}
// imsi - mnc
if (getMncName(part.string())) {
mnc = part;
index++;
if (index == N) {
goto success;
}
part = parts[index];
} else {
//printf("not mcc: %s\n", part.string());
}
// locale - language
if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
loc = part;
index++;
if (index == N) {
goto success;
}
part = parts[index];
} else {
//printf("not language: %s\n", part.string());
}
// locale - region
if (loc.length() > 0
&& part.length() == 3 && part[0] == 'r' && part[0] && part[1]) {
loc += "-";
part.toUpper();
loc += part.string() + 1;
index++;
if (index == N) {
goto success;
}
part = parts[index];
} else {
//printf("not region: %s\n", part.string());
}
if (getScreenLayoutSizeName(part.string())) {
layoutsize = part;
index++;
if (index == N) {
goto success;
}
part = parts[index];
} else {
//printf("not screen layout size: %s\n", part.string());
}
if (getScreenLayoutLongName(part.string())) {
layoutlong = part;
index++;
if (index == N) {
goto success;
}
part = parts[index];
} else {
//printf("not screen layout long: %s\n", part.string());
}
// orientation
if (getOrientationName(part.string())) {
orient = part;
index++;
if (index == N) {
goto success;
}
part = parts[index];
} else {
//printf("not orientation: %s\n", part.string());
}
// density
if (getDensityName(part.string())) {
den = part;
index++;
if (index == N) {
goto success;
}
part = parts[index];
} else {
//printf("not density: %s\n", part.string());
}
// touchscreen
if (getTouchscreenName(part.string())) {
touch = part;
index++;
if (index == N) {
goto success;
}
part = parts[index];
} else {
//printf("not touchscreen: %s\n", part.string());
}
// keyboard hidden
if (getKeysHiddenName(part.string())) {
keysHidden = part;
index++;
if (index == N) {
goto success;
}
part = parts[index];
} else {
//printf("not keysHidden: %s\n", part.string());
}
// keyboard
if (getKeyboardName(part.string())) {
key = part;
index++;
if (index == N) {
goto success;
}
part = parts[index];
} else {
//printf("not keyboard: %s\n", part.string());
}
// navigation hidden
if (getNavHiddenName(part.string())) {
navHidden = part;
index++;
if (index == N) {
goto success;
}
part = parts[index];
} else {
//printf("not navHidden: %s\n", part.string());
}
if (getNavigationName(part.string())) {
nav = part;
index++;
if (index == N) {
goto success;
}
part = parts[index];
} else {
//printf("not navigation: %s\n", part.string());
}
if (getScreenSizeName(part.string())) {
size = part;
index++;
if (index == N) {
goto success;
}
part = parts[index];
} else {
//printf("not screen size: %s\n", part.string());
}
if (getVersionName(part.string())) {
vers = part;
index++;
if (index == N) {
goto success;
}
part = parts[index];
} else {
//printf("not version: %s\n", part.string());
}
// if there are extra parts, it doesn't match
return false;
success:
this->mcc = mcc;
this->mnc = mnc;
this->locale = loc;
this->screenLayoutSize = layoutsize;
this->screenLayoutLong = layoutlong;
this->orientation = orient;
this->density = den;
this->touchscreen = touch;
this->keysHidden = keysHidden;
this->keyboard = key;
this->navHidden = navHidden;
this->navigation = nav;
this->screenSize = size;
this->version = vers;
// what is this anyway?
this->vendor = "";
return true;
}
String8
AaptGroupEntry::toString() const
{
String8 s = this->mcc;
s += ",";
s += this->mnc;
s += ",";
s += this->locale;
s += ",";
s += screenLayoutSize;
s += ",";
s += screenLayoutLong;
s += ",";
s += this->orientation;
s += ",";
s += density;
s += ",";
s += touchscreen;
s += ",";
s += keysHidden;
s += ",";
s += keyboard;
s += ",";
s += navHidden;
s += ",";
s += navigation;
s += ",";
s += screenSize;
s += ",";
s += version;
return s;
}
String8
AaptGroupEntry::toDirName(const String8& resType) const
{
String8 s = resType;
if (this->mcc != "") {
s += "-";
s += mcc;
}
if (this->mnc != "") {
s += "-";
s += mnc;
}
if (this->locale != "") {
s += "-";
s += locale;
}
if (this->screenLayoutSize != "") {
s += "-";
s += screenLayoutSize;
}
if (this->screenLayoutLong != "") {
s += "-";
s += screenLayoutLong;
}
if (this->orientation != "") {
s += "-";
s += orientation;
}
if (this->density != "") {
s += "-";
s += density;
}
if (this->touchscreen != "") {
s += "-";
s += touchscreen;
}
if (this->keysHidden != "") {
s += "-";
s += keysHidden;
}
if (this->keyboard != "") {
s += "-";
s += keyboard;
}
if (this->navHidden != "") {
s += "-";
s += navHidden;
}
if (this->navigation != "") {
s += "-";
s += navigation;
}
if (this->screenSize != "") {
s += "-";
s += screenSize;
}
if (this->version != "") {
s += "-";
s += version;
}
return s;
}
bool AaptGroupEntry::getMccName(const char* name,
ResTable_config* out)
{
if (strcmp(name, kWildcardName) == 0) {
if (out) out->mcc = 0;
return true;
}
const char* c = name;
if (tolower(*c) != 'm') return false;
c++;
if (tolower(*c) != 'c') return false;
c++;
if (tolower(*c) != 'c') return false;
c++;
const char* val = c;
while (*c >= '0' && *c <= '9') {
c++;
}
if (*c != 0) return false;
if (c-val != 3) return false;
int d = atoi(val);
if (d != 0) {
if (out) out->mcc = d;
return true;
}
return false;
}
bool AaptGroupEntry::getMncName(const char* name,
ResTable_config* out)
{
if (strcmp(name, kWildcardName) == 0) {
if (out) out->mcc = 0;
return true;
}
const char* c = name;
if (tolower(*c) != 'm') return false;
c++;
if (tolower(*c) != 'n') return false;
c++;
if (tolower(*c) != 'c') return false;
c++;
const char* val = c;
while (*c >= '0' && *c <= '9') {
c++;
}
if (*c != 0) return false;
if (c-val == 0 || c-val > 3) return false;
int d = atoi(val);
if (d != 0) {
if (out) out->mnc = d;
return true;
}
return false;
}
/*
* Does this directory name fit the pattern of a locale dir ("en-rUS" or
* "default")?
*
* TODO: Should insist that the first two letters are lower case, and the
* second two are upper.
*/
bool AaptGroupEntry::getLocaleName(const char* fileName,
ResTable_config* out)
{
if (strcmp(fileName, kWildcardName) == 0
|| strcmp(fileName, kDefaultLocale) == 0) {
if (out) {
out->language[0] = 0;
out->language[1] = 0;
out->country[0] = 0;
out->country[1] = 0;
}
return true;
}
if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
if (out) {
out->language[0] = fileName[0];
out->language[1] = fileName[1];
out->country[0] = 0;
out->country[1] = 0;
}
return true;
}
if (strlen(fileName) == 5 &&
isalpha(fileName[0]) &&
isalpha(fileName[1]) &&
fileName[2] == '-' &&
isalpha(fileName[3]) &&
isalpha(fileName[4])) {
if (out) {
out->language[0] = fileName[0];
out->language[1] = fileName[1];
out->country[0] = fileName[3];
out->country[1] = fileName[4];
}
return true;
}
return false;
}
bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
ResTable_config* out)
{
if (strcmp(name, kWildcardName) == 0) {
if (out) out->screenLayout =
(out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
| ResTable_config::SCREENSIZE_ANY;
return true;
} else if (strcmp(name, "small") == 0) {
if (out) out->screenLayout =
(out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
| ResTable_config::SCREENSIZE_SMALL;
return true;
} else if (strcmp(name, "normal") == 0) {
if (out) out->screenLayout =
(out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
| ResTable_config::SCREENSIZE_NORMAL;
return true;
} else if (strcmp(name, "large") == 0) {
if (out) out->screenLayout =
(out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
| ResTable_config::SCREENSIZE_LARGE;
return true;
}
return false;
}
bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
ResTable_config* out)
{
if (strcmp(name, kWildcardName) == 0) {
if (out) out->screenLayout =
(out->screenLayout&~ResTable_config::MASK_SCREENLONG)
| ResTable_config::SCREENLONG_ANY;
return true;
} else if (strcmp(name, "long") == 0) {
if (out) out->screenLayout =
(out->screenLayout&~ResTable_config::MASK_SCREENLONG)
| ResTable_config::SCREENLONG_YES;
return true;
} else if (strcmp(name, "notlong") == 0) {
if (out) out->screenLayout =
(out->screenLayout&~ResTable_config::MASK_SCREENLONG)
| ResTable_config::SCREENLONG_NO;
return true;
}
return false;
}
bool AaptGroupEntry::getOrientationName(const char* name,
ResTable_config* out)
{
if (strcmp(name, kWildcardName) == 0) {
if (out) out->orientation = out->ORIENTATION_ANY;
return true;
} else if (strcmp(name, "port") == 0) {
if (out) out->orientation = out->ORIENTATION_PORT;
return true;
} else if (strcmp(name, "land") == 0) {
if (out) out->orientation = out->ORIENTATION_LAND;
return true;
} else if (strcmp(name, "square") == 0) {
if (out) out->orientation = out->ORIENTATION_SQUARE;
return true;
}
return false;
}
bool AaptGroupEntry::getDensityName(const char* name,
ResTable_config* out)
{
if (strcmp(name, kWildcardName) == 0) {
if (out) out->density = ResTable_config::DENSITY_DEFAULT;
return true;
}
if (strcmp(name, "nodpi") == 0) {
if (out) out->density = ResTable_config::DENSITY_NONE;
return true;
}
if (strcmp(name, "ldpi") == 0) {
if (out) out->density = ResTable_config::DENSITY_LOW;
return true;
}
if (strcmp(name, "mdpi") == 0) {
if (out) out->density = ResTable_config::DENSITY_MEDIUM;
return true;
}
if (strcmp(name, "hdpi") == 0) {
if (out) out->density = ResTable_config::DENSITY_HIGH;
return true;
}
char* c = (char*)name;
while (*c >= '0' && *c <= '9') {
c++;
}
// check that we have 'dpi' after the last digit.
if (toupper(c[0]) != 'D' ||
toupper(c[1]) != 'P' ||
toupper(c[2]) != 'I' ||
c[3] != 0) {
return false;
}
// temporarily replace the first letter with \0 to
// use atoi.
char tmp = c[0];
c[0] = '\0';
int d = atoi(name);
c[0] = tmp;
if (d != 0) {
if (out) out->density = d;
return true;
}
return false;
}
bool AaptGroupEntry::getTouchscreenName(const char* name,
ResTable_config* out)
{
if (strcmp(name, kWildcardName) == 0) {
if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
return true;
} else if (strcmp(name, "notouch") == 0) {
if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
return true;
} else if (strcmp(name, "stylus") == 0) {
if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
return true;
} else if (strcmp(name, "finger") == 0) {
if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
return true;
}
return false;
}
bool AaptGroupEntry::getKeysHiddenName(const char* name,
ResTable_config* out)
{
uint8_t mask = 0;
uint8_t value = 0;
if (strcmp(name, kWildcardName) == 0) {
mask = out->MASK_KEYSHIDDEN;
value = out->KEYSHIDDEN_ANY;
} else if (strcmp(name, "keysexposed") == 0) {
mask = out->MASK_KEYSHIDDEN;
value = out->KEYSHIDDEN_NO;
} else if (strcmp(name, "keyshidden") == 0) {
mask = out->MASK_KEYSHIDDEN;
value = out->KEYSHIDDEN_YES;
} else if (strcmp(name, "keyssoft") == 0) {
mask = out->MASK_KEYSHIDDEN;
value = out->KEYSHIDDEN_SOFT;
}
if (mask != 0) {
if (out) out->inputFlags = (out->inputFlags&~mask) | value;
return true;
}
return false;
}
bool AaptGroupEntry::getKeyboardName(const char* name,
ResTable_config* out)
{
if (strcmp(name, kWildcardName) == 0) {
if (out) out->keyboard = out->KEYBOARD_ANY;
return true;
} else if (strcmp(name, "nokeys") == 0) {
if (out) out->keyboard = out->KEYBOARD_NOKEYS;
return true;
} else if (strcmp(name, "qwerty") == 0) {
if (out) out->keyboard = out->KEYBOARD_QWERTY;
return true;
} else if (strcmp(name, "12key") == 0) {
if (out) out->keyboard = out->KEYBOARD_12KEY;
return true;
}
return false;
}
bool AaptGroupEntry::getNavHiddenName(const char* name,
ResTable_config* out)
{
uint8_t mask = 0;
uint8_t value = 0;
if (strcmp(name, kWildcardName) == 0) {
mask = out->MASK_NAVHIDDEN;
value = out->NAVHIDDEN_ANY;
} else if (strcmp(name, "navexposed") == 0) {
mask = out->MASK_NAVHIDDEN;
value = out->NAVHIDDEN_NO;
} else if (strcmp(name, "navhidden") == 0) {
mask = out->MASK_NAVHIDDEN;
value = out->NAVHIDDEN_YES;
}
if (mask != 0) {
if (out) out->inputFlags = (out->inputFlags&~mask) | value;
return true;
}
return false;
}
bool AaptGroupEntry::getNavigationName(const char* name,
ResTable_config* out)
{
if (strcmp(name, kWildcardName) == 0) {
if (out) out->navigation = out->NAVIGATION_ANY;
return true;
} else if (strcmp(name, "nonav") == 0) {
if (out) out->navigation = out->NAVIGATION_NONAV;
return true;
} else if (strcmp(name, "dpad") == 0) {
if (out) out->navigation = out->NAVIGATION_DPAD;
return true;
} else if (strcmp(name, "trackball") == 0) {
if (out) out->navigation = out->NAVIGATION_TRACKBALL;
return true;
} else if (strcmp(name, "wheel") == 0) {
if (out) out->navigation = out->NAVIGATION_WHEEL;
return true;
}
return false;
}
bool AaptGroupEntry::getScreenSizeName(const char* name,
ResTable_config* out)
{
if (strcmp(name, kWildcardName) == 0) {
if (out) {
out->screenWidth = out->SCREENWIDTH_ANY;
out->screenHeight = out->SCREENHEIGHT_ANY;
}
return true;
}
const char* x = name;
while (*x >= '0' && *x <= '9') x++;
if (x == name || *x != 'x') return false;
String8 xName(name, x-name);
x++;
const char* y = x;
while (*y >= '0' && *y <= '9') y++;
if (y == name || *y != 0) return false;
String8 yName(x, y-x);
uint16_t w = (uint16_t)atoi(xName.string());
uint16_t h = (uint16_t)atoi(yName.string());
if (w < h) {
return false;
}
if (out) {
out->screenWidth = w;
out->screenHeight = h;
}
return true;
}
bool AaptGroupEntry::getVersionName(const char* name,
ResTable_config* out)
{
if (strcmp(name, kWildcardName) == 0) {
if (out) {
out->sdkVersion = out->SDKVERSION_ANY;
out->minorVersion = out->MINORVERSION_ANY;
}
return true;
}
if (*name != 'v') {
return false;
}
name++;
const char* s = name;
while (*s >= '0' && *s <= '9') s++;
if (s == name || *s != 0) return false;
String8 sdkName(name, s-name);
if (out) {
out->sdkVersion = (uint16_t)atoi(sdkName.string());
out->minorVersion = 0;
}
return true;
}
int AaptGroupEntry::compare(const AaptGroupEntry& o) const
{
int v = mcc.compare(o.mcc);
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 = screenLayoutSize.compare(o.screenLayoutSize);
if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
if (v == 0) v = orientation.compare(o.orientation);
if (v == 0) v = density.compare(o.density);
if (v == 0) v = touchscreen.compare(o.touchscreen);
if (v == 0) v = keysHidden.compare(o.keysHidden);
if (v == 0) v = keyboard.compare(o.keyboard);
if (v == 0) v = navHidden.compare(o.navHidden);
if (v == 0) v = navigation.compare(o.navigation);
if (v == 0) v = screenSize.compare(o.screenSize);
if (v == 0) v = version.compare(o.version);
return v;
}
ResTable_config AaptGroupEntry::toParams() const
{
ResTable_config params;
memset(&params, 0, sizeof(params));
getMccName(mcc.string(), &params);
getMncName(mnc.string(), &params);
getLocaleName(locale.string(), &params);
getScreenLayoutSizeName(screenLayoutSize.string(), &params);
getScreenLayoutLongName(screenLayoutLong.string(), &params);
getOrientationName(orientation.string(), &params);
getDensityName(density.string(), &params);
getTouchscreenName(touchscreen.string(), &params);
getKeysHiddenName(keysHidden.string(), &params);
getKeyboardName(keyboard.string(), &params);
getNavHiddenName(navHidden.string(), &params);
getNavigationName(navigation.string(), &params);
getScreenSizeName(screenSize.string(), &params);
getVersionName(version.string(), &params);
return params;
}
// =========================================================================
// =========================================================================
// =========================================================================
void* AaptFile::editData(size_t size)
{
if (size <= mBufferSize) {
mDataSize = size;
return mData;
}
size_t allocSize = (size*3)/2;
void* buf = realloc(mData, allocSize);
if (buf == NULL) {
return NULL;
}
mData = buf;
mDataSize = size;
mBufferSize = allocSize;
return buf;
}
void* AaptFile::editData(size_t* outSize)
{
if (outSize) {
*outSize = mDataSize;
}
return mData;
}
void* AaptFile::padData(size_t wordSize)
{
const size_t extra = mDataSize%wordSize;
if (extra == 0) {
return mData;
}
size_t initial = mDataSize;
void* data = editData(initial+(wordSize-extra));
if (data != NULL) {
memset(((uint8_t*)data) + initial, 0, wordSize-extra);
}
return data;
}
status_t AaptFile::writeData(const void* data, size_t size)
{
size_t end = mDataSize;
size_t total = size + end;
void* buf = editData(total);
if (buf == NULL) {
return UNKNOWN_ERROR;
}
memcpy(((char*)buf)+end, data, size);
return NO_ERROR;
}
void AaptFile::clearData()
{
if (mData != NULL) free(mData);
mData = NULL;
mDataSize = 0;
mBufferSize = 0;
}
String8 AaptFile::getPrintableSource() const
{
if (hasData()) {
String8 name(mGroupEntry.locale.string());
name.appendPath(mGroupEntry.vendor.string());
name.appendPath(mPath);
name.append(" #generated");
return name;
}
return mSourceFile;
}
// =========================================================================
// =========================================================================
// =========================================================================
status_t AaptGroup::addFile(const sp<AaptFile>& file)
{
if (mFiles.indexOfKey(file->getGroupEntry()) < 0) {
file->mPath = mPath;
mFiles.add(file->getGroupEntry(), file);
return NO_ERROR;
}
SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
getPrintableSource().string());
return UNKNOWN_ERROR;
}
void AaptGroup::removeFile(size_t index)
{
mFiles.removeItemsAt(index);
}
void AaptGroup::print() const
{
printf(" %s\n", 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(),
(int)file->getSize());
} else {
printf(" Src: %s\n", file->getPrintableSource().string());
}
}
}
String8 AaptGroup::getPrintableSource() const
{
if (mFiles.size() > 0) {
// Arbitrarily pull the first source file out of the list.
return mFiles.valueAt(0)->getPrintableSource();
}
// Should never hit this case, but to be safe...
return getPath();
}
// =========================================================================
// =========================================================================
// =========================================================================
status_t AaptDir::addFile(const String8& name, const sp<AaptGroup>& file)
{
if (mFiles.indexOfKey(name) >= 0) {
return ALREADY_EXISTS;
}
mFiles.add(name, file);
return NO_ERROR;
}
status_t AaptDir::addDir(const String8& name, const sp<AaptDir>& dir)
{
if (mDirs.indexOfKey(name) >= 0) {
return ALREADY_EXISTS;
}
mDirs.add(name, dir);
return NO_ERROR;
}
sp<AaptDir> AaptDir::makeDir(const String8& path)
{
String8 name;
String8 remain = path;
sp<AaptDir> subdir = this;
while (name = remain.walkPath(&remain), remain != "") {
subdir = subdir->makeDir(name);
}
ssize_t i = subdir->mDirs.indexOfKey(name);
if (i >= 0) {
return subdir->mDirs.valueAt(i);
}
sp<AaptDir> dir = new AaptDir(name, subdir->mPath.appendPathCopy(name));
subdir->mDirs.add(name, dir);
return dir;
}
void AaptDir::removeFile(const String8& name)
{
mFiles.removeItem(name);
}
void AaptDir::removeDir(const String8& name)
{
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;
if (mFiles.indexOfKey(leafName) >= 0) {
group = mFiles.valueFor(leafName);
} else {
group = new AaptGroup(leafName, mPath.appendPathCopy(leafName));
mFiles.add(leafName, group);
}
return group->addFile(file);
}
ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
const AaptGroupEntry& kind, const String8& resType)
{
Vector<String8> fileNames;
{
DIR* dir = NULL;
dir = opendir(srcDir.string());
if (dir == NULL) {
fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
return UNKNOWN_ERROR;
}
/*
* Slurp the filenames out of the directory.
*/
while (1) {
struct dirent* entry;
entry = readdir(dir);
if (entry == NULL)
break;
if (isHidden(srcDir.string(), entry->d_name))
continue;
fileNames.add(String8(entry->d_name));
}
closedir(dir);
}
ssize_t count = 0;
/*
* Stash away the files and recursively descend into subdirectories.
*/
const size_t N = fileNames.size();
size_t i;
for (i = 0; i < N; i++) {
String8 pathName(srcDir);
FileType type;
pathName.appendPath(fileNames[i].string());
type = getFileType(pathName.string());
if (type == kFileTypeDirectory) {
sp<AaptDir> subdir;
bool notAdded = false;
if (mDirs.indexOfKey(fileNames[i]) >= 0) {
subdir = mDirs.valueFor(fileNames[i]);
} else {
subdir = new AaptDir(fileNames[i], mPath.appendPathCopy(fileNames[i]));
notAdded = true;
}
ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
resType);
if (res < NO_ERROR) {
return res;
}
if (res > 0 && notAdded) {
mDirs.add(fileNames[i], subdir);
}
count += res;
} else if (type == kFileTypeRegular) {
sp<AaptFile> file = new AaptFile(pathName, kind, resType);
status_t err = addLeafFile(fileNames[i], file);
if (err != NO_ERROR) {
return err;
}
count++;
} else {
if (bundle->getVerbose())
printf(" (ignoring non-file/dir '%s')\n", pathName.string());
}
}
return count;
}
status_t AaptDir::validate() const
{
const size_t NF = mFiles.size();
const size_t ND = mDirs.size();
size_t i;
for (i = 0; i < NF; i++) {
if (!validateFileName(mFiles.valueAt(i)->getLeaf().string())) {
SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
"Invalid filename. Unable to add.");
return UNKNOWN_ERROR;
}
size_t j;
for (j = i+1; j < NF; j++) {
if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
mFiles.valueAt(j)->getLeaf().string()) == 0) {
SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
"File is case-insensitive equivalent to: %s",
mFiles.valueAt(j)->getPrintableSource().string());
return UNKNOWN_ERROR;
}
// TODO: if ".gz", check for non-.gz; if non-, check for ".gz"
// (this is mostly caught by the "marked" stuff, below)
}
for (j = 0; j < ND; j++) {
if (strcasecmp(mFiles.valueAt(i)->getLeaf().string(),
mDirs.valueAt(j)->getLeaf().string()) == 0) {
SourcePos(mFiles.valueAt(i)->getPrintableSource(), -1).error(
"File conflicts with dir from: %s",
mDirs.valueAt(j)->getPrintableSource().string());
return UNKNOWN_ERROR;
}
}
}
for (i = 0; i < ND; i++) {
if (!validateFileName(mDirs.valueAt(i)->getLeaf().string())) {
SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
"Invalid directory name, unable to add.");
return UNKNOWN_ERROR;
}
size_t j;
for (j = i+1; j < ND; j++) {
if (strcasecmp(mDirs.valueAt(i)->getLeaf().string(),
mDirs.valueAt(j)->getLeaf().string()) == 0) {
SourcePos(mDirs.valueAt(i)->getPrintableSource(), -1).error(
"Directory is case-insensitive equivalent to: %s",
mDirs.valueAt(j)->getPrintableSource().string());
return UNKNOWN_ERROR;
}
}
status_t err = mDirs.valueAt(i)->validate();
if (err != NO_ERROR) {
return err;
}
}
return NO_ERROR;
}
void AaptDir::print() const
{
const size_t ND=getDirs().size();
size_t i;
for (i=0; i<ND; i++) {
getDirs().valueAt(i)->print();
}
const size_t NF=getFiles().size();
for (i=0; i<NF; i++) {
getFiles().valueAt(i)->print();
}
}
String8 AaptDir::getPrintableSource() const
{
if (mFiles.size() > 0) {
// Arbitrarily pull the first file out of the list as the source dir.
return mFiles.valueAt(0)->getPrintableSource().getPathDir();
}
if (mDirs.size() > 0) {
// Or arbitrarily pull the first dir out of the list as the source dir.
return mDirs.valueAt(0)->getPrintableSource().getPathDir();
}
// Should never hit this case, but to be safe...
return mPath;
}
// =========================================================================
// =========================================================================
// =========================================================================
sp<AaptFile> AaptAssets::addFile(
const String8& filePath, const AaptGroupEntry& entry,
const String8& srcDir, sp<AaptGroup>* outGroup,
const String8& resType)
{
sp<AaptDir> dir = this;
sp<AaptGroup> group;
sp<AaptFile> file;
String8 root, remain(filePath), partialPath;
while (remain.length() > 0) {
root = remain.walkPath(&remain);
partialPath.appendPath(root);
const String8 rootStr(root);
if (remain.length() == 0) {
ssize_t i = dir->getFiles().indexOfKey(rootStr);
if (i >= 0) {
group = dir->getFiles().valueAt(i);
} else {
group = new AaptGroup(rootStr, filePath);
status_t res = dir->addFile(rootStr, group);
if (res != NO_ERROR) {
return NULL;
}
}
file = new AaptFile(srcDir.appendPathCopy(filePath), entry, resType);
status_t res = group->addFile(file);
if (res != NO_ERROR) {
return NULL;
}
break;
} else {
ssize_t i = dir->getDirs().indexOfKey(rootStr);
if (i >= 0) {
dir = dir->getDirs().valueAt(i);
} else {
sp<AaptDir> subdir = new AaptDir(rootStr, partialPath);
status_t res = dir->addDir(rootStr, subdir);
if (res != NO_ERROR) {
return NULL;
}
dir = subdir;
}
}
}
mGroupEntries.add(entry);
if (outGroup) *outGroup = group;
return file;
}
void AaptAssets::addResource(const String8& leafName, const String8& path,
const sp<AaptFile>& file, const String8& resType)
{
sp<AaptDir> res = AaptDir::makeDir(kResString);
String8 dirname = file->getGroupEntry().toDirName(resType);
sp<AaptDir> subdir = res->makeDir(dirname);
sp<AaptGroup> grr = new AaptGroup(leafName, path);
grr->addFile(file);
subdir->addFile(leafName, grr);
}
ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
{
int count;
int totalCount = 0;
FileType type;
const Vector<const char *>& resDirs = bundle->getResourceSourceDirs();
const size_t dirCount =resDirs.size();
sp<AaptAssets> current = this;
const int N = bundle->getFileSpecCount();
/*
* If a package manifest was specified, include that first.
*/
if (bundle->getAndroidManifestFile() != NULL) {
// place at root of zip.
String8 srcFile(bundle->getAndroidManifestFile());
addFile(srcFile.getPathLeaf(), AaptGroupEntry(), srcFile.getPathDir(),
NULL, String8());
totalCount++;
}
/*
* If a directory of custom assets was supplied, slurp 'em up.
*/
if (bundle->getAssetSourceDir()) {
const char* assetDir = bundle->getAssetSourceDir();
FileType type = getFileType(assetDir);
if (type == kFileTypeNonexistent) {
fprintf(stderr, "ERROR: asset directory '%s' does not exist\n", assetDir);
return UNKNOWN_ERROR;
}
if (type != kFileTypeDirectory) {
fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
return UNKNOWN_ERROR;
}
String8 assetRoot(assetDir);
sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
AaptGroupEntry group;
count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
String8());
if (count < 0) {
totalCount = count;
goto bail;
}
if (count > 0) {
mGroupEntries.add(group);
}
totalCount += count;
if (bundle->getVerbose())
printf("Found %d custom asset file%s in %s\n",
count, (count==1) ? "" : "s", assetDir);
}
/*
* If a directory of resource-specific assets was supplied, slurp 'em up.
*/
for (size_t i=0; i<dirCount; i++) {
const char *res = resDirs[i];
if (res) {
type = getFileType(res);
if (type == kFileTypeNonexistent) {
fprintf(stderr, "ERROR: resource directory '%s' does not exist\n", res);
return UNKNOWN_ERROR;
}
if (type == kFileTypeDirectory) {
if (i>0) {
sp<AaptAssets> nextOverlay = new AaptAssets();
current->setOverlay(nextOverlay);
current = nextOverlay;
}
count = current->slurpResourceTree(bundle, String8(res));
if (count < 0) {
totalCount = count;
goto bail;
}
totalCount += count;
}
else {
fprintf(stderr, "ERROR: '%s' is not a directory\n", res);
return UNKNOWN_ERROR;
}
}
}
/*
* Now do any additional raw files.
*/
for (int arg=0; arg<N; arg++) {
const char* assetDir = bundle->getFileSpecEntry(arg);
FileType type = getFileType(assetDir);
if (type == kFileTypeNonexistent) {
fprintf(stderr, "ERROR: input directory '%s' does not exist\n", assetDir);
return UNKNOWN_ERROR;
}
if (type != kFileTypeDirectory) {
fprintf(stderr, "ERROR: '%s' is not a directory\n", assetDir);
return UNKNOWN_ERROR;
}
String8 assetRoot(assetDir);
if (bundle->getVerbose())
printf("Processing raw dir '%s'\n", (const char*) assetDir);
/*
* Do a recursive traversal of subdir tree. We don't make any
* 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());
if (count < 0) {
/* failure; report error and remove archive */
totalCount = count;
goto bail;
}
totalCount += count;
if (bundle->getVerbose())
printf("Found %d asset file%s in %s\n",
count, (count==1) ? "" : "s", assetDir);
}
count = validate();
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)
{
ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType);
if (res > 0) {
mGroupEntries.add(kind);
}
return res;
}
ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
{
ssize_t err = 0;
DIR* dir = opendir(srcDir.string());
if (dir == NULL) {
fprintf(stderr, "ERROR: opendir(%s): %s\n", srcDir.string(), strerror(errno));
return UNKNOWN_ERROR;
}
status_t count = 0;
/*
* Run through the directory, looking for dirs that match the
* expected pattern.
*/
while (1) {
struct dirent* entry = readdir(dir);
if (entry == NULL) {
break;
}
if (isHidden(srcDir.string(), entry->d_name)) {
continue;
}
String8 subdirName(srcDir);
subdirName.appendPath(entry->d_name);
AaptGroupEntry group;
String8 resType;
bool b = group.initFromDirName(entry->d_name, &resType);
if (!b) {
fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
entry->d_name);
err = -1;
continue;
}
FileType type = getFileType(subdirName.string());
if (type == kFileTypeDirectory) {
sp<AaptDir> dir = makeDir(String8(entry->d_name));
ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
resType);
if (res < 0) {
count = res;
goto bail;
}
if (res > 0) {
mGroupEntries.add(group);
count += res;
}
mDirs.add(dir);
} else {
if (bundle->getVerbose()) {
fprintf(stderr, " (ignoring file '%s')\n", subdirName.string());
}
}
}
bail:
closedir(dir);
dir = NULL;
if (err != 0) {
return err;
}
return count;
}
ssize_t
AaptAssets::slurpResourceZip(Bundle* bundle, const char* filename)
{
int count = 0;
SortedVector<AaptGroupEntry> entries;
ZipFile* zip = new ZipFile;
status_t err = zip->open(filename, ZipFile::kOpenReadOnly);
if (err != NO_ERROR) {
fprintf(stderr, "error opening zip file %s\n", filename);
count = err;
delete zip;
return -1;
}
const int N = zip->getNumEntries();
for (int i=0; i<N; i++) {
ZipEntry* entry = zip->getEntryByIndex(i);
if (entry->getDeleted()) {
continue;
}
String8 entryName(entry->getFileName());
String8 dirName = entryName.getPathDir();
sp<AaptDir> dir = dirName == "" ? this : makeDir(dirName);
String8 resType;
AaptGroupEntry kind;
String8 remain;
if (entryName.walkPath(&remain) == kResourceDir) {
// these are the resources, pull their type out of the directory name
kind.initFromDirName(remain.walkPath().string(), &resType);
} else {
// these are untyped and don't have an AaptGroupEntry
}
if (entries.indexOf(kind) < 0) {
entries.add(kind);
mGroupEntries.add(kind);
}
// use the one from the zip file if they both exist.
dir->removeFile(entryName.getPathLeaf());
sp<AaptFile> file = new AaptFile(entryName, kind, resType);
status_t err = dir->addLeafFile(entryName.getPathLeaf(), file);
if (err != NO_ERROR) {
fprintf(stderr, "err=%s entryName=%s\n", strerror(err), entryName.string());
count = err;
goto bail;
}
file->setCompressionMethod(entry->getCompressionMethod());
#if 0
if (entryName == "AndroidManifest.xml") {
printf("AndroidManifest.xml\n");
}
printf("\n\nfile: %s\n", entryName.string());
#endif
size_t len = entry->getUncompressedLen();
void* data = zip->uncompress(entry);
void* buf = file->editData(len);
memcpy(buf, data, len);
#if 0
const int OFF = 0;
const unsigned char* p = (unsigned char*)data;
const unsigned char* end = p+len;
p += OFF;
for (int i=0; i<32 && p < end; i++) {
printf("0x%03x ", i*0x10 + OFF);
for (int j=0; j<0x10 && p < end; j++) {
printf(" %02x", *p);
p++;
}
printf("\n");
}
#endif
free(data);
count++;
}
bail:
delete zip;
return count;
}
sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
{
sp<AaptSymbols> sym = mSymbols.valueFor(name);
if (sym == NULL) {
sym = new AaptSymbols();
mSymbols.add(name, sym);
}
return sym;
}
status_t AaptAssets::buildIncludedResources(Bundle* bundle)
{
if (!mHaveIncludedAssets) {
// Add in all includes.
const Vector<const char*>& incl = bundle->getPackageIncludes();
const size_t N=incl.size();
for (size_t i=0; i<N; i++) {
if (bundle->getVerbose())
printf("Including resources from package: %s\n", incl[i]);
if (!mIncludedAssets.addAssetPath(String8(incl[i]), NULL)) {
fprintf(stderr, "ERROR: Asset package include '%s' not found.\n",
incl[i]);
return UNKNOWN_ERROR;
}
}
mHaveIncludedAssets = true;
}
return NO_ERROR;
}
status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file)
{
const ResTable& res = getIncludedResources();
// XXX dirty!
return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL);
}
const ResTable& AaptAssets::getIncludedResources() const
{
return mIncludedAssets.getResources(false);
}
void AaptAssets::print() const
{
printf("Locale/Vendor pairs:\n");
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());
}
printf("\nFiles:\n");
AaptDir::print();
}
sp<AaptDir> AaptAssets::resDir(const String8& name)
{
const Vector<sp<AaptDir> >& dirs = mDirs;
const size_t N = dirs.size();
for (size_t i=0; i<N; i++) {
const sp<AaptDir>& d = dirs.itemAt(i);
if (d->getLeaf() == name) {
return d;
}
}
return NULL;
}
bool
valid_symbol_name(const String8& symbol)
{
static char const * const KEYWORDS[] = {
"abstract", "assert", "boolean", "break",
"byte", "case", "catch", "char", "class", "const", "continue",
"default", "do", "double", "else", "enum", "extends", "final",
"finally", "float", "for", "goto", "if", "implements", "import",
"instanceof", "int", "interface", "long", "native", "new", "package",
"private", "protected", "public", "return", "short", "static",
"strictfp", "super", "switch", "synchronized", "this", "throw",
"throws", "transient", "try", "void", "volatile", "while",
"true", "false", "null",
NULL
};
const char*const* k = KEYWORDS;
const char*const s = symbol.string();
while (*k) {
if (0 == strcmp(s, *k)) {
return false;
}
k++;
}
return true;
}