Resource.cpp revision 6a7d2757a936ee79b8dba2055139c8b71130c58e
1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// 2f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Copyright 2006 The Android Open Source Project 3f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// 4f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Build resource files from raw assets. 5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// 6f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "Main.h" 7f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "AaptAssets.h" 8f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "StringPool.h" 9f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "XMLNode.h" 10f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ResourceTable.h" 11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "Images.h" 12f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "CrunchCache.h" 14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "FileFinder.h" 15f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "CacheUpdater.h" 16f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 17f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "WorkQueue.h" 18f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 19f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#if HAVE_PRINTF_ZD 20f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# define ZD "%zd" 21f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# define ZD_TYPE ssize_t 22f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#else 23f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# define ZD "%ld" 24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# define ZD_TYPE long 25f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#endif 26f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 27f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#define NOISY(x) // x 28f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 29f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Number of threads to use for preprocessing images. 30f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)static const size_t MAX_THREADS = 4; 31f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 32f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// ========================================================================== 33f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// ========================================================================== 34f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// ========================================================================== 35f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 36f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)class PackageInfo 37f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles){ 38f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)public: 39f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) PackageInfo() 40f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) { 41f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 42f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) ~PackageInfo() 43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) { 44f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 45f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 46f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) status_t parsePackage(const sp<AaptGroup>& grp); 47f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}; 48f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 49f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// ========================================================================== 50f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// ========================================================================== 51f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// ========================================================================== 52f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 53f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)static String8 parseResourceName(const String8& leaf) 54f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles){ 55f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const char* firstDot = strchr(leaf.string(), '.'); 56f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const char* str = leaf.string(); 57f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 58f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (firstDot) { 59f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return String8(str, firstDot-str); 60f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } else { 61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return String8(str); 62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 64f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 65f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)ResourceTypeSet::ResourceTypeSet() 66f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) :RefBase(), 67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) KeyedVector<String8,sp<AaptGroup> >() 68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles){ 69f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 70f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)FilePathStore::FilePathStore() 72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) :RefBase(), 73f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Vector<String8>() 74f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles){ 75f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 77f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)class ResourceDirIterator 78f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles){ 79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)public: 80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) ResourceDirIterator(const sp<ResourceTypeSet>& set, const String8& resType) 81f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) : mResType(resType), mSet(set), mSetPos(0), mGroupPos(0) 82f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) { 83f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) memset(&mParams, 0, sizeof(ResTable_config)); 84f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 85f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 86f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) inline const sp<AaptGroup>& getGroup() const { return mGroup; } 87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) inline const sp<AaptFile>& getFile() const { return mFile; } 88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 89f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) inline const String8& getBaseName() const { return mBaseName; } 90f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) inline const String8& getLeafName() const { return mLeafName; } 91f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) inline String8 getPath() const { return mPath; } 92f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) inline const ResTable_config& getParams() const { return mParams; } 93f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 94f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) enum { 95f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) EOD = 1 96f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) }; 97f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 98f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) ssize_t next() 99f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) { 100f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) while (true) { 101f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) sp<AaptGroup> group; 102f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) sp<AaptFile> file; 103f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 104f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Try to get next file in this current group. 105f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (mGroup != NULL && mGroupPos < mGroup->getFiles().size()) { 106f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) group = mGroup; 107f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) file = group->getFiles().valueAt(mGroupPos++); 108f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 109f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Try to get the next group/file in this directory 1105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else if (mSetPos < mSet->size()) { 1115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu mGroup = group = mSet->valueAt(mSetPos++); 1125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (group->getFiles().size() < 1) { 1135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu continue; 1145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 1155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu file = group->getFiles().valueAt(0); 116f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) mGroupPos = 1; 117f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 118f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // All done! 119f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } else { 120f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return EOD; 121f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 122 123 mFile = file; 124 125 String8 leaf(group->getLeaf()); 126 mLeafName = String8(leaf); 127 mParams = file->getGroupEntry().toParams(); 128 NOISY(printf("Dir %s: mcc=%d mnc=%d lang=%c%c cnt=%c%c orient=%d ui=%d density=%d touch=%d key=%d inp=%d nav=%d\n", 129 group->getPath().string(), mParams.mcc, mParams.mnc, 130 mParams.language[0] ? mParams.language[0] : '-', 131 mParams.language[1] ? mParams.language[1] : '-', 132 mParams.country[0] ? mParams.country[0] : '-', 133 mParams.country[1] ? mParams.country[1] : '-', 134 mParams.orientation, mParams.uiMode, 135 mParams.density, mParams.touchscreen, mParams.keyboard, 136 mParams.inputFlags, mParams.navigation)); 137 mPath = "res"; 138 mPath.appendPath(file->getGroupEntry().toDirName(mResType)); 139 mPath.appendPath(leaf); 140 mBaseName = parseResourceName(leaf); 141 if (mBaseName == "") { 142 fprintf(stderr, "Error: malformed resource filename %s\n", 143 file->getPrintableSource().string()); 144 return UNKNOWN_ERROR; 145 } 146 147 NOISY(printf("file name=%s\n", mBaseName.string())); 148 149 return NO_ERROR; 150 } 151 } 152 153private: 154 String8 mResType; 155 156 const sp<ResourceTypeSet> mSet; 157 size_t mSetPos; 158 159 sp<AaptGroup> mGroup; 160 size_t mGroupPos; 161 162 sp<AaptFile> mFile; 163 String8 mBaseName; 164 String8 mLeafName; 165 String8 mPath; 166 ResTable_config mParams; 167}; 168 169class AnnotationProcessor { 170public: 171 AnnotationProcessor() : mDeprecated(false), mSystemApi(false) { } 172 173 void preprocessComment(String8& comment) { 174 if (comment.size() > 0) { 175 if (comment.contains("@deprecated")) { 176 mDeprecated = true; 177 } 178 if (comment.removeAll("@SystemApi")) { 179 mSystemApi = true; 180 } 181 } 182 } 183 184 void printAnnotations(FILE* fp, const char* indentStr) { 185 if (mDeprecated) { 186 fprintf(fp, "%s@Deprecated\n", indentStr); 187 } 188 if (mSystemApi) { 189 fprintf(fp, "%s@android.annotation.SystemApi\n", indentStr); 190 } 191 } 192 193private: 194 bool mDeprecated; 195 bool mSystemApi; 196}; 197 198// ========================================================================== 199// ========================================================================== 200// ========================================================================== 201 202bool isValidResourceType(const String8& type) 203{ 204 return type == "anim" || type == "animator" || type == "interpolator" 205 || type == "transition" 206 || type == "drawable" || type == "layout" 207 || type == "values" || type == "xml" || type == "raw" 208 || type == "color" || type == "menu" || type == "mipmap"; 209} 210 211static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets, 212 const sp<AaptGroup>& grp) 213{ 214 if (grp->getFiles().size() != 1) { 215 fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n", 216 grp->getFiles().valueAt(0)->getPrintableSource().string()); 217 } 218 219 sp<AaptFile> file = grp->getFiles().valueAt(0); 220 221 ResXMLTree block; 222 status_t err = parseXMLResource(file, &block); 223 if (err != NO_ERROR) { 224 return err; 225 } 226 //printXMLBlock(&block); 227 228 ResXMLTree::event_code_t code; 229 while ((code=block.next()) != ResXMLTree::START_TAG 230 && code != ResXMLTree::END_DOCUMENT 231 && code != ResXMLTree::BAD_DOCUMENT) { 232 } 233 234 size_t len; 235 if (code != ResXMLTree::START_TAG) { 236 fprintf(stderr, "%s:%d: No start tag found\n", 237 file->getPrintableSource().string(), block.getLineNumber()); 238 return UNKNOWN_ERROR; 239 } 240 if (strcmp16(block.getElementName(&len), String16("manifest").string()) != 0) { 241 fprintf(stderr, "%s:%d: Invalid start tag %s, expected <manifest>\n", 242 file->getPrintableSource().string(), block.getLineNumber(), 243 String8(block.getElementName(&len)).string()); 244 return UNKNOWN_ERROR; 245 } 246 247 ssize_t nameIndex = block.indexOfAttribute(NULL, "package"); 248 if (nameIndex < 0) { 249 fprintf(stderr, "%s:%d: <manifest> does not have package attribute.\n", 250 file->getPrintableSource().string(), block.getLineNumber()); 251 return UNKNOWN_ERROR; 252 } 253 254 assets->setPackage(String8(block.getAttributeStringValue(nameIndex, &len))); 255 256 String16 uses_sdk16("uses-sdk"); 257 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 258 && code != ResXMLTree::BAD_DOCUMENT) { 259 if (code == ResXMLTree::START_TAG) { 260 if (strcmp16(block.getElementName(&len), uses_sdk16.string()) == 0) { 261 ssize_t minSdkIndex = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, 262 "minSdkVersion"); 263 if (minSdkIndex >= 0) { 264 const uint16_t* minSdk16 = block.getAttributeStringValue(minSdkIndex, &len); 265 const char* minSdk8 = strdup(String8(minSdk16).string()); 266 bundle->setManifestMinSdkVersion(minSdk8); 267 } 268 } 269 } 270 } 271 272 return NO_ERROR; 273} 274 275// ========================================================================== 276// ========================================================================== 277// ========================================================================== 278 279static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets, 280 ResourceTable* table, 281 const sp<ResourceTypeSet>& set, 282 const char* resType) 283{ 284 String8 type8(resType); 285 String16 type16(resType); 286 287 bool hasErrors = false; 288 289 ResourceDirIterator it(set, String8(resType)); 290 ssize_t res; 291 while ((res=it.next()) == NO_ERROR) { 292 if (bundle->getVerbose()) { 293 printf(" (new resource id %s from %s)\n", 294 it.getBaseName().string(), it.getFile()->getPrintableSource().string()); 295 } 296 String16 baseName(it.getBaseName()); 297 const char16_t* str = baseName.string(); 298 const char16_t* const end = str + baseName.size(); 299 while (str < end) { 300 if (!((*str >= 'a' && *str <= 'z') 301 || (*str >= '0' && *str <= '9') 302 || *str == '_' || *str == '.')) { 303 fprintf(stderr, "%s: Invalid file name: must contain only [a-z0-9_.]\n", 304 it.getPath().string()); 305 hasErrors = true; 306 } 307 str++; 308 } 309 String8 resPath = it.getPath(); 310 resPath.convertToResPath(); 311 table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()), 312 type16, 313 baseName, 314 String16(resPath), 315 NULL, 316 &it.getParams()); 317 assets->addResource(it.getLeafName(), resPath, it.getFile(), type8); 318 } 319 320 return hasErrors ? UNKNOWN_ERROR : NO_ERROR; 321} 322 323class PreProcessImageWorkUnit : public WorkQueue::WorkUnit { 324public: 325 PreProcessImageWorkUnit(const Bundle* bundle, const sp<AaptAssets>& assets, 326 const sp<AaptFile>& file, volatile bool* hasErrors) : 327 mBundle(bundle), mAssets(assets), mFile(file), mHasErrors(hasErrors) { 328 } 329 330 virtual bool run() { 331 status_t status = preProcessImage(mBundle, mAssets, mFile, NULL); 332 if (status) { 333 *mHasErrors = true; 334 } 335 return true; // continue even if there are errors 336 } 337 338private: 339 const Bundle* mBundle; 340 sp<AaptAssets> mAssets; 341 sp<AaptFile> mFile; 342 volatile bool* mHasErrors; 343}; 344 345static status_t preProcessImages(const Bundle* bundle, const sp<AaptAssets>& assets, 346 const sp<ResourceTypeSet>& set, const char* type) 347{ 348 volatile bool hasErrors = false; 349 ssize_t res = NO_ERROR; 350 if (bundle->getUseCrunchCache() == false) { 351 WorkQueue wq(MAX_THREADS, false); 352 ResourceDirIterator it(set, String8(type)); 353 while ((res=it.next()) == NO_ERROR) { 354 PreProcessImageWorkUnit* w = new PreProcessImageWorkUnit( 355 bundle, assets, it.getFile(), &hasErrors); 356 status_t status = wq.schedule(w); 357 if (status) { 358 fprintf(stderr, "preProcessImages failed: schedule() returned %d\n", status); 359 hasErrors = true; 360 delete w; 361 break; 362 } 363 } 364 status_t status = wq.finish(); 365 if (status) { 366 fprintf(stderr, "preProcessImages failed: finish() returned %d\n", status); 367 hasErrors = true; 368 } 369 } 370 return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR; 371} 372 373static void collect_files(const sp<AaptDir>& dir, 374 KeyedVector<String8, sp<ResourceTypeSet> >* resources) 375{ 376 const DefaultKeyedVector<String8, sp<AaptGroup> >& groups = dir->getFiles(); 377 int N = groups.size(); 378 for (int i=0; i<N; i++) { 379 String8 leafName = groups.keyAt(i); 380 const sp<AaptGroup>& group = groups.valueAt(i); 381 382 const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files 383 = group->getFiles(); 384 385 if (files.size() == 0) { 386 continue; 387 } 388 389 String8 resType = files.valueAt(0)->getResourceType(); 390 391 ssize_t index = resources->indexOfKey(resType); 392 393 if (index < 0) { 394 sp<ResourceTypeSet> set = new ResourceTypeSet(); 395 NOISY(printf("Creating new resource type set for leaf %s with group %s (%p)\n", 396 leafName.string(), group->getPath().string(), group.get())); 397 set->add(leafName, group); 398 resources->add(resType, set); 399 } else { 400 sp<ResourceTypeSet> set = resources->valueAt(index); 401 index = set->indexOfKey(leafName); 402 if (index < 0) { 403 NOISY(printf("Adding to resource type set for leaf %s group %s (%p)\n", 404 leafName.string(), group->getPath().string(), group.get())); 405 set->add(leafName, group); 406 } else { 407 sp<AaptGroup> existingGroup = set->valueAt(index); 408 NOISY(printf("Extending to resource type set for leaf %s group %s (%p)\n", 409 leafName.string(), group->getPath().string(), group.get())); 410 for (size_t j=0; j<files.size(); j++) { 411 NOISY(printf("Adding file %s in group %s resType %s\n", 412 files.valueAt(j)->getSourceFile().string(), 413 files.keyAt(j).toDirName(String8()).string(), 414 resType.string())); 415 status_t err = existingGroup->addFile(files.valueAt(j)); 416 } 417 } 418 } 419 } 420} 421 422static void collect_files(const sp<AaptAssets>& ass, 423 KeyedVector<String8, sp<ResourceTypeSet> >* resources) 424{ 425 const Vector<sp<AaptDir> >& dirs = ass->resDirs(); 426 int N = dirs.size(); 427 428 for (int i=0; i<N; i++) { 429 sp<AaptDir> d = dirs.itemAt(i); 430 NOISY(printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(), 431 d->getLeaf().string())); 432 collect_files(d, resources); 433 434 // don't try to include the res dir 435 NOISY(printf("Removing dir leaf %s\n", d->getLeaf().string())); 436 ass->removeDir(d->getLeaf()); 437 } 438} 439 440enum { 441 ATTR_OKAY = -1, 442 ATTR_NOT_FOUND = -2, 443 ATTR_LEADING_SPACES = -3, 444 ATTR_TRAILING_SPACES = -4 445}; 446static int validateAttr(const String8& path, const ResTable& table, 447 const ResXMLParser& parser, 448 const char* ns, const char* attr, const char* validChars, bool required) 449{ 450 size_t len; 451 452 ssize_t index = parser.indexOfAttribute(ns, attr); 453 const uint16_t* str; 454 Res_value value; 455 if (index >= 0 && parser.getAttributeValue(index, &value) >= 0) { 456 const ResStringPool* pool = &parser.getStrings(); 457 if (value.dataType == Res_value::TYPE_REFERENCE) { 458 uint32_t specFlags = 0; 459 int strIdx; 460 if ((strIdx=table.resolveReference(&value, 0x10000000, NULL, &specFlags)) < 0) { 461 fprintf(stderr, "%s:%d: Tag <%s> attribute %s references unknown resid 0x%08x.\n", 462 path.string(), parser.getLineNumber(), 463 String8(parser.getElementName(&len)).string(), attr, 464 value.data); 465 return ATTR_NOT_FOUND; 466 } 467 468 pool = table.getTableStringBlock(strIdx); 469 #if 0 470 if (pool != NULL) { 471 str = pool->stringAt(value.data, &len); 472 } 473 printf("***** RES ATTR: %s specFlags=0x%x strIdx=%d: %s\n", attr, 474 specFlags, strIdx, str != NULL ? String8(str).string() : "???"); 475 #endif 476 if ((specFlags&~ResTable_typeSpec::SPEC_PUBLIC) != 0 && false) { 477 fprintf(stderr, "%s:%d: Tag <%s> attribute %s varies by configurations 0x%x.\n", 478 path.string(), parser.getLineNumber(), 479 String8(parser.getElementName(&len)).string(), attr, 480 specFlags); 481 return ATTR_NOT_FOUND; 482 } 483 } 484 if (value.dataType == Res_value::TYPE_STRING) { 485 if (pool == NULL) { 486 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has no string block.\n", 487 path.string(), parser.getLineNumber(), 488 String8(parser.getElementName(&len)).string(), attr); 489 return ATTR_NOT_FOUND; 490 } 491 if ((str=pool->stringAt(value.data, &len)) == NULL) { 492 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n", 493 path.string(), parser.getLineNumber(), 494 String8(parser.getElementName(&len)).string(), attr); 495 return ATTR_NOT_FOUND; 496 } 497 } else { 498 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid type %d.\n", 499 path.string(), parser.getLineNumber(), 500 String8(parser.getElementName(&len)).string(), attr, 501 value.dataType); 502 return ATTR_NOT_FOUND; 503 } 504 if (validChars) { 505 for (size_t i=0; i<len; i++) { 506 uint16_t c = str[i]; 507 const char* p = validChars; 508 bool okay = false; 509 while (*p) { 510 if (c == *p) { 511 okay = true; 512 break; 513 } 514 p++; 515 } 516 if (!okay) { 517 fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid character '%c'.\n", 518 path.string(), parser.getLineNumber(), 519 String8(parser.getElementName(&len)).string(), attr, (char)str[i]); 520 return (int)i; 521 } 522 } 523 } 524 if (*str == ' ') { 525 fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not start with a space.\n", 526 path.string(), parser.getLineNumber(), 527 String8(parser.getElementName(&len)).string(), attr); 528 return ATTR_LEADING_SPACES; 529 } 530 if (str[len-1] == ' ') { 531 fprintf(stderr, "%s:%d: Tag <%s> attribute %s can not end with a space.\n", 532 path.string(), parser.getLineNumber(), 533 String8(parser.getElementName(&len)).string(), attr); 534 return ATTR_TRAILING_SPACES; 535 } 536 return ATTR_OKAY; 537 } 538 if (required) { 539 fprintf(stderr, "%s:%d: Tag <%s> missing required attribute %s.\n", 540 path.string(), parser.getLineNumber(), 541 String8(parser.getElementName(&len)).string(), attr); 542 return ATTR_NOT_FOUND; 543 } 544 return ATTR_OKAY; 545} 546 547static void checkForIds(const String8& path, ResXMLParser& parser) 548{ 549 ResXMLTree::event_code_t code; 550 while ((code=parser.next()) != ResXMLTree::END_DOCUMENT 551 && code > ResXMLTree::BAD_DOCUMENT) { 552 if (code == ResXMLTree::START_TAG) { 553 ssize_t index = parser.indexOfAttribute(NULL, "id"); 554 if (index >= 0) { 555 fprintf(stderr, "%s:%d: warning: found plain 'id' attribute; did you mean the new 'android:id' name?\n", 556 path.string(), parser.getLineNumber()); 557 } 558 } 559 } 560} 561 562static bool applyFileOverlay(Bundle *bundle, 563 const sp<AaptAssets>& assets, 564 sp<ResourceTypeSet> *baseSet, 565 const char *resType) 566{ 567 if (bundle->getVerbose()) { 568 printf("applyFileOverlay for %s\n", resType); 569 } 570 571 // Replace any base level files in this category with any found from the overlay 572 // Also add any found only in the overlay. 573 sp<AaptAssets> overlay = assets->getOverlay(); 574 String8 resTypeString(resType); 575 576 // work through the linked list of overlays 577 while (overlay.get()) { 578 KeyedVector<String8, sp<ResourceTypeSet> >* overlayRes = overlay->getResources(); 579 580 // get the overlay resources of the requested type 581 ssize_t index = overlayRes->indexOfKey(resTypeString); 582 if (index >= 0) { 583 sp<ResourceTypeSet> overlaySet = overlayRes->valueAt(index); 584 585 // for each of the resources, check for a match in the previously built 586 // non-overlay "baseset". 587 size_t overlayCount = overlaySet->size(); 588 for (size_t overlayIndex=0; overlayIndex<overlayCount; overlayIndex++) { 589 if (bundle->getVerbose()) { 590 printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string()); 591 } 592 ssize_t baseIndex = -1; 593 if (baseSet->get() != NULL) { 594 baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex)); 595 } 596 if (baseIndex >= 0) { 597 // look for same flavor. For a given file (strings.xml, for example) 598 // there may be a locale specific or other flavors - we want to match 599 // the same flavor. 600 sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex); 601 sp<AaptGroup> baseGroup = (*baseSet)->valueAt(baseIndex); 602 603 DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles = 604 overlayGroup->getFiles(); 605 if (bundle->getVerbose()) { 606 DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles = 607 baseGroup->getFiles(); 608 for (size_t i=0; i < baseFiles.size(); i++) { 609 printf("baseFile " ZD " has flavor %s\n", (ZD_TYPE) i, 610 baseFiles.keyAt(i).toString().string()); 611 } 612 for (size_t i=0; i < overlayFiles.size(); i++) { 613 printf("overlayFile " ZD " has flavor %s\n", (ZD_TYPE) i, 614 overlayFiles.keyAt(i).toString().string()); 615 } 616 } 617 618 size_t overlayGroupSize = overlayFiles.size(); 619 for (size_t overlayGroupIndex = 0; 620 overlayGroupIndex<overlayGroupSize; 621 overlayGroupIndex++) { 622 ssize_t baseFileIndex = 623 baseGroup->getFiles().indexOfKey(overlayFiles. 624 keyAt(overlayGroupIndex)); 625 if (baseFileIndex >= 0) { 626 if (bundle->getVerbose()) { 627 printf("found a match (" ZD ") for overlay file %s, for flavor %s\n", 628 (ZD_TYPE) baseFileIndex, 629 overlayGroup->getLeaf().string(), 630 overlayFiles.keyAt(overlayGroupIndex).toString().string()); 631 } 632 baseGroup->removeFile(baseFileIndex); 633 } else { 634 // didn't find a match fall through and add it.. 635 if (true || bundle->getVerbose()) { 636 printf("nothing matches overlay file %s, for flavor %s\n", 637 overlayGroup->getLeaf().string(), 638 overlayFiles.keyAt(overlayGroupIndex).toString().string()); 639 } 640 } 641 baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex)); 642 assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex)); 643 } 644 } else { 645 if (baseSet->get() == NULL) { 646 *baseSet = new ResourceTypeSet(); 647 assets->getResources()->add(String8(resType), *baseSet); 648 } 649 // this group doesn't exist (a file that's only in the overlay) 650 (*baseSet)->add(overlaySet->keyAt(overlayIndex), 651 overlaySet->valueAt(overlayIndex)); 652 // make sure all flavors are defined in the resources. 653 sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex); 654 DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles = 655 overlayGroup->getFiles(); 656 size_t overlayGroupSize = overlayFiles.size(); 657 for (size_t overlayGroupIndex = 0; 658 overlayGroupIndex<overlayGroupSize; 659 overlayGroupIndex++) { 660 assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex)); 661 } 662 } 663 } 664 // this overlay didn't have resources for this type 665 } 666 // try next overlay 667 overlay = overlay->getOverlay(); 668 } 669 return true; 670} 671 672/* 673 * Inserts an attribute in a given node. 674 * If errorOnFailedInsert is true, and the attribute already exists, returns false. 675 * If replaceExisting is true, the attribute will be updated if it already exists. 676 * Returns true otherwise, even if the attribute already exists, and does not modify 677 * the existing attribute's value. 678 */ 679bool addTagAttribute(const sp<XMLNode>& node, const char* ns8, 680 const char* attr8, const char* value, bool errorOnFailedInsert, 681 bool replaceExisting) 682{ 683 if (value == NULL) { 684 return true; 685 } 686 687 const String16 ns(ns8); 688 const String16 attr(attr8); 689 690 XMLNode::attribute_entry* existingEntry = node->editAttribute(ns, attr); 691 if (existingEntry != NULL) { 692 if (replaceExisting) { 693 NOISY(printf("Info: AndroidManifest.xml already defines %s (in %s);" 694 " overwriting existing value from manifest.\n", 695 String8(attr).string(), String8(ns).string())); 696 existingEntry->string = String16(value); 697 return true; 698 } 699 700 if (errorOnFailedInsert) { 701 fprintf(stderr, "Error: AndroidManifest.xml already defines %s (in %s);" 702 " cannot insert new value %s.\n", 703 String8(attr).string(), String8(ns).string(), value); 704 return false; 705 } 706 707 fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);" 708 " using existing value in manifest.\n", 709 String8(attr).string(), String8(ns).string()); 710 711 // don't stop the build. 712 return true; 713 } 714 715 node->addAttribute(ns, attr, String16(value)); 716 return true; 717} 718 719/* 720 * Inserts an attribute in a given node, only if the attribute does not 721 * exist. 722 * If errorOnFailedInsert is true, and the attribute already exists, returns false. 723 * Returns true otherwise, even if the attribute already exists. 724 */ 725bool addTagAttribute(const sp<XMLNode>& node, const char* ns8, 726 const char* attr8, const char* value, bool errorOnFailedInsert) 727{ 728 return addTagAttribute(node, ns8, attr8, value, errorOnFailedInsert, false); 729} 730 731static void fullyQualifyClassName(const String8& package, sp<XMLNode> node, 732 const String16& attrName) { 733 XMLNode::attribute_entry* attr = node->editAttribute( 734 String16("http://schemas.android.com/apk/res/android"), attrName); 735 if (attr != NULL) { 736 String8 name(attr->string); 737 738 // asdf --> package.asdf 739 // .asdf .a.b --> package.asdf package.a.b 740 // asdf.adsf --> asdf.asdf 741 String8 className; 742 const char* p = name.string(); 743 const char* q = strchr(p, '.'); 744 if (p == q) { 745 className += package; 746 className += name; 747 } else if (q == NULL) { 748 className += package; 749 className += "."; 750 className += name; 751 } else { 752 className += name; 753 } 754 NOISY(printf("Qualifying class '%s' to '%s'", name.string(), className.string())); 755 attr->string.setTo(String16(className)); 756 } 757} 758 759status_t massageManifest(Bundle* bundle, sp<XMLNode> root) 760{ 761 root = root->searchElement(String16(), String16("manifest")); 762 if (root == NULL) { 763 fprintf(stderr, "No <manifest> tag.\n"); 764 return UNKNOWN_ERROR; 765 } 766 767 bool errorOnFailedInsert = bundle->getErrorOnFailedInsert(); 768 bool replaceVersion = bundle->getReplaceVersion(); 769 770 if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode", 771 bundle->getVersionCode(), errorOnFailedInsert, replaceVersion)) { 772 return UNKNOWN_ERROR; 773 } else { 774 const XMLNode::attribute_entry* attr = root->getAttribute( 775 String16(RESOURCES_ANDROID_NAMESPACE), String16("versionCode")); 776 if (attr != NULL) { 777 bundle->setVersionCode(strdup(String8(attr->string).string())); 778 } 779 } 780 781 if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName", 782 bundle->getVersionName(), errorOnFailedInsert, replaceVersion)) { 783 return UNKNOWN_ERROR; 784 } 785 786 if (bundle->getMinSdkVersion() != NULL 787 || bundle->getTargetSdkVersion() != NULL 788 || bundle->getMaxSdkVersion() != NULL) { 789 sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk")); 790 if (vers == NULL) { 791 vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk")); 792 root->insertChildAt(vers, 0); 793 } 794 795 if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion", 796 bundle->getMinSdkVersion(), errorOnFailedInsert)) { 797 return UNKNOWN_ERROR; 798 } 799 if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion", 800 bundle->getTargetSdkVersion(), errorOnFailedInsert)) { 801 return UNKNOWN_ERROR; 802 } 803 if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion", 804 bundle->getMaxSdkVersion(), errorOnFailedInsert)) { 805 return UNKNOWN_ERROR; 806 } 807 } 808 809 if (bundle->getDebugMode()) { 810 sp<XMLNode> application = root->getChildElement(String16(), String16("application")); 811 if (application != NULL) { 812 if (!addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true", 813 errorOnFailedInsert)) { 814 return UNKNOWN_ERROR; 815 } 816 } 817 } 818 819 // Deal with manifest package name overrides 820 const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride(); 821 if (manifestPackageNameOverride != NULL) { 822 // Update the actual package name 823 XMLNode::attribute_entry* attr = root->editAttribute(String16(), String16("package")); 824 if (attr == NULL) { 825 fprintf(stderr, "package name is required with --rename-manifest-package.\n"); 826 return UNKNOWN_ERROR; 827 } 828 String8 origPackage(attr->string); 829 attr->string.setTo(String16(manifestPackageNameOverride)); 830 NOISY(printf("Overriding package '%s' to be '%s'\n", origPackage.string(), manifestPackageNameOverride)); 831 832 // Make class names fully qualified 833 sp<XMLNode> application = root->getChildElement(String16(), String16("application")); 834 if (application != NULL) { 835 fullyQualifyClassName(origPackage, application, String16("name")); 836 fullyQualifyClassName(origPackage, application, String16("backupAgent")); 837 838 Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren()); 839 for (size_t i = 0; i < children.size(); i++) { 840 sp<XMLNode> child = children.editItemAt(i); 841 String8 tag(child->getElementName()); 842 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") { 843 fullyQualifyClassName(origPackage, child, String16("name")); 844 } else if (tag == "activity-alias") { 845 fullyQualifyClassName(origPackage, child, String16("name")); 846 fullyQualifyClassName(origPackage, child, String16("targetActivity")); 847 } 848 } 849 } 850 } 851 852 // Deal with manifest package name overrides 853 const char* instrumentationPackageNameOverride = bundle->getInstrumentationPackageNameOverride(); 854 if (instrumentationPackageNameOverride != NULL) { 855 // Fix up instrumentation targets. 856 Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(root->getChildren()); 857 for (size_t i = 0; i < children.size(); i++) { 858 sp<XMLNode> child = children.editItemAt(i); 859 String8 tag(child->getElementName()); 860 if (tag == "instrumentation") { 861 XMLNode::attribute_entry* attr = child->editAttribute( 862 String16("http://schemas.android.com/apk/res/android"), String16("targetPackage")); 863 if (attr != NULL) { 864 attr->string.setTo(String16(instrumentationPackageNameOverride)); 865 } 866 } 867 } 868 } 869 870 // Generate split name if feature is present. 871 const XMLNode::attribute_entry* attr = root->getAttribute(String16(), String16("featureName")); 872 if (attr != NULL) { 873 String16 splitName("feature_"); 874 splitName.append(attr->string); 875 status_t err = root->addAttribute(String16(), String16("split"), splitName); 876 if (err != NO_ERROR) { 877 ALOGE("Failed to insert split name into AndroidManifest.xml"); 878 return err; 879 } 880 } 881 882 return NO_ERROR; 883} 884 885#define ASSIGN_IT(n) \ 886 do { \ 887 ssize_t index = resources->indexOfKey(String8(#n)); \ 888 if (index >= 0) { \ 889 n ## s = resources->valueAt(index); \ 890 } \ 891 } while (0) 892 893status_t updatePreProcessedCache(Bundle* bundle) 894{ 895 #if BENCHMARK 896 fprintf(stdout, "BENCHMARK: Starting PNG PreProcessing \n"); 897 long startPNGTime = clock(); 898 #endif /* BENCHMARK */ 899 900 String8 source(bundle->getResourceSourceDirs()[0]); 901 String8 dest(bundle->getCrunchedOutputDir()); 902 903 FileFinder* ff = new SystemFileFinder(); 904 CrunchCache cc(source,dest,ff); 905 906 CacheUpdater* cu = new SystemCacheUpdater(bundle); 907 size_t numFiles = cc.crunch(cu); 908 909 if (bundle->getVerbose()) 910 fprintf(stdout, "Crunched %d PNG files to update cache\n", (int)numFiles); 911 912 delete ff; 913 delete cu; 914 915 #if BENCHMARK 916 fprintf(stdout, "BENCHMARK: End PNG PreProcessing. Time Elapsed: %f ms \n" 917 ,(clock() - startPNGTime)/1000.0); 918 #endif /* BENCHMARK */ 919 return 0; 920} 921 922status_t generateAndroidManifestForSplit(Bundle* bundle, const sp<AaptAssets>& assets, 923 const sp<ApkSplit>& split, sp<AaptFile>& outFile, ResourceTable* table) { 924 const String8 filename("AndroidManifest.xml"); 925 const String16 androidPrefix("android"); 926 const String16 androidNSUri("http://schemas.android.com/apk/res/android"); 927 sp<XMLNode> root = XMLNode::newNamespace(filename, androidPrefix, androidNSUri); 928 929 // Build the <manifest> tag 930 sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest")); 931 932 // Add the 'package' attribute which is set to the package name. 933 const char* packageName = assets->getPackage(); 934 const char* manifestPackageNameOverride = bundle->getManifestPackageNameOverride(); 935 if (manifestPackageNameOverride != NULL) { 936 packageName = manifestPackageNameOverride; 937 } 938 manifest->addAttribute(String16(), String16("package"), String16(packageName)); 939 940 // Add the 'versionCode' attribute which is set to the original version code. 941 if (!addTagAttribute(manifest, RESOURCES_ANDROID_NAMESPACE, "versionCode", 942 bundle->getVersionCode(), true, true)) { 943 return UNKNOWN_ERROR; 944 } 945 946 // Add the 'split' attribute which describes the configurations included. 947 String8 splitName("config."); 948 splitName.append(split->getPackageSafeName()); 949 manifest->addAttribute(String16(), String16("split"), String16(splitName)); 950 951 // Build an empty <application> tag (required). 952 sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application")); 953 954 // Add the 'hasCode' attribute which is never true for resource splits. 955 if (!addTagAttribute(app, RESOURCES_ANDROID_NAMESPACE, "hasCode", 956 "false", true, true)) { 957 return UNKNOWN_ERROR; 958 } 959 960 manifest->addChild(app); 961 root->addChild(manifest); 962 963 int err = compileXmlFile(assets, root, outFile, table); 964 if (err < NO_ERROR) { 965 return err; 966 } 967 outFile->setCompressionMethod(ZipEntry::kCompressDeflated); 968 return NO_ERROR; 969} 970 971status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder) 972{ 973 // First, look for a package file to parse. This is required to 974 // be able to generate the resource information. 975 sp<AaptGroup> androidManifestFile = 976 assets->getFiles().valueFor(String8("AndroidManifest.xml")); 977 if (androidManifestFile == NULL) { 978 fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n"); 979 return UNKNOWN_ERROR; 980 } 981 982 status_t err = parsePackage(bundle, assets, androidManifestFile); 983 if (err != NO_ERROR) { 984 return err; 985 } 986 987 NOISY(printf("Creating resources for package %s\n", 988 assets->getPackage().string())); 989 990 ResourceTable::PackageType packageType = ResourceTable::App; 991 if (bundle->getBuildSharedLibrary()) { 992 packageType = ResourceTable::SharedLibrary; 993 } else if (bundle->getExtending()) { 994 packageType = ResourceTable::System; 995 } else if (!bundle->getFeatureOfPackage().isEmpty()) { 996 packageType = ResourceTable::AppFeature; 997 } 998 999 ResourceTable table(bundle, String16(assets->getPackage()), packageType); 1000 err = table.addIncludedResources(bundle, assets); 1001 if (err != NO_ERROR) { 1002 return err; 1003 } 1004 1005 NOISY(printf("Found %d included resource packages\n", (int)table.size())); 1006 1007 // Standard flags for compiled XML and optional UTF-8 encoding 1008 int xmlFlags = XML_COMPILE_STANDARD_RESOURCE; 1009 1010 /* Only enable UTF-8 if the caller of aapt didn't specifically 1011 * request UTF-16 encoding and the parameters of this package 1012 * allow UTF-8 to be used. 1013 */ 1014 if (!bundle->getUTF16StringsOption()) { 1015 xmlFlags |= XML_COMPILE_UTF8; 1016 } 1017 1018 // -------------------------------------------------------------- 1019 // First, gather all resource information. 1020 // -------------------------------------------------------------- 1021 1022 // resType -> leafName -> group 1023 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 1024 new KeyedVector<String8, sp<ResourceTypeSet> >; 1025 collect_files(assets, resources); 1026 1027 sp<ResourceTypeSet> drawables; 1028 sp<ResourceTypeSet> layouts; 1029 sp<ResourceTypeSet> anims; 1030 sp<ResourceTypeSet> animators; 1031 sp<ResourceTypeSet> interpolators; 1032 sp<ResourceTypeSet> transitions; 1033 sp<ResourceTypeSet> xmls; 1034 sp<ResourceTypeSet> raws; 1035 sp<ResourceTypeSet> colors; 1036 sp<ResourceTypeSet> menus; 1037 sp<ResourceTypeSet> mipmaps; 1038 1039 ASSIGN_IT(drawable); 1040 ASSIGN_IT(layout); 1041 ASSIGN_IT(anim); 1042 ASSIGN_IT(animator); 1043 ASSIGN_IT(interpolator); 1044 ASSIGN_IT(transition); 1045 ASSIGN_IT(xml); 1046 ASSIGN_IT(raw); 1047 ASSIGN_IT(color); 1048 ASSIGN_IT(menu); 1049 ASSIGN_IT(mipmap); 1050 1051 assets->setResources(resources); 1052 // now go through any resource overlays and collect their files 1053 sp<AaptAssets> current = assets->getOverlay(); 1054 while(current.get()) { 1055 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 1056 new KeyedVector<String8, sp<ResourceTypeSet> >; 1057 current->setResources(resources); 1058 collect_files(current, resources); 1059 current = current->getOverlay(); 1060 } 1061 // apply the overlay files to the base set 1062 if (!applyFileOverlay(bundle, assets, &drawables, "drawable") || 1063 !applyFileOverlay(bundle, assets, &layouts, "layout") || 1064 !applyFileOverlay(bundle, assets, &anims, "anim") || 1065 !applyFileOverlay(bundle, assets, &animators, "animator") || 1066 !applyFileOverlay(bundle, assets, &interpolators, "interpolator") || 1067 !applyFileOverlay(bundle, assets, &transitions, "transition") || 1068 !applyFileOverlay(bundle, assets, &xmls, "xml") || 1069 !applyFileOverlay(bundle, assets, &raws, "raw") || 1070 !applyFileOverlay(bundle, assets, &colors, "color") || 1071 !applyFileOverlay(bundle, assets, &menus, "menu") || 1072 !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) { 1073 return UNKNOWN_ERROR; 1074 } 1075 1076 bool hasErrors = false; 1077 1078 if (drawables != NULL) { 1079 if (bundle->getOutputAPKFile() != NULL) { 1080 err = preProcessImages(bundle, assets, drawables, "drawable"); 1081 } 1082 if (err == NO_ERROR) { 1083 err = makeFileResources(bundle, assets, &table, drawables, "drawable"); 1084 if (err != NO_ERROR) { 1085 hasErrors = true; 1086 } 1087 } else { 1088 hasErrors = true; 1089 } 1090 } 1091 1092 if (mipmaps != NULL) { 1093 if (bundle->getOutputAPKFile() != NULL) { 1094 err = preProcessImages(bundle, assets, mipmaps, "mipmap"); 1095 } 1096 if (err == NO_ERROR) { 1097 err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap"); 1098 if (err != NO_ERROR) { 1099 hasErrors = true; 1100 } 1101 } else { 1102 hasErrors = true; 1103 } 1104 } 1105 1106 if (layouts != NULL) { 1107 err = makeFileResources(bundle, assets, &table, layouts, "layout"); 1108 if (err != NO_ERROR) { 1109 hasErrors = true; 1110 } 1111 } 1112 1113 if (anims != NULL) { 1114 err = makeFileResources(bundle, assets, &table, anims, "anim"); 1115 if (err != NO_ERROR) { 1116 hasErrors = true; 1117 } 1118 } 1119 1120 if (animators != NULL) { 1121 err = makeFileResources(bundle, assets, &table, animators, "animator"); 1122 if (err != NO_ERROR) { 1123 hasErrors = true; 1124 } 1125 } 1126 1127 if (transitions != NULL) { 1128 err = makeFileResources(bundle, assets, &table, transitions, "transition"); 1129 if (err != NO_ERROR) { 1130 hasErrors = true; 1131 } 1132 } 1133 1134 if (interpolators != NULL) { 1135 err = makeFileResources(bundle, assets, &table, interpolators, "interpolator"); 1136 if (err != NO_ERROR) { 1137 hasErrors = true; 1138 } 1139 } 1140 1141 if (xmls != NULL) { 1142 err = makeFileResources(bundle, assets, &table, xmls, "xml"); 1143 if (err != NO_ERROR) { 1144 hasErrors = true; 1145 } 1146 } 1147 1148 if (raws != NULL) { 1149 err = makeFileResources(bundle, assets, &table, raws, "raw"); 1150 if (err != NO_ERROR) { 1151 hasErrors = true; 1152 } 1153 } 1154 1155 // compile resources 1156 current = assets; 1157 while(current.get()) { 1158 KeyedVector<String8, sp<ResourceTypeSet> > *resources = 1159 current->getResources(); 1160 1161 ssize_t index = resources->indexOfKey(String8("values")); 1162 if (index >= 0) { 1163 ResourceDirIterator it(resources->valueAt(index), String8("values")); 1164 ssize_t res; 1165 while ((res=it.next()) == NO_ERROR) { 1166 sp<AaptFile> file = it.getFile(); 1167 res = compileResourceFile(bundle, assets, file, it.getParams(), 1168 (current!=assets), &table); 1169 if (res != NO_ERROR) { 1170 hasErrors = true; 1171 } 1172 } 1173 } 1174 current = current->getOverlay(); 1175 } 1176 1177 if (colors != NULL) { 1178 err = makeFileResources(bundle, assets, &table, colors, "color"); 1179 if (err != NO_ERROR) { 1180 hasErrors = true; 1181 } 1182 } 1183 1184 if (menus != NULL) { 1185 err = makeFileResources(bundle, assets, &table, menus, "menu"); 1186 if (err != NO_ERROR) { 1187 hasErrors = true; 1188 } 1189 } 1190 1191 // -------------------------------------------------------------------- 1192 // Assignment of resource IDs and initial generation of resource table. 1193 // -------------------------------------------------------------------- 1194 1195 if (table.hasResources()) { 1196 err = table.assignResourceIds(); 1197 if (err < NO_ERROR) { 1198 return err; 1199 } 1200 } 1201 1202 // -------------------------------------------------------------- 1203 // Finally, we can now we can compile XML files, which may reference 1204 // resources. 1205 // -------------------------------------------------------------- 1206 1207 if (layouts != NULL) { 1208 ResourceDirIterator it(layouts, String8("layout")); 1209 while ((err=it.next()) == NO_ERROR) { 1210 String8 src = it.getFile()->getPrintableSource(); 1211 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1212 if (err == NO_ERROR) { 1213 ResXMLTree block; 1214 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); 1215 checkForIds(src, block); 1216 } else { 1217 hasErrors = true; 1218 } 1219 } 1220 1221 if (err < NO_ERROR) { 1222 hasErrors = true; 1223 } 1224 err = NO_ERROR; 1225 } 1226 1227 if (anims != NULL) { 1228 ResourceDirIterator it(anims, String8("anim")); 1229 while ((err=it.next()) == NO_ERROR) { 1230 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1231 if (err != NO_ERROR) { 1232 hasErrors = true; 1233 } 1234 } 1235 1236 if (err < NO_ERROR) { 1237 hasErrors = true; 1238 } 1239 err = NO_ERROR; 1240 } 1241 1242 if (animators != NULL) { 1243 ResourceDirIterator it(animators, String8("animator")); 1244 while ((err=it.next()) == NO_ERROR) { 1245 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1246 if (err != NO_ERROR) { 1247 hasErrors = true; 1248 } 1249 } 1250 1251 if (err < NO_ERROR) { 1252 hasErrors = true; 1253 } 1254 err = NO_ERROR; 1255 } 1256 1257 if (interpolators != NULL) { 1258 ResourceDirIterator it(interpolators, String8("interpolator")); 1259 while ((err=it.next()) == NO_ERROR) { 1260 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1261 if (err != NO_ERROR) { 1262 hasErrors = true; 1263 } 1264 } 1265 1266 if (err < NO_ERROR) { 1267 hasErrors = true; 1268 } 1269 err = NO_ERROR; 1270 } 1271 1272 if (transitions != NULL) { 1273 ResourceDirIterator it(transitions, String8("transition")); 1274 while ((err=it.next()) == NO_ERROR) { 1275 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1276 if (err != NO_ERROR) { 1277 hasErrors = true; 1278 } 1279 } 1280 1281 if (err < NO_ERROR) { 1282 hasErrors = true; 1283 } 1284 err = NO_ERROR; 1285 } 1286 1287 if (xmls != NULL) { 1288 ResourceDirIterator it(xmls, String8("xml")); 1289 while ((err=it.next()) == NO_ERROR) { 1290 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1291 if (err != NO_ERROR) { 1292 hasErrors = true; 1293 } 1294 } 1295 1296 if (err < NO_ERROR) { 1297 hasErrors = true; 1298 } 1299 err = NO_ERROR; 1300 } 1301 1302 if (drawables != NULL) { 1303 ResourceDirIterator it(drawables, String8("drawable")); 1304 while ((err=it.next()) == NO_ERROR) { 1305 err = postProcessImage(assets, &table, it.getFile()); 1306 if (err != NO_ERROR) { 1307 hasErrors = true; 1308 } 1309 } 1310 1311 if (err < NO_ERROR) { 1312 hasErrors = true; 1313 } 1314 err = NO_ERROR; 1315 } 1316 1317 if (colors != NULL) { 1318 ResourceDirIterator it(colors, String8("color")); 1319 while ((err=it.next()) == NO_ERROR) { 1320 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1321 if (err != NO_ERROR) { 1322 hasErrors = true; 1323 } 1324 } 1325 1326 if (err < NO_ERROR) { 1327 hasErrors = true; 1328 } 1329 err = NO_ERROR; 1330 } 1331 1332 if (menus != NULL) { 1333 ResourceDirIterator it(menus, String8("menu")); 1334 while ((err=it.next()) == NO_ERROR) { 1335 String8 src = it.getFile()->getPrintableSource(); 1336 err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); 1337 if (err == NO_ERROR) { 1338 ResXMLTree block; 1339 block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); 1340 checkForIds(src, block); 1341 } else { 1342 hasErrors = true; 1343 } 1344 } 1345 1346 if (err < NO_ERROR) { 1347 hasErrors = true; 1348 } 1349 err = NO_ERROR; 1350 } 1351 1352 if (table.validateLocalizations()) { 1353 hasErrors = true; 1354 } 1355 1356 if (hasErrors) { 1357 return UNKNOWN_ERROR; 1358 } 1359 1360 const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0)); 1361 String8 manifestPath(manifestFile->getPrintableSource()); 1362 1363 // Generate final compiled manifest file. 1364 manifestFile->clearData(); 1365 sp<XMLNode> manifestTree = XMLNode::parse(manifestFile); 1366 if (manifestTree == NULL) { 1367 return UNKNOWN_ERROR; 1368 } 1369 err = massageManifest(bundle, manifestTree); 1370 if (err < NO_ERROR) { 1371 return err; 1372 } 1373 err = compileXmlFile(assets, manifestTree, manifestFile, &table); 1374 if (err < NO_ERROR) { 1375 return err; 1376 } 1377 1378 //block.restart(); 1379 //printXMLBlock(&block); 1380 1381 // -------------------------------------------------------------- 1382 // Generate the final resource table. 1383 // Re-flatten because we may have added new resource IDs 1384 // -------------------------------------------------------------- 1385 1386 ResTable finalResTable; 1387 sp<AaptFile> resFile; 1388 1389 if (table.hasResources()) { 1390 sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R")); 1391 err = table.addSymbols(symbols); 1392 if (err < NO_ERROR) { 1393 return err; 1394 } 1395 1396 Vector<sp<ApkSplit> >& splits = builder->getSplits(); 1397 const size_t numSplits = splits.size(); 1398 for (size_t i = 0; i < numSplits; i++) { 1399 sp<ApkSplit>& split = splits.editItemAt(i); 1400 sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"), 1401 AaptGroupEntry(), String8()); 1402 err = table.flatten(bundle, split->getResourceFilter(), flattenedTable); 1403 if (err != NO_ERROR) { 1404 fprintf(stderr, "Failed to generate resource table for split '%s'\n", 1405 split->getPrintableName().string()); 1406 return err; 1407 } 1408 split->addEntry(String8("resources.arsc"), flattenedTable); 1409 1410 if (split->isBase()) { 1411 resFile = flattenedTable; 1412 err = finalResTable.add(flattenedTable->getData(), flattenedTable->getSize()); 1413 if (err != NO_ERROR) { 1414 fprintf(stderr, "Generated resource table is corrupt.\n"); 1415 return err; 1416 } 1417 } else { 1418 sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"), 1419 AaptGroupEntry(), String8()); 1420 err = generateAndroidManifestForSplit(bundle, assets, split, 1421 generatedManifest, &table); 1422 if (err != NO_ERROR) { 1423 fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n", 1424 split->getPrintableName().string()); 1425 return err; 1426 } 1427 split->addEntry(String8("AndroidManifest.xml"), generatedManifest); 1428 } 1429 } 1430 1431 if (bundle->getPublicOutputFile()) { 1432 FILE* fp = fopen(bundle->getPublicOutputFile(), "w+"); 1433 if (fp == NULL) { 1434 fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n", 1435 (const char*)bundle->getPublicOutputFile(), strerror(errno)); 1436 return UNKNOWN_ERROR; 1437 } 1438 if (bundle->getVerbose()) { 1439 printf(" Writing public definitions to %s.\n", bundle->getPublicOutputFile()); 1440 } 1441 table.writePublicDefinitions(String16(assets->getPackage()), fp); 1442 fclose(fp); 1443 } 1444 1445 if (finalResTable.getTableCount() == 0 || resFile == NULL) { 1446 fprintf(stderr, "No resource table was generated.\n"); 1447 return UNKNOWN_ERROR; 1448 } 1449 } 1450 1451 // Perform a basic validation of the manifest file. This time we 1452 // parse it with the comments intact, so that we can use them to 1453 // generate java docs... so we are not going to write this one 1454 // back out to the final manifest data. 1455 sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(), 1456 manifestFile->getGroupEntry(), 1457 manifestFile->getResourceType()); 1458 err = compileXmlFile(assets, manifestFile, 1459 outManifestFile, &table, 1460 XML_COMPILE_ASSIGN_ATTRIBUTE_IDS 1461 | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES); 1462 if (err < NO_ERROR) { 1463 return err; 1464 } 1465 ResXMLTree block; 1466 block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true); 1467 String16 manifest16("manifest"); 1468 String16 permission16("permission"); 1469 String16 permission_group16("permission-group"); 1470 String16 uses_permission16("uses-permission"); 1471 String16 instrumentation16("instrumentation"); 1472 String16 application16("application"); 1473 String16 provider16("provider"); 1474 String16 service16("service"); 1475 String16 receiver16("receiver"); 1476 String16 activity16("activity"); 1477 String16 action16("action"); 1478 String16 category16("category"); 1479 String16 data16("scheme"); 1480 String16 feature_group16("feature-group"); 1481 String16 uses_feature16("uses-feature"); 1482 const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz" 1483 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789"; 1484 const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz" 1485 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-"; 1486 const char* classIdentChars = "abcdefghijklmnopqrstuvwxyz" 1487 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789$"; 1488 const char* processIdentChars = "abcdefghijklmnopqrstuvwxyz" 1489 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:"; 1490 const char* authoritiesIdentChars = "abcdefghijklmnopqrstuvwxyz" 1491 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-:;"; 1492 const char* typeIdentChars = "abcdefghijklmnopqrstuvwxyz" 1493 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:-/*+"; 1494 const char* schemeIdentChars = "abcdefghijklmnopqrstuvwxyz" 1495 "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-"; 1496 ResXMLTree::event_code_t code; 1497 sp<AaptSymbols> permissionSymbols; 1498 sp<AaptSymbols> permissionGroupSymbols; 1499 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 1500 && code > ResXMLTree::BAD_DOCUMENT) { 1501 if (code == ResXMLTree::START_TAG) { 1502 size_t len; 1503 if (block.getElementNamespace(&len) != NULL) { 1504 continue; 1505 } 1506 if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) { 1507 if (validateAttr(manifestPath, finalResTable, block, NULL, "package", 1508 packageIdentChars, true) != ATTR_OKAY) { 1509 hasErrors = true; 1510 } 1511 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1512 "sharedUserId", packageIdentChars, false) != ATTR_OKAY) { 1513 hasErrors = true; 1514 } 1515 } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0 1516 || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) { 1517 const bool isGroup = strcmp16(block.getElementName(&len), 1518 permission_group16.string()) == 0; 1519 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1520 "name", isGroup ? packageIdentCharsWithTheStupid 1521 : packageIdentChars, true) != ATTR_OKAY) { 1522 hasErrors = true; 1523 } 1524 SourcePos srcPos(manifestPath, block.getLineNumber()); 1525 sp<AaptSymbols> syms; 1526 if (!isGroup) { 1527 syms = permissionSymbols; 1528 if (syms == NULL) { 1529 sp<AaptSymbols> symbols = 1530 assets->getSymbolsFor(String8("Manifest")); 1531 syms = permissionSymbols = symbols->addNestedSymbol( 1532 String8("permission"), srcPos); 1533 } 1534 } else { 1535 syms = permissionGroupSymbols; 1536 if (syms == NULL) { 1537 sp<AaptSymbols> symbols = 1538 assets->getSymbolsFor(String8("Manifest")); 1539 syms = permissionGroupSymbols = symbols->addNestedSymbol( 1540 String8("permission_group"), srcPos); 1541 } 1542 } 1543 size_t len; 1544 ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name"); 1545 const uint16_t* id = block.getAttributeStringValue(index, &len); 1546 if (id == NULL) { 1547 fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n", 1548 manifestPath.string(), block.getLineNumber(), 1549 String8(block.getElementName(&len)).string()); 1550 hasErrors = true; 1551 break; 1552 } 1553 String8 idStr(id); 1554 char* p = idStr.lockBuffer(idStr.size()); 1555 char* e = p + idStr.size(); 1556 bool begins_with_digit = true; // init to true so an empty string fails 1557 while (e > p) { 1558 e--; 1559 if (*e >= '0' && *e <= '9') { 1560 begins_with_digit = true; 1561 continue; 1562 } 1563 if ((*e >= 'a' && *e <= 'z') || 1564 (*e >= 'A' && *e <= 'Z') || 1565 (*e == '_')) { 1566 begins_with_digit = false; 1567 continue; 1568 } 1569 if (isGroup && (*e == '-')) { 1570 *e = '_'; 1571 begins_with_digit = false; 1572 continue; 1573 } 1574 e++; 1575 break; 1576 } 1577 idStr.unlockBuffer(); 1578 // verify that we stopped because we hit a period or 1579 // the beginning of the string, and that the 1580 // identifier didn't begin with a digit. 1581 if (begins_with_digit || (e != p && *(e-1) != '.')) { 1582 fprintf(stderr, 1583 "%s:%d: Permission name <%s> is not a valid Java symbol\n", 1584 manifestPath.string(), block.getLineNumber(), idStr.string()); 1585 hasErrors = true; 1586 } 1587 syms->addStringSymbol(String8(e), idStr, srcPos); 1588 const uint16_t* cmt = block.getComment(&len); 1589 if (cmt != NULL && *cmt != 0) { 1590 //printf("Comment of %s: %s\n", String8(e).string(), 1591 // String8(cmt).string()); 1592 syms->appendComment(String8(e), String16(cmt), srcPos); 1593 } else { 1594 //printf("No comment for %s\n", String8(e).string()); 1595 } 1596 syms->makeSymbolPublic(String8(e), srcPos); 1597 } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) { 1598 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1599 "name", packageIdentChars, true) != ATTR_OKAY) { 1600 hasErrors = true; 1601 } 1602 } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) { 1603 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1604 "name", classIdentChars, true) != ATTR_OKAY) { 1605 hasErrors = true; 1606 } 1607 if (validateAttr(manifestPath, finalResTable, block, 1608 RESOURCES_ANDROID_NAMESPACE, "targetPackage", 1609 packageIdentChars, true) != ATTR_OKAY) { 1610 hasErrors = true; 1611 } 1612 } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) { 1613 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1614 "name", classIdentChars, false) != ATTR_OKAY) { 1615 hasErrors = true; 1616 } 1617 if (validateAttr(manifestPath, finalResTable, block, 1618 RESOURCES_ANDROID_NAMESPACE, "permission", 1619 packageIdentChars, false) != ATTR_OKAY) { 1620 hasErrors = true; 1621 } 1622 if (validateAttr(manifestPath, finalResTable, block, 1623 RESOURCES_ANDROID_NAMESPACE, "process", 1624 processIdentChars, false) != ATTR_OKAY) { 1625 hasErrors = true; 1626 } 1627 if (validateAttr(manifestPath, finalResTable, block, 1628 RESOURCES_ANDROID_NAMESPACE, "taskAffinity", 1629 processIdentChars, false) != ATTR_OKAY) { 1630 hasErrors = true; 1631 } 1632 } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) { 1633 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1634 "name", classIdentChars, true) != ATTR_OKAY) { 1635 hasErrors = true; 1636 } 1637 if (validateAttr(manifestPath, finalResTable, block, 1638 RESOURCES_ANDROID_NAMESPACE, "authorities", 1639 authoritiesIdentChars, true) != ATTR_OKAY) { 1640 hasErrors = true; 1641 } 1642 if (validateAttr(manifestPath, finalResTable, block, 1643 RESOURCES_ANDROID_NAMESPACE, "permission", 1644 packageIdentChars, false) != ATTR_OKAY) { 1645 hasErrors = true; 1646 } 1647 if (validateAttr(manifestPath, finalResTable, block, 1648 RESOURCES_ANDROID_NAMESPACE, "process", 1649 processIdentChars, false) != ATTR_OKAY) { 1650 hasErrors = true; 1651 } 1652 } else if (strcmp16(block.getElementName(&len), service16.string()) == 0 1653 || strcmp16(block.getElementName(&len), receiver16.string()) == 0 1654 || strcmp16(block.getElementName(&len), activity16.string()) == 0) { 1655 if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, 1656 "name", classIdentChars, true) != ATTR_OKAY) { 1657 hasErrors = true; 1658 } 1659 if (validateAttr(manifestPath, finalResTable, block, 1660 RESOURCES_ANDROID_NAMESPACE, "permission", 1661 packageIdentChars, false) != ATTR_OKAY) { 1662 hasErrors = true; 1663 } 1664 if (validateAttr(manifestPath, finalResTable, block, 1665 RESOURCES_ANDROID_NAMESPACE, "process", 1666 processIdentChars, false) != ATTR_OKAY) { 1667 hasErrors = true; 1668 } 1669 if (validateAttr(manifestPath, finalResTable, block, 1670 RESOURCES_ANDROID_NAMESPACE, "taskAffinity", 1671 processIdentChars, false) != ATTR_OKAY) { 1672 hasErrors = true; 1673 } 1674 } else if (strcmp16(block.getElementName(&len), action16.string()) == 0 1675 || strcmp16(block.getElementName(&len), category16.string()) == 0) { 1676 if (validateAttr(manifestPath, finalResTable, block, 1677 RESOURCES_ANDROID_NAMESPACE, "name", 1678 packageIdentChars, true) != ATTR_OKAY) { 1679 hasErrors = true; 1680 } 1681 } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) { 1682 if (validateAttr(manifestPath, finalResTable, block, 1683 RESOURCES_ANDROID_NAMESPACE, "mimeType", 1684 typeIdentChars, true) != ATTR_OKAY) { 1685 hasErrors = true; 1686 } 1687 if (validateAttr(manifestPath, finalResTable, block, 1688 RESOURCES_ANDROID_NAMESPACE, "scheme", 1689 schemeIdentChars, true) != ATTR_OKAY) { 1690 hasErrors = true; 1691 } 1692 } else if (strcmp16(block.getElementName(&len), feature_group16.string()) == 0) { 1693 int depth = 1; 1694 while ((code=block.next()) != ResXMLTree::END_DOCUMENT 1695 && code > ResXMLTree::BAD_DOCUMENT) { 1696 if (code == ResXMLTree::START_TAG) { 1697 depth++; 1698 if (strcmp16(block.getElementName(&len), uses_feature16.string()) == 0) { 1699 ssize_t idx = block.indexOfAttribute( 1700 RESOURCES_ANDROID_NAMESPACE, "required"); 1701 if (idx < 0) { 1702 continue; 1703 } 1704 1705 int32_t data = block.getAttributeData(idx); 1706 if (data == 0) { 1707 fprintf(stderr, "%s:%d: Tag <uses-feature> can not have " 1708 "android:required=\"false\" when inside a " 1709 "<feature-group> tag.\n", 1710 manifestPath.string(), block.getLineNumber()); 1711 hasErrors = true; 1712 } 1713 } 1714 } else if (code == ResXMLTree::END_TAG) { 1715 depth--; 1716 if (depth == 0) { 1717 break; 1718 } 1719 } 1720 } 1721 } 1722 } 1723 } 1724 1725 if (hasErrors) { 1726 return UNKNOWN_ERROR; 1727 } 1728 1729 if (resFile != NULL) { 1730 // These resources are now considered to be a part of the included 1731 // resources, for others to reference. 1732 err = assets->addIncludedResources(resFile); 1733 if (err < NO_ERROR) { 1734 fprintf(stderr, "ERROR: Unable to parse generated resources, aborting.\n"); 1735 return err; 1736 } 1737 } 1738 1739 return err; 1740} 1741 1742static const char* getIndentSpace(int indent) 1743{ 1744static const char whitespace[] = 1745" "; 1746 1747 return whitespace + sizeof(whitespace) - 1 - indent*4; 1748} 1749 1750static String8 flattenSymbol(const String8& symbol) { 1751 String8 result(symbol); 1752 ssize_t first; 1753 if ((first = symbol.find(":", 0)) >= 0 1754 || (first = symbol.find(".", 0)) >= 0) { 1755 size_t size = symbol.size(); 1756 char* buf = result.lockBuffer(size); 1757 for (size_t i = first; i < size; i++) { 1758 if (buf[i] == ':' || buf[i] == '.') { 1759 buf[i] = '_'; 1760 } 1761 } 1762 result.unlockBuffer(size); 1763 } 1764 return result; 1765} 1766 1767static String8 getSymbolPackage(const String8& symbol, const sp<AaptAssets>& assets, bool pub) { 1768 ssize_t colon = symbol.find(":", 0); 1769 if (colon >= 0) { 1770 return String8(symbol.string(), colon); 1771 } 1772 return pub ? assets->getPackage() : assets->getSymbolsPrivatePackage(); 1773} 1774 1775static String8 getSymbolName(const String8& symbol) { 1776 ssize_t colon = symbol.find(":", 0); 1777 if (colon >= 0) { 1778 return String8(symbol.string() + colon + 1); 1779 } 1780 return symbol; 1781} 1782 1783static String16 getAttributeComment(const sp<AaptAssets>& assets, 1784 const String8& name, 1785 String16* outTypeComment = NULL) 1786{ 1787 sp<AaptSymbols> asym = assets->getSymbolsFor(String8("R")); 1788 if (asym != NULL) { 1789 //printf("Got R symbols!\n"); 1790 asym = asym->getNestedSymbols().valueFor(String8("attr")); 1791 if (asym != NULL) { 1792 //printf("Got attrs symbols! comment %s=%s\n", 1793 // name.string(), String8(asym->getComment(name)).string()); 1794 if (outTypeComment != NULL) { 1795 *outTypeComment = asym->getTypeComment(name); 1796 } 1797 return asym->getComment(name); 1798 } 1799 } 1800 return String16(); 1801} 1802 1803static status_t writeLayoutClasses( 1804 FILE* fp, const sp<AaptAssets>& assets, 1805 const sp<AaptSymbols>& symbols, int indent, bool includePrivate, bool nonConstantId) 1806{ 1807 const char* indentStr = getIndentSpace(indent); 1808 if (!includePrivate) { 1809 fprintf(fp, "%s/** @doconly */\n", indentStr); 1810 } 1811 fprintf(fp, "%spublic static final class styleable {\n", indentStr); 1812 indent++; 1813 1814 String16 attr16("attr"); 1815 String16 package16(assets->getPackage()); 1816 1817 indentStr = getIndentSpace(indent); 1818 bool hasErrors = false; 1819 1820 size_t i; 1821 size_t N = symbols->getNestedSymbols().size(); 1822 for (i=0; i<N; i++) { 1823 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 1824 String8 realClassName(symbols->getNestedSymbols().keyAt(i)); 1825 String8 nclassName(flattenSymbol(realClassName)); 1826 1827 SortedVector<uint32_t> idents; 1828 Vector<uint32_t> origOrder; 1829 Vector<bool> publicFlags; 1830 1831 size_t a; 1832 size_t NA = nsymbols->getSymbols().size(); 1833 for (a=0; a<NA; a++) { 1834 const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a)); 1835 int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32 1836 ? sym.int32Val : 0; 1837 bool isPublic = true; 1838 if (code == 0) { 1839 String16 name16(sym.name); 1840 uint32_t typeSpecFlags; 1841 code = assets->getIncludedResources().identifierForName( 1842 name16.string(), name16.size(), 1843 attr16.string(), attr16.size(), 1844 package16.string(), package16.size(), &typeSpecFlags); 1845 if (code == 0) { 1846 fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n", 1847 nclassName.string(), sym.name.string()); 1848 hasErrors = true; 1849 } 1850 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 1851 } 1852 idents.add(code); 1853 origOrder.add(code); 1854 publicFlags.add(isPublic); 1855 } 1856 1857 NA = idents.size(); 1858 1859 String16 comment = symbols->getComment(realClassName); 1860 AnnotationProcessor ann; 1861 fprintf(fp, "%s/** ", indentStr); 1862 if (comment.size() > 0) { 1863 String8 cmt(comment); 1864 ann.preprocessComment(cmt); 1865 fprintf(fp, "%s\n", cmt.string()); 1866 } else { 1867 fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string()); 1868 } 1869 bool hasTable = false; 1870 for (a=0; a<NA; a++) { 1871 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 1872 if (pos >= 0) { 1873 if (!hasTable) { 1874 hasTable = true; 1875 fprintf(fp, 1876 "%s <p>Includes the following attributes:</p>\n" 1877 "%s <table>\n" 1878 "%s <colgroup align=\"left\" />\n" 1879 "%s <colgroup align=\"left\" />\n" 1880 "%s <tr><th>Attribute</th><th>Description</th></tr>\n", 1881 indentStr, 1882 indentStr, 1883 indentStr, 1884 indentStr, 1885 indentStr); 1886 } 1887 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 1888 if (!publicFlags.itemAt(a) && !includePrivate) { 1889 continue; 1890 } 1891 String8 name8(sym.name); 1892 String16 comment(sym.comment); 1893 if (comment.size() <= 0) { 1894 comment = getAttributeComment(assets, name8); 1895 } 1896 if (comment.size() > 0) { 1897 const char16_t* p = comment.string(); 1898 while (*p != 0 && *p != '.') { 1899 if (*p == '{') { 1900 while (*p != 0 && *p != '}') { 1901 p++; 1902 } 1903 } else { 1904 p++; 1905 } 1906 } 1907 if (*p == '.') { 1908 p++; 1909 } 1910 comment = String16(comment.string(), p-comment.string()); 1911 } 1912 fprintf(fp, "%s <tr><td><code>{@link #%s_%s %s:%s}</code></td><td>%s</td></tr>\n", 1913 indentStr, nclassName.string(), 1914 flattenSymbol(name8).string(), 1915 getSymbolPackage(name8, assets, true).string(), 1916 getSymbolName(name8).string(), 1917 String8(comment).string()); 1918 } 1919 } 1920 if (hasTable) { 1921 fprintf(fp, "%s </table>\n", indentStr); 1922 } 1923 for (a=0; a<NA; a++) { 1924 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 1925 if (pos >= 0) { 1926 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 1927 if (!publicFlags.itemAt(a) && !includePrivate) { 1928 continue; 1929 } 1930 fprintf(fp, "%s @see #%s_%s\n", 1931 indentStr, nclassName.string(), 1932 flattenSymbol(sym.name).string()); 1933 } 1934 } 1935 fprintf(fp, "%s */\n", getIndentSpace(indent)); 1936 1937 ann.printAnnotations(fp, indentStr); 1938 1939 fprintf(fp, 1940 "%spublic static final int[] %s = {\n" 1941 "%s", 1942 indentStr, nclassName.string(), 1943 getIndentSpace(indent+1)); 1944 1945 for (a=0; a<NA; a++) { 1946 if (a != 0) { 1947 if ((a&3) == 0) { 1948 fprintf(fp, ",\n%s", getIndentSpace(indent+1)); 1949 } else { 1950 fprintf(fp, ", "); 1951 } 1952 } 1953 fprintf(fp, "0x%08x", idents[a]); 1954 } 1955 1956 fprintf(fp, "\n%s};\n", indentStr); 1957 1958 for (a=0; a<NA; a++) { 1959 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 1960 if (pos >= 0) { 1961 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 1962 if (!publicFlags.itemAt(a) && !includePrivate) { 1963 continue; 1964 } 1965 String8 name8(sym.name); 1966 String16 comment(sym.comment); 1967 String16 typeComment; 1968 if (comment.size() <= 0) { 1969 comment = getAttributeComment(assets, name8, &typeComment); 1970 } else { 1971 getAttributeComment(assets, name8, &typeComment); 1972 } 1973 1974 uint32_t typeSpecFlags = 0; 1975 String16 name16(sym.name); 1976 assets->getIncludedResources().identifierForName( 1977 name16.string(), name16.size(), 1978 attr16.string(), attr16.size(), 1979 package16.string(), package16.size(), &typeSpecFlags); 1980 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(), 1981 // String8(attr16).string(), String8(name16).string(), typeSpecFlags); 1982 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 1983 1984 AnnotationProcessor ann; 1985 fprintf(fp, "%s/**\n", indentStr); 1986 if (comment.size() > 0) { 1987 String8 cmt(comment); 1988 ann.preprocessComment(cmt); 1989 fprintf(fp, "%s <p>\n%s @attr description\n", indentStr, indentStr); 1990 fprintf(fp, "%s %s\n", indentStr, cmt.string()); 1991 } else { 1992 fprintf(fp, 1993 "%s <p>This symbol is the offset where the {@link %s.R.attr#%s}\n" 1994 "%s attribute's value can be found in the {@link #%s} array.\n", 1995 indentStr, 1996 getSymbolPackage(name8, assets, pub).string(), 1997 getSymbolName(name8).string(), 1998 indentStr, nclassName.string()); 1999 } 2000 if (typeComment.size() > 0) { 2001 String8 cmt(typeComment); 2002 ann.preprocessComment(cmt); 2003 fprintf(fp, "\n\n%s %s\n", indentStr, cmt.string()); 2004 } 2005 if (comment.size() > 0) { 2006 if (pub) { 2007 fprintf(fp, 2008 "%s <p>This corresponds to the global attribute\n" 2009 "%s resource symbol {@link %s.R.attr#%s}.\n", 2010 indentStr, indentStr, 2011 getSymbolPackage(name8, assets, true).string(), 2012 getSymbolName(name8).string()); 2013 } else { 2014 fprintf(fp, 2015 "%s <p>This is a private symbol.\n", indentStr); 2016 } 2017 } 2018 fprintf(fp, "%s @attr name %s:%s\n", indentStr, 2019 getSymbolPackage(name8, assets, pub).string(), 2020 getSymbolName(name8).string()); 2021 fprintf(fp, "%s*/\n", indentStr); 2022 ann.printAnnotations(fp, indentStr); 2023 2024 const char * id_format = nonConstantId ? 2025 "%spublic static int %s_%s = %d;\n" : 2026 "%spublic static final int %s_%s = %d;\n"; 2027 2028 fprintf(fp, 2029 id_format, 2030 indentStr, nclassName.string(), 2031 flattenSymbol(name8).string(), (int)pos); 2032 } 2033 } 2034 } 2035 2036 indent--; 2037 fprintf(fp, "%s};\n", getIndentSpace(indent)); 2038 return hasErrors ? UNKNOWN_ERROR : NO_ERROR; 2039} 2040 2041static status_t writeTextLayoutClasses( 2042 FILE* fp, const sp<AaptAssets>& assets, 2043 const sp<AaptSymbols>& symbols, bool includePrivate) 2044{ 2045 String16 attr16("attr"); 2046 String16 package16(assets->getPackage()); 2047 2048 bool hasErrors = false; 2049 2050 size_t i; 2051 size_t N = symbols->getNestedSymbols().size(); 2052 for (i=0; i<N; i++) { 2053 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2054 String8 realClassName(symbols->getNestedSymbols().keyAt(i)); 2055 String8 nclassName(flattenSymbol(realClassName)); 2056 2057 SortedVector<uint32_t> idents; 2058 Vector<uint32_t> origOrder; 2059 Vector<bool> publicFlags; 2060 2061 size_t a; 2062 size_t NA = nsymbols->getSymbols().size(); 2063 for (a=0; a<NA; a++) { 2064 const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a)); 2065 int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32 2066 ? sym.int32Val : 0; 2067 bool isPublic = true; 2068 if (code == 0) { 2069 String16 name16(sym.name); 2070 uint32_t typeSpecFlags; 2071 code = assets->getIncludedResources().identifierForName( 2072 name16.string(), name16.size(), 2073 attr16.string(), attr16.size(), 2074 package16.string(), package16.size(), &typeSpecFlags); 2075 if (code == 0) { 2076 fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n", 2077 nclassName.string(), sym.name.string()); 2078 hasErrors = true; 2079 } 2080 isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2081 } 2082 idents.add(code); 2083 origOrder.add(code); 2084 publicFlags.add(isPublic); 2085 } 2086 2087 NA = idents.size(); 2088 2089 fprintf(fp, "int[] styleable %s {", nclassName.string()); 2090 2091 for (a=0; a<NA; a++) { 2092 if (a != 0) { 2093 fprintf(fp, ","); 2094 } 2095 fprintf(fp, " 0x%08x", idents[a]); 2096 } 2097 2098 fprintf(fp, " }\n"); 2099 2100 for (a=0; a<NA; a++) { 2101 ssize_t pos = idents.indexOf(origOrder.itemAt(a)); 2102 if (pos >= 0) { 2103 const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a); 2104 if (!publicFlags.itemAt(a) && !includePrivate) { 2105 continue; 2106 } 2107 String8 name8(sym.name); 2108 String16 comment(sym.comment); 2109 String16 typeComment; 2110 if (comment.size() <= 0) { 2111 comment = getAttributeComment(assets, name8, &typeComment); 2112 } else { 2113 getAttributeComment(assets, name8, &typeComment); 2114 } 2115 2116 uint32_t typeSpecFlags = 0; 2117 String16 name16(sym.name); 2118 assets->getIncludedResources().identifierForName( 2119 name16.string(), name16.size(), 2120 attr16.string(), attr16.size(), 2121 package16.string(), package16.size(), &typeSpecFlags); 2122 //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(), 2123 // String8(attr16).string(), String8(name16).string(), typeSpecFlags); 2124 const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; 2125 2126 fprintf(fp, 2127 "int styleable %s_%s %d\n", 2128 nclassName.string(), 2129 flattenSymbol(name8).string(), (int)pos); 2130 } 2131 } 2132 } 2133 2134 return hasErrors ? UNKNOWN_ERROR : NO_ERROR; 2135} 2136 2137static status_t writeSymbolClass( 2138 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate, 2139 const sp<AaptSymbols>& symbols, const String8& className, int indent, 2140 bool nonConstantId) 2141{ 2142 fprintf(fp, "%spublic %sfinal class %s {\n", 2143 getIndentSpace(indent), 2144 indent != 0 ? "static " : "", className.string()); 2145 indent++; 2146 2147 size_t i; 2148 status_t err = NO_ERROR; 2149 2150 const char * id_format = nonConstantId ? 2151 "%spublic static int %s=0x%08x;\n" : 2152 "%spublic static final int %s=0x%08x;\n"; 2153 2154 size_t N = symbols->getSymbols().size(); 2155 for (i=0; i<N; i++) { 2156 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2157 if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) { 2158 continue; 2159 } 2160 if (!assets->isJavaSymbol(sym, includePrivate)) { 2161 continue; 2162 } 2163 String8 name8(sym.name); 2164 String16 comment(sym.comment); 2165 bool haveComment = false; 2166 AnnotationProcessor ann; 2167 if (comment.size() > 0) { 2168 haveComment = true; 2169 String8 cmt(comment); 2170 ann.preprocessComment(cmt); 2171 fprintf(fp, 2172 "%s/** %s\n", 2173 getIndentSpace(indent), cmt.string()); 2174 } else if (sym.isPublic && !includePrivate) { 2175 sym.sourcePos.warning("No comment for public symbol %s:%s/%s", 2176 assets->getPackage().string(), className.string(), 2177 String8(sym.name).string()); 2178 } 2179 String16 typeComment(sym.typeComment); 2180 if (typeComment.size() > 0) { 2181 String8 cmt(typeComment); 2182 ann.preprocessComment(cmt); 2183 if (!haveComment) { 2184 haveComment = true; 2185 fprintf(fp, 2186 "%s/** %s\n", getIndentSpace(indent), cmt.string()); 2187 } else { 2188 fprintf(fp, 2189 "%s %s\n", getIndentSpace(indent), cmt.string()); 2190 } 2191 } 2192 if (haveComment) { 2193 fprintf(fp,"%s */\n", getIndentSpace(indent)); 2194 } 2195 ann.printAnnotations(fp, getIndentSpace(indent)); 2196 fprintf(fp, id_format, 2197 getIndentSpace(indent), 2198 flattenSymbol(name8).string(), (int)sym.int32Val); 2199 } 2200 2201 for (i=0; i<N; i++) { 2202 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2203 if (sym.typeCode != AaptSymbolEntry::TYPE_STRING) { 2204 continue; 2205 } 2206 if (!assets->isJavaSymbol(sym, includePrivate)) { 2207 continue; 2208 } 2209 String8 name8(sym.name); 2210 String16 comment(sym.comment); 2211 AnnotationProcessor ann; 2212 if (comment.size() > 0) { 2213 String8 cmt(comment); 2214 ann.preprocessComment(cmt); 2215 fprintf(fp, 2216 "%s/** %s\n" 2217 "%s */\n", 2218 getIndentSpace(indent), cmt.string(), 2219 getIndentSpace(indent)); 2220 } else if (sym.isPublic && !includePrivate) { 2221 sym.sourcePos.warning("No comment for public symbol %s:%s/%s", 2222 assets->getPackage().string(), className.string(), 2223 String8(sym.name).string()); 2224 } 2225 ann.printAnnotations(fp, getIndentSpace(indent)); 2226 fprintf(fp, "%spublic static final String %s=\"%s\";\n", 2227 getIndentSpace(indent), 2228 flattenSymbol(name8).string(), sym.stringVal.string()); 2229 } 2230 2231 sp<AaptSymbols> styleableSymbols; 2232 2233 N = symbols->getNestedSymbols().size(); 2234 for (i=0; i<N; i++) { 2235 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2236 String8 nclassName(symbols->getNestedSymbols().keyAt(i)); 2237 if (nclassName == "styleable") { 2238 styleableSymbols = nsymbols; 2239 } else { 2240 err = writeSymbolClass(fp, assets, includePrivate, nsymbols, nclassName, indent, nonConstantId); 2241 } 2242 if (err != NO_ERROR) { 2243 return err; 2244 } 2245 } 2246 2247 if (styleableSymbols != NULL) { 2248 err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate, nonConstantId); 2249 if (err != NO_ERROR) { 2250 return err; 2251 } 2252 } 2253 2254 indent--; 2255 fprintf(fp, "%s}\n", getIndentSpace(indent)); 2256 return NO_ERROR; 2257} 2258 2259static status_t writeTextSymbolClass( 2260 FILE* fp, const sp<AaptAssets>& assets, bool includePrivate, 2261 const sp<AaptSymbols>& symbols, const String8& className) 2262{ 2263 size_t i; 2264 status_t err = NO_ERROR; 2265 2266 size_t N = symbols->getSymbols().size(); 2267 for (i=0; i<N; i++) { 2268 const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i); 2269 if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) { 2270 continue; 2271 } 2272 2273 if (!assets->isJavaSymbol(sym, includePrivate)) { 2274 continue; 2275 } 2276 2277 String8 name8(sym.name); 2278 fprintf(fp, "int %s %s 0x%08x\n", 2279 className.string(), 2280 flattenSymbol(name8).string(), (int)sym.int32Val); 2281 } 2282 2283 N = symbols->getNestedSymbols().size(); 2284 for (i=0; i<N; i++) { 2285 sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i); 2286 String8 nclassName(symbols->getNestedSymbols().keyAt(i)); 2287 if (nclassName == "styleable") { 2288 err = writeTextLayoutClasses(fp, assets, nsymbols, includePrivate); 2289 } else { 2290 err = writeTextSymbolClass(fp, assets, includePrivate, nsymbols, nclassName); 2291 } 2292 if (err != NO_ERROR) { 2293 return err; 2294 } 2295 } 2296 2297 return NO_ERROR; 2298} 2299 2300status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, 2301 const String8& package, bool includePrivate) 2302{ 2303 if (!bundle->getRClassDir()) { 2304 return NO_ERROR; 2305 } 2306 2307 const char* textSymbolsDest = bundle->getOutputTextSymbols(); 2308 2309 String8 R("R"); 2310 const size_t N = assets->getSymbols().size(); 2311 for (size_t i=0; i<N; i++) { 2312 sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i); 2313 String8 className(assets->getSymbols().keyAt(i)); 2314 String8 dest(bundle->getRClassDir()); 2315 2316 if (bundle->getMakePackageDirs()) { 2317 String8 pkg(package); 2318 const char* last = pkg.string(); 2319 const char* s = last-1; 2320 do { 2321 s++; 2322 if (s > last && (*s == '.' || *s == 0)) { 2323 String8 part(last, s-last); 2324 dest.appendPath(part); 2325#ifdef HAVE_MS_C_RUNTIME 2326 _mkdir(dest.string()); 2327#else 2328 mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP); 2329#endif 2330 last = s+1; 2331 } 2332 } while (*s); 2333 } 2334 dest.appendPath(className); 2335 dest.append(".java"); 2336 FILE* fp = fopen(dest.string(), "w+"); 2337 if (fp == NULL) { 2338 fprintf(stderr, "ERROR: Unable to open class file %s: %s\n", 2339 dest.string(), strerror(errno)); 2340 return UNKNOWN_ERROR; 2341 } 2342 if (bundle->getVerbose()) { 2343 printf(" Writing symbols for class %s.\n", className.string()); 2344 } 2345 2346 fprintf(fp, 2347 "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n" 2348 " *\n" 2349 " * This class was automatically generated by the\n" 2350 " * aapt tool from the resource data it found. It\n" 2351 " * should not be modified by hand.\n" 2352 " */\n" 2353 "\n" 2354 "package %s;\n\n", package.string()); 2355 2356 status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, 2357 className, 0, bundle->getNonConstantId()); 2358 fclose(fp); 2359 if (err != NO_ERROR) { 2360 return err; 2361 } 2362 2363 if (textSymbolsDest != NULL && R == className) { 2364 String8 textDest(textSymbolsDest); 2365 textDest.appendPath(className); 2366 textDest.append(".txt"); 2367 2368 FILE* fp = fopen(textDest.string(), "w+"); 2369 if (fp == NULL) { 2370 fprintf(stderr, "ERROR: Unable to open text symbol file %s: %s\n", 2371 textDest.string(), strerror(errno)); 2372 return UNKNOWN_ERROR; 2373 } 2374 if (bundle->getVerbose()) { 2375 printf(" Writing text symbols for class %s.\n", className.string()); 2376 } 2377 2378 status_t err = writeTextSymbolClass(fp, assets, includePrivate, symbols, 2379 className); 2380 fclose(fp); 2381 if (err != NO_ERROR) { 2382 return err; 2383 } 2384 } 2385 2386 // If we were asked to generate a dependency file, we'll go ahead and add this R.java 2387 // as a target in the dependency file right next to it. 2388 if (bundle->getGenDependencies() && R == className) { 2389 // Add this R.java to the dependency file 2390 String8 dependencyFile(bundle->getRClassDir()); 2391 dependencyFile.appendPath("R.java.d"); 2392 2393 FILE *fp = fopen(dependencyFile.string(), "a"); 2394 fprintf(fp,"%s \\\n", dest.string()); 2395 fclose(fp); 2396 } 2397 } 2398 2399 return NO_ERROR; 2400} 2401 2402 2403class ProguardKeepSet 2404{ 2405public: 2406 // { rule --> { file locations } } 2407 KeyedVector<String8, SortedVector<String8> > rules; 2408 2409 void add(const String8& rule, const String8& where); 2410}; 2411 2412void ProguardKeepSet::add(const String8& rule, const String8& where) 2413{ 2414 ssize_t index = rules.indexOfKey(rule); 2415 if (index < 0) { 2416 index = rules.add(rule, SortedVector<String8>()); 2417 } 2418 rules.editValueAt(index).add(where); 2419} 2420 2421void 2422addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName, 2423 const char* pkg, const String8& srcName, int line) 2424{ 2425 String8 className(inClassName); 2426 if (pkg != NULL) { 2427 // asdf --> package.asdf 2428 // .asdf .a.b --> package.asdf package.a.b 2429 // asdf.adsf --> asdf.asdf 2430 const char* p = className.string(); 2431 const char* q = strchr(p, '.'); 2432 if (p == q) { 2433 className = pkg; 2434 className.append(inClassName); 2435 } else if (q == NULL) { 2436 className = pkg; 2437 className.append("."); 2438 className.append(inClassName); 2439 } 2440 } 2441 2442 String8 rule("-keep class "); 2443 rule += className; 2444 rule += " { <init>(...); }"; 2445 2446 String8 location("view "); 2447 location += srcName; 2448 char lineno[20]; 2449 sprintf(lineno, ":%d", line); 2450 location += lineno; 2451 2452 keep->add(rule, location); 2453} 2454 2455void 2456addProguardKeepMethodRule(ProguardKeepSet* keep, const String8& memberName, 2457 const char* pkg, const String8& srcName, int line) 2458{ 2459 String8 rule("-keepclassmembers class * { *** "); 2460 rule += memberName; 2461 rule += "(...); }"; 2462 2463 String8 location("onClick "); 2464 location += srcName; 2465 char lineno[20]; 2466 sprintf(lineno, ":%d", line); 2467 location += lineno; 2468 2469 keep->add(rule, location); 2470} 2471 2472status_t 2473writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets) 2474{ 2475 status_t err; 2476 ResXMLTree tree; 2477 size_t len; 2478 ResXMLTree::event_code_t code; 2479 int depth = 0; 2480 bool inApplication = false; 2481 String8 error; 2482 sp<AaptGroup> assGroup; 2483 sp<AaptFile> assFile; 2484 String8 pkg; 2485 2486 // First, look for a package file to parse. This is required to 2487 // be able to generate the resource information. 2488 assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml")); 2489 if (assGroup == NULL) { 2490 fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n"); 2491 return -1; 2492 } 2493 2494 if (assGroup->getFiles().size() != 1) { 2495 fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n", 2496 assGroup->getFiles().valueAt(0)->getPrintableSource().string()); 2497 } 2498 2499 assFile = assGroup->getFiles().valueAt(0); 2500 2501 err = parseXMLResource(assFile, &tree); 2502 if (err != NO_ERROR) { 2503 return err; 2504 } 2505 2506 tree.restart(); 2507 2508 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 2509 if (code == ResXMLTree::END_TAG) { 2510 if (/* name == "Application" && */ depth == 2) { 2511 inApplication = false; 2512 } 2513 depth--; 2514 continue; 2515 } 2516 if (code != ResXMLTree::START_TAG) { 2517 continue; 2518 } 2519 depth++; 2520 String8 tag(tree.getElementName(&len)); 2521 // printf("Depth %d tag %s\n", depth, tag.string()); 2522 bool keepTag = false; 2523 if (depth == 1) { 2524 if (tag != "manifest") { 2525 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); 2526 return -1; 2527 } 2528 pkg = getAttribute(tree, NULL, "package", NULL); 2529 } else if (depth == 2) { 2530 if (tag == "application") { 2531 inApplication = true; 2532 keepTag = true; 2533 2534 String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android", 2535 "backupAgent", &error); 2536 if (agent.length() > 0) { 2537 addProguardKeepRule(keep, agent, pkg.string(), 2538 assFile->getPrintableSource(), tree.getLineNumber()); 2539 } 2540 } else if (tag == "instrumentation") { 2541 keepTag = true; 2542 } 2543 } 2544 if (!keepTag && inApplication && depth == 3) { 2545 if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") { 2546 keepTag = true; 2547 } 2548 } 2549 if (keepTag) { 2550 String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android", 2551 "name", &error); 2552 if (error != "") { 2553 fprintf(stderr, "ERROR: %s\n", error.string()); 2554 return -1; 2555 } 2556 if (name.length() > 0) { 2557 addProguardKeepRule(keep, name, pkg.string(), 2558 assFile->getPrintableSource(), tree.getLineNumber()); 2559 } 2560 } 2561 } 2562 2563 return NO_ERROR; 2564} 2565 2566struct NamespaceAttributePair { 2567 const char* ns; 2568 const char* attr; 2569 2570 NamespaceAttributePair(const char* n, const char* a) : ns(n), attr(a) {} 2571 NamespaceAttributePair() : ns(NULL), attr(NULL) {} 2572}; 2573 2574status_t 2575writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile, 2576 const Vector<String8>& startTags, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs) 2577{ 2578 status_t err; 2579 ResXMLTree tree; 2580 size_t len; 2581 ResXMLTree::event_code_t code; 2582 2583 err = parseXMLResource(layoutFile, &tree); 2584 if (err != NO_ERROR) { 2585 return err; 2586 } 2587 2588 tree.restart(); 2589 2590 if (!startTags.isEmpty()) { 2591 bool haveStart = false; 2592 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 2593 if (code != ResXMLTree::START_TAG) { 2594 continue; 2595 } 2596 String8 tag(tree.getElementName(&len)); 2597 const size_t numStartTags = startTags.size(); 2598 for (size_t i = 0; i < numStartTags; i++) { 2599 if (tag == startTags[i]) { 2600 haveStart = true; 2601 } 2602 } 2603 break; 2604 } 2605 if (!haveStart) { 2606 return NO_ERROR; 2607 } 2608 } 2609 2610 while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { 2611 if (code != ResXMLTree::START_TAG) { 2612 continue; 2613 } 2614 String8 tag(tree.getElementName(&len)); 2615 2616 // If there is no '.', we'll assume that it's one of the built in names. 2617 if (strchr(tag.string(), '.')) { 2618 addProguardKeepRule(keep, tag, NULL, 2619 layoutFile->getPrintableSource(), tree.getLineNumber()); 2620 } else if (tagAttrPairs != NULL) { 2621 ssize_t tagIndex = tagAttrPairs->indexOfKey(tag); 2622 if (tagIndex >= 0) { 2623 const Vector<NamespaceAttributePair>& nsAttrVector = tagAttrPairs->valueAt(tagIndex); 2624 for (size_t i = 0; i < nsAttrVector.size(); i++) { 2625 const NamespaceAttributePair& nsAttr = nsAttrVector[i]; 2626 2627 ssize_t attrIndex = tree.indexOfAttribute(nsAttr.ns, nsAttr.attr); 2628 if (attrIndex < 0) { 2629 // fprintf(stderr, "%s:%d: <%s> does not have attribute %s:%s.\n", 2630 // layoutFile->getPrintableSource().string(), tree.getLineNumber(), 2631 // tag.string(), nsAttr.ns, nsAttr.attr); 2632 } else { 2633 size_t len; 2634 addProguardKeepRule(keep, 2635 String8(tree.getAttributeStringValue(attrIndex, &len)), NULL, 2636 layoutFile->getPrintableSource(), tree.getLineNumber()); 2637 } 2638 } 2639 } 2640 } 2641 ssize_t attrIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "onClick"); 2642 if (attrIndex >= 0) { 2643 size_t len; 2644 addProguardKeepMethodRule(keep, 2645 String8(tree.getAttributeStringValue(attrIndex, &len)), NULL, 2646 layoutFile->getPrintableSource(), tree.getLineNumber()); 2647 } 2648 } 2649 2650 return NO_ERROR; 2651} 2652 2653static void addTagAttrPair(KeyedVector<String8, Vector<NamespaceAttributePair> >* dest, 2654 const char* tag, const char* ns, const char* attr) { 2655 String8 tagStr(tag); 2656 ssize_t index = dest->indexOfKey(tagStr); 2657 2658 if (index < 0) { 2659 Vector<NamespaceAttributePair> vector; 2660 vector.add(NamespaceAttributePair(ns, attr)); 2661 dest->add(tagStr, vector); 2662 } else { 2663 dest->editValueAt(index).add(NamespaceAttributePair(ns, attr)); 2664 } 2665} 2666 2667status_t 2668writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets) 2669{ 2670 status_t err; 2671 2672 // tag:attribute pairs that should be checked in layout files. 2673 KeyedVector<String8, Vector<NamespaceAttributePair> > kLayoutTagAttrPairs; 2674 addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class"); 2675 addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class"); 2676 addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name"); 2677 2678 // tag:attribute pairs that should be checked in xml files. 2679 KeyedVector<String8, Vector<NamespaceAttributePair> > kXmlTagAttrPairs; 2680 addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment"); 2681 addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment"); 2682 2683 const Vector<sp<AaptDir> >& dirs = assets->resDirs(); 2684 const size_t K = dirs.size(); 2685 for (size_t k=0; k<K; k++) { 2686 const sp<AaptDir>& d = dirs.itemAt(k); 2687 const String8& dirName = d->getLeaf(); 2688 Vector<String8> startTags; 2689 const char* startTag = NULL; 2690 const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL; 2691 if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) { 2692 tagAttrPairs = &kLayoutTagAttrPairs; 2693 } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) { 2694 startTags.add(String8("PreferenceScreen")); 2695 startTags.add(String8("preference-headers")); 2696 tagAttrPairs = &kXmlTagAttrPairs; 2697 } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) { 2698 startTags.add(String8("menu")); 2699 tagAttrPairs = NULL; 2700 } else { 2701 continue; 2702 } 2703 2704 const KeyedVector<String8,sp<AaptGroup> > groups = d->getFiles(); 2705 const size_t N = groups.size(); 2706 for (size_t i=0; i<N; i++) { 2707 const sp<AaptGroup>& group = groups.valueAt(i); 2708 const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles(); 2709 const size_t M = files.size(); 2710 for (size_t j=0; j<M; j++) { 2711 err = writeProguardForXml(keep, files.valueAt(j), startTags, tagAttrPairs); 2712 if (err < 0) { 2713 return err; 2714 } 2715 } 2716 } 2717 } 2718 // Handle the overlays 2719 sp<AaptAssets> overlay = assets->getOverlay(); 2720 if (overlay.get()) { 2721 return writeProguardForLayouts(keep, overlay); 2722 } 2723 2724 return NO_ERROR; 2725} 2726 2727status_t 2728writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets) 2729{ 2730 status_t err = -1; 2731 2732 if (!bundle->getProguardFile()) { 2733 return NO_ERROR; 2734 } 2735 2736 ProguardKeepSet keep; 2737 2738 err = writeProguardForAndroidManifest(&keep, assets); 2739 if (err < 0) { 2740 return err; 2741 } 2742 2743 err = writeProguardForLayouts(&keep, assets); 2744 if (err < 0) { 2745 return err; 2746 } 2747 2748 FILE* fp = fopen(bundle->getProguardFile(), "w+"); 2749 if (fp == NULL) { 2750 fprintf(stderr, "ERROR: Unable to open class file %s: %s\n", 2751 bundle->getProguardFile(), strerror(errno)); 2752 return UNKNOWN_ERROR; 2753 } 2754 2755 const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules; 2756 const size_t N = rules.size(); 2757 for (size_t i=0; i<N; i++) { 2758 const SortedVector<String8>& locations = rules.valueAt(i); 2759 const size_t M = locations.size(); 2760 for (size_t j=0; j<M; j++) { 2761 fprintf(fp, "# %s\n", locations.itemAt(j).string()); 2762 } 2763 fprintf(fp, "%s\n\n", rules.keyAt(i).string()); 2764 } 2765 fclose(fp); 2766 2767 return err; 2768} 2769 2770// Loops through the string paths and writes them to the file pointer 2771// Each file path is written on its own line with a terminating backslash. 2772status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp) 2773{ 2774 status_t deps = -1; 2775 for (size_t file_i = 0; file_i < files->size(); ++file_i) { 2776 // Add the full file path to the dependency file 2777 fprintf(fp, "%s \\\n", files->itemAt(file_i).string()); 2778 deps++; 2779 } 2780 return deps; 2781} 2782 2783status_t 2784writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw) 2785{ 2786 status_t deps = -1; 2787 deps += writePathsToFile(assets->getFullResPaths(), fp); 2788 if (includeRaw) { 2789 deps += writePathsToFile(assets->getFullAssetPaths(), fp); 2790 } 2791 return deps; 2792} 2793