static jint norwegianime_BinaryDictionary_open
        (JNIEnv *env, jobject object, jobject assetManager, jstring resourceString,
         jint typedLetterMultiplier, jint fullWordMultiplier)
{
    // Get the native file descriptor from the FileDescriptor object
    AssetManager *am = (AssetManager*) env->GetIntField(assetManager, sAssetManagerNativeField);
    if (!am) {
        LOGE("DICT: Couldn't get AssetManager native peer\n");
        return 0;
    }
    const char *resourcePath = env->GetStringUTFChars(resourceString, NULL);

    Asset *dictAsset = am->openNonAsset(resourcePath, Asset::ACCESS_BUFFER);
    if (dictAsset == NULL) {
        LOGE("DICT: Couldn't get asset %s\n", resourcePath); 
        env->ReleaseStringUTFChars(resourceString, resourcePath);
        return 0;
    }

    void *dict = (void*) dictAsset->getBuffer(false);
    if (dict == NULL) {
        LOGE("DICT: Dictionary buffer is null\n");
        env->ReleaseStringUTFChars(resourceString, resourcePath);
        return 0;
    }
    Dictionary *dictionary = new Dictionary(dict, typedLetterMultiplier, fullWordMultiplier);
    dictionary->setAsset(dictAsset);
    
    env->ReleaseStringUTFChars(resourceString, resourcePath);
    return (jint) dictionary;  
}
void FrameLoaderClientAndroid::dispatchDidFailProvisionalLoad(const ResourceError& error) {
    ASSERT(m_frame);
    // Ignore ErrorInterrupted since it is due to a policy interruption. This
    // is caused by a decision to download the main resource rather than
    // display it.
    if (error.errorCode() == InternalErrorInterrupted
            || error.errorCode() == InternalErrorCancelled) {
        // If we decided to download the main resource or if the user cancelled
        // it, make sure we report that the load is done.
        didFinishLoad();
        return;
    }

    AssetManager* am = globalAssetManager();

    // Check to see if the error code was not generated internally
    WebCore::PlatformBridge::rawResId id = WebCore::PlatformBridge::NoDomain;
    if ((error.errorCode() == ErrorFile ||
            error.errorCode() == ErrorFileNotFound) &&
            (!error.localizedDescription().isEmpty())) {
        id = WebCore::PlatformBridge::LoadError;
    }
    String filename = m_webFrame->getRawResourceFilename(id);
    if (filename.isEmpty())
        return;

    // Grab the error page from the asset manager
    Asset* a = am->openNonAsset(
            filename.utf8().data(), Asset::ACCESS_BUFFER);
    if (!a)
        return;

    // Take the failing url and encode html entities so javascript urls are not
    // executed.
    CString failingUrl = error.failingURL().utf8();
    WTF::Vector<char> url;
    int len = failingUrl.length();
    const char* data = failingUrl.data();
    for (int i = 0; i < len; i++) {
        char c = data[i];
        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
                || (c >= '0' && c <= '9'))
            url.append(c);
        else {
            char buf[16];
            int res = sprintf(buf, "&#%d;", c);
            buf[res] = 0;
            url.append(buf, res);
        }
    }


    // Replace all occurances of %s with the failing url.
    String s = UTF8Encoding().decode((const char*)a->getBuffer(false), a->getLength());

	// samsung shkim 
	// \frameworks\base\core\res\res\raw-XX\nodomain.html or loaderror.html
	// These error pages does not have <viewport> tag, it is loaded as low zoom scale
	if( s.contains( "viewport" ) == false )
		s = s.replace( "<head>", "<head> <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\"/>" );
	
    s = s.replace("%s", String(url.data(), url.size()));

    // Replace all occurances of %e with the error text
    s = s.replace("%e", error.localizedDescription());

    // Create the request and the substitute data and tell the FrameLoader to
    // load with the replacement data.
    // use KURL(const char*) as KURL(const String& url) can trigger ASSERT for
    // invalidate URL string.
    loadDataIntoFrame(m_frame, KURL(ParsedURLString, data), error.failingURL(), s);

    // Delete the asset.
    delete a;
}
Example #3
0
/*
 * Handle the "list" command, which can be a simple file dump or
 * a verbose listing.
 *
 * The verbose listing closely matches the output of the Info-ZIP "unzip"
 * command.
 */
int doList(Bundle* bundle)
{
    int result = 1;
    ZipFile* zip = NULL;
    const ZipEntry* entry;
    long totalUncLen, totalCompLen;
    const char* zipFileName;

    if (bundle->getFileSpecCount() != 1) {
        fprintf(stderr, "ERROR: specify zip file name (only)\n");
        goto bail;
    }
    zipFileName = bundle->getFileSpecEntry(0);

    zip = openReadOnly(zipFileName);
    if (zip == NULL)
        goto bail;

    int count, i;

    if (bundle->getVerbose()) {
        printf("Archive:  %s\n", zipFileName);
        printf(
            " Length   Method    Size  Ratio   Offset      Date  Time  CRC-32    Name\n");
        printf(
            "--------  ------  ------- -----  -------      ----  ----  ------    ----\n");
    }

    totalUncLen = totalCompLen = 0;

    count = zip->getNumEntries();
    for (i = 0; i < count; i++) {
        entry = zip->getEntryByIndex(i);
        if (bundle->getVerbose()) {
            char dateBuf[32];
            time_t when;

            when = entry->getModWhen();
            strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M",
                localtime(&when));

            printf("%8ld  %-7.7s %7ld %3d%%  %8zd  %s  %08lx  %s\n",
                (long) entry->getUncompressedLen(),
                compressionName(entry->getCompressionMethod()),
                (long) entry->getCompressedLen(),
                calcPercent(entry->getUncompressedLen(),
                            entry->getCompressedLen()),
                (size_t) entry->getLFHOffset(),
                dateBuf,
                entry->getCRC32(),
                entry->getFileName());
        } else {
            printf("%s\n", entry->getFileName());
        }

        totalUncLen += entry->getUncompressedLen();
        totalCompLen += entry->getCompressedLen();
    }

    if (bundle->getVerbose()) {
        printf(
        "--------          -------  ---                            -------\n");
        printf("%8ld          %7ld  %2d%%                            %d files\n",
            totalUncLen,
            totalCompLen,
            calcPercent(totalUncLen, totalCompLen),
            zip->getNumEntries());
    }

    if (bundle->getAndroidList()) {
        AssetManager assets;
        if (!assets.addAssetPath(String8(zipFileName), NULL)) {
            fprintf(stderr, "ERROR: list -a failed because assets could not be loaded\n");
            goto bail;
        }

        const ResTable& res = assets.getResources(false);
        if (&res == NULL) {
            printf("\nNo resource table found.\n");
        } else {
#ifndef HAVE_ANDROID_OS
            printf("\nResource table:\n");
            res.print(false);
#endif
        }

        Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
                                                   Asset::ACCESS_BUFFER);
        if (manifestAsset == NULL) {
            printf("\nNo AndroidManifest.xml found.\n");
        } else {
            printf("\nAndroid manifest:\n");
            ResXMLTree tree;
            tree.setTo(manifestAsset->getBuffer(true),
                       manifestAsset->getLength());
            printXMLBlock(&tree);
        }
        delete manifestAsset;
    }

    result = 0;

bail:
    delete zip;
    return result;
}
int doDump(Bundle* bundle)
{
    status_t result = UNKNOWN_ERROR;

    if (bundle->getFileSpecCount() < 1) {
        fprintf(stderr, "ERROR: no dump option specified\n");
        return 1;
    }

    if (bundle->getFileSpecCount() < 2) {
        fprintf(stderr, "ERROR: no dump file specified\n");
        return 1;
    }

    const char* option = bundle->getFileSpecEntry(0);
    const char* filename = bundle->getFileSpecEntry(1);

    AssetManager assets;
    int32_t assetsCookie;
    if (!assets.addAssetPath(String8(filename), &assetsCookie)) {
        fprintf(stderr, "ERROR: dump failed because assets could not be loaded\n");
        return 1;
    }

    // Make a dummy config for retrieving resources...  we need to supply
    // non-default values for some configs so that we can retrieve resources
    // in the app that don't have a default.  The most important of these is
    // the API version because key resources like icons will have an implicit
    // version if they are using newer config types like density.
    ResTable_config config;
    memset(&config, 0, sizeof(ResTable_config));
    config.language[0] = 'e';
    config.language[1] = 'n';
    config.country[0] = 'U';
    config.country[1] = 'S';
    config.orientation = ResTable_config::ORIENTATION_PORT;
    config.density = ResTable_config::DENSITY_MEDIUM;
    config.sdkVersion = 10000; // Very high.
    config.screenWidthDp = 320;
    config.screenHeightDp = 480;
    config.smallestScreenWidthDp = 320;
    config.screenLayout |= ResTable_config::SCREENSIZE_NORMAL;
    assets.setConfiguration(config);

    const ResTable& res = assets.getResources(false);
    if (res.getError() != NO_ERROR) {
        fprintf(stderr, "ERROR: dump failed because the resource table is invalid/corrupt.\n");
        return 1;
    }

    // The dynamicRefTable can be null if there are no resources for this asset cookie.
    // This fine.
    const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);

    Asset* asset = NULL;

    if (strcmp("resources", option) == 0) {
#ifndef __ANDROID__
        res.print(bundle->getValues());
#endif

    } else if (strcmp("strings", option) == 0) {
        const ResStringPool* pool = res.getTableStringBlock(0);
        printStringPool(pool);

    } else if (strcmp("xmltree", option) == 0) {
        if (bundle->getFileSpecCount() < 3) {
            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
            goto bail;
        }

        for (int i=2; i<bundle->getFileSpecCount(); i++) {
            const char* resname = bundle->getFileSpecEntry(i);
            ResXMLTree tree(dynamicRefTable);
            asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
            if (asset == NULL) {
                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
                goto bail;
            }

            if (tree.setTo(asset->getBuffer(true),
                           asset->getLength()) != NO_ERROR) {
                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
                goto bail;
            }
            tree.restart();
            printXMLBlock(&tree);
            tree.uninit();
            delete asset;
            asset = NULL;
        }

    } else if (strcmp("xmlstrings", option) == 0) {
        if (bundle->getFileSpecCount() < 3) {
            fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
            goto bail;
        }

        for (int i=2; i<bundle->getFileSpecCount(); i++) {
            const char* resname = bundle->getFileSpecEntry(i);
            asset = assets.openNonAsset(assetsCookie, resname, Asset::ACCESS_BUFFER);
            if (asset == NULL) {
                fprintf(stderr, "ERROR: dump failed because resource %s found\n", resname);
                goto bail;
            }

            ResXMLTree tree(dynamicRefTable);
            if (tree.setTo(asset->getBuffer(true),
                           asset->getLength()) != NO_ERROR) {
                fprintf(stderr, "ERROR: Resource %s is corrupt\n", resname);
                goto bail;
            }
            printStringPool(&tree.getStrings());
            delete asset;
            asset = NULL;
        }

    } else {
        asset = assets.openNonAsset(assetsCookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
        if (asset == NULL) {
            fprintf(stderr, "ERROR: dump failed because no AndroidManifest.xml found\n");
            goto bail;
        }

        ResXMLTree tree(dynamicRefTable);
        if (tree.setTo(asset->getBuffer(true),
                       asset->getLength()) != NO_ERROR) {
            fprintf(stderr, "ERROR: AndroidManifest.xml is corrupt\n");
            goto bail;
        }
        tree.restart();

        if (strcmp("permissions", option) == 0) {
            size_t len;
            ResXMLTree::event_code_t code;
            int depth = 0;
            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
                if (code == ResXMLTree::END_TAG) {
                    depth--;
                    continue;
                }
                if (code != ResXMLTree::START_TAG) {
                    continue;
                }
                depth++;
                const char16_t* ctag16 = tree.getElementName(&len);
                if (ctag16 == NULL) {
                    fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
                    goto bail;
                }
                String8 tag(ctag16);
                //printf("Depth %d tag %s\n", depth, tag.string());
                if (depth == 1) {
                    if (tag != "manifest") {
                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
                        goto bail;
                    }
                    String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
                    printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
                } else if (depth == 2 && tag == "permission") {
                    String8 error;
                    String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                    if (error != "") {
                        fprintf(stderr, "ERROR: %s\n", error.string());
                        goto bail;
                    }
                    printf("permission: %s\n",
                            ResTable::normalizeForOutput(name.string()).string());
                } else if (depth == 2 && tag == "uses-permission") {
                    String8 error;
                    String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                    if (error != "") {
                        fprintf(stderr, "ERROR: %s\n", error.string());
                        goto bail;
                    }
                    printUsesPermission(name,
                            AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
                            AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
                }
            }
        } else if (strcmp("badging", option) == 0) {
            Vector<String8> locales;
            res.getLocales(&locales);

            Vector<ResTable_config> configs;
            res.getConfigurations(&configs);
            SortedVector<int> densities;
            const size_t NC = configs.size();
            for (size_t i=0; i<NC; i++) {
                int dens = configs[i].density;
                if (dens == 0) {
                    dens = 160;
                }
                densities.add(dens);
            }

            size_t len;
            ResXMLTree::event_code_t code;
            int depth = 0;
            String8 error;
            bool withinActivity = false;
            bool isMainActivity = false;
            bool isLauncherActivity = false;
            bool isLeanbackLauncherActivity = false;
            bool isSearchable = false;
            bool withinApplication = false;
            bool withinSupportsInput = false;
            bool withinFeatureGroup = false;
            bool withinReceiver = false;
            bool withinService = false;
            bool withinProvider = false;
            bool withinIntentFilter = false;
            bool hasMainActivity = false;
            bool hasOtherActivities = false;
            bool hasOtherReceivers = false;
            bool hasOtherServices = false;
            bool hasIntentFilter = false;

            bool hasWallpaperService = false;
            bool hasImeService = false;
            bool hasAccessibilityService = false;
            bool hasPrintService = false;
            bool hasWidgetReceivers = false;
            bool hasDeviceAdminReceiver = false;
            bool hasPaymentService = false;
            bool hasDocumentsProvider = false;
            bool hasCameraActivity = false;
            bool hasCameraSecureActivity = false;
            bool hasLauncher = false;
            bool hasNotificationListenerService = false;
            bool hasDreamService = false;

            bool actMainActivity = false;
            bool actWidgetReceivers = false;
            bool actDeviceAdminEnabled = false;
            bool actImeService = false;
            bool actWallpaperService = false;
            bool actAccessibilityService = false;
            bool actPrintService = false;
            bool actHostApduService = false;
            bool actOffHostApduService = false;
            bool actDocumentsProvider = false;
            bool actNotificationListenerService = false;
            bool actDreamService = false;
            bool actCamera = false;
            bool actCameraSecure = false;
            bool catLauncher = false;
            bool hasMetaHostPaymentCategory = false;
            bool hasMetaOffHostPaymentCategory = false;

            // These permissions are required by services implementing services
            // the system binds to (IME, Accessibility, PrintServices, etc.)
            bool hasBindDeviceAdminPermission = false;
            bool hasBindInputMethodPermission = false;
            bool hasBindAccessibilityServicePermission = false;
            bool hasBindPrintServicePermission = false;
            bool hasBindNfcServicePermission = false;
            bool hasRequiredSafAttributes = false;
            bool hasBindNotificationListenerServicePermission = false;
            bool hasBindDreamServicePermission = false;

            // These two implement the implicit permissions that are granted
            // to pre-1.6 applications.
            bool hasWriteExternalStoragePermission = false;
            bool hasReadPhoneStatePermission = false;

            // If an app requests write storage, they will also get read storage.
            bool hasReadExternalStoragePermission = false;

            // Implement transition to read and write call log.
            bool hasReadContactsPermission = false;
            bool hasWriteContactsPermission = false;
            bool hasReadCallLogPermission = false;
            bool hasWriteCallLogPermission = false;

            // If an app declares itself as multiArch, we report the
            // native libraries differently.
            bool hasMultiArch = false;

            // This next group of variables is used to implement a group of
            // backward-compatibility heuristics necessitated by the addition of
            // some new uses-feature constants in 2.1 and 2.2. In most cases, the
            // heuristic is "if an app requests a permission but doesn't explicitly
            // request the corresponding <uses-feature>, presume it's there anyway".

            // 2.2 also added some other features that apps can request, but that
            // have no corresponding permission, so we cannot implement any
            // back-compatibility heuristic for them. The below are thus unnecessary
            // (but are retained here for documentary purposes.)
            //bool specCompassFeature = false;
            //bool specAccelerometerFeature = false;
            //bool specProximityFeature = false;
            //bool specAmbientLightFeature = false;
            //bool specLiveWallpaperFeature = false;

            int targetSdk = 0;
            int smallScreen = 1;
            int normalScreen = 1;
            int largeScreen = 1;
            int xlargeScreen = 1;
            int anyDensity = 1;
            int requiresSmallestWidthDp = 0;
            int compatibleWidthLimitDp = 0;
            int largestWidthLimitDp = 0;
            String8 pkg;
            String8 activityName;
            String8 activityLabel;
            String8 activityIcon;
            String8 activityBanner;
            String8 receiverName;
            String8 serviceName;
            Vector<String8> supportedInput;

            FeatureGroup commonFeatures;
            Vector<FeatureGroup> featureGroups;
            KeyedVector<String8, ImpliedFeature> impliedFeatures;

            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
                if (code == ResXMLTree::END_TAG) {
                    depth--;
                    if (depth < 2) {
                        if (withinSupportsInput && !supportedInput.isEmpty()) {
                            printf("supports-input: '");
                            const size_t N = supportedInput.size();
                            for (size_t i=0; i<N; i++) {
                                printf("%s", ResTable::normalizeForOutput(
                                        supportedInput[i].string()).string());
                                if (i != N - 1) {
                                    printf("' '");
                                } else {
                                    printf("'\n");
                                }
                            }
                            supportedInput.clear();
                        }
                        withinApplication = false;
                        withinSupportsInput = false;
                        withinFeatureGroup = false;
                    } else if (depth < 3) {
                        if (withinActivity && isMainActivity) {
                            String8 aName(getComponentName(pkg, activityName));
                            if (isLauncherActivity) {
                                printf("launchable-activity:");
                                if (aName.length() > 0) {
                                    printf(" name='%s' ",
                                            ResTable::normalizeForOutput(aName.string()).string());
                                }
                                printf(" label='%s' icon='%s'\n",
                                        ResTable::normalizeForOutput(activityLabel.string()).string(),
                                        ResTable::normalizeForOutput(activityIcon.string()).string());
                            }
                            if (isLeanbackLauncherActivity) {
                                printf("leanback-launchable-activity:");
                                if (aName.length() > 0) {
                                    printf(" name='%s' ",
                                            ResTable::normalizeForOutput(aName.string()).string());
                                }
                                printf(" label='%s' icon='%s' banner='%s'\n",
                                        ResTable::normalizeForOutput(activityLabel.string()).string(),
                                        ResTable::normalizeForOutput(activityIcon.string()).string(),
                                        ResTable::normalizeForOutput(activityBanner.string()).string());
                            }
                        }
                        if (!hasIntentFilter) {
                            hasOtherActivities |= withinActivity;
                            hasOtherReceivers |= withinReceiver;
                            hasOtherServices |= withinService;
                        } else {
                            if (withinService) {
                                hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
                                        hasBindNfcServicePermission);
                                hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
                                        hasBindNfcServicePermission);
                            }
                        }
                        withinActivity = false;
                        withinService = false;
                        withinReceiver = false;
                        withinProvider = false;
                        hasIntentFilter = false;
                        isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
                    } else if (depth < 4) {
                        if (withinIntentFilter) {
                            if (withinActivity) {
                                hasMainActivity |= actMainActivity;
                                hasLauncher |= catLauncher;
                                hasCameraActivity |= actCamera;
                                hasCameraSecureActivity |= actCameraSecure;
                                hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
                            } else if (withinReceiver) {
                                hasWidgetReceivers |= actWidgetReceivers;
                                hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
                                        hasBindDeviceAdminPermission);
                                hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
                            } else if (withinService) {
                                hasImeService |= actImeService;
                                hasWallpaperService |= actWallpaperService;
                                hasAccessibilityService |= (actAccessibilityService &&
                                        hasBindAccessibilityServicePermission);
                                hasPrintService |= (actPrintService && hasBindPrintServicePermission);
                                hasNotificationListenerService |= actNotificationListenerService &&
                                        hasBindNotificationListenerServicePermission;
                                hasDreamService |= actDreamService && hasBindDreamServicePermission;
                                hasOtherServices |= (!actImeService && !actWallpaperService &&
                                        !actAccessibilityService && !actPrintService &&
                                        !actHostApduService && !actOffHostApduService &&
                                        !actNotificationListenerService);
                            } else if (withinProvider) {
                                hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
                            }
                        }
                        withinIntentFilter = false;
                    }
                    continue;
                }
                if (code != ResXMLTree::START_TAG) {
                    continue;
                }
                depth++;

                const char16_t* ctag16 = tree.getElementName(&len);
                if (ctag16 == NULL) {
                    fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n");
                    goto bail;
                }
                String8 tag(ctag16);
                //printf("Depth %d,  %s\n", depth, tag.string());
                if (depth == 1) {
                    if (tag != "manifest") {
                        fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
                        goto bail;
                    }
                    pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
                    printf("package: name='%s' ",
                            ResTable::normalizeForOutput(pkg.string()).string());
                    int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR,
                            &error);
                    if (error != "") {
                        fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n",
                                error.string());
                        goto bail;
                    }
                    if (versionCode > 0) {
                        printf("versionCode='%d' ", versionCode);
                    } else {
                        printf("versionCode='' ");
                    }
                    String8 versionName = AaptXml::getResolvedAttribute(res, tree,
                            VERSION_NAME_ATTR, &error);
                    if (error != "") {
                        fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n",
                                error.string());
                        goto bail;
                    }
                    printf("versionName='%s'",
                            ResTable::normalizeForOutput(versionName.string()).string());

                    String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
                    if (!splitName.isEmpty()) {
                        printf(" split='%s'", ResTable::normalizeForOutput(
                                    splitName.string()).string());
                    }

                    String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
                            "platformBuildVersionName");
                    printf(" platformBuildVersionName='%s'", platformVersionName.string());
                    printf("\n");

                    int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
                            INSTALL_LOCATION_ATTR, &error);
                    if (error != "") {
                        fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n",
                                error.string());
                        goto bail;
                    }

                    if (installLocation >= 0) {
                        printf("install-location:'");
                        switch (installLocation) {
                            case 0:
                                printf("auto");
                                break;
                            case 1:
                                printf("internalOnly");
                                break;
                            case 2:
                                printf("preferExternal");
                                break;
                            default:
                                fprintf(stderr, "Invalid installLocation %d\n", installLocation);
                                goto bail;
                        }
                        printf("'\n");
                    }
                } else if (depth == 2) {
                    withinApplication = false;
                    if (tag == "application") {
Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
        String8 *outError = NULL)
{
    Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
    if (aidAsset == NULL) {
        if (outError != NULL) *outError = "xml resource does not exist";
        return Vector<String8>();
    }

    const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");

    bool withinApduService = false;
    Vector<String8> categories;

    String8 error;
    ResXMLTree tree;
    tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());

    size_t len;
    int depth = 0;
    ResXMLTree::event_code_t code;
    while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
        if (code == ResXMLTree::END_TAG) {
            depth--;
            const char16_t* ctag16 = tree.getElementName(&len);
            if (ctag16 == NULL) {
                *outError = "failed to get XML element name (bad string pool)";
                return Vector<String8>();
            }
            String8 tag(ctag16);

            if (depth == 0 && tag == serviceTagName) {
                withinApduService = false;
            }

        } else if (code == ResXMLTree::START_TAG) {
            depth++;
            const char16_t* ctag16 = tree.getElementName(&len);
            if (ctag16 == NULL) {
                *outError = "failed to get XML element name (bad string pool)";
                return Vector<String8>();
            }
            String8 tag(ctag16);

            if (depth == 1) {
                if (tag == serviceTagName) {
                    withinApduService = true;
                }
            } else if (depth == 2 && withinApduService) {
                if (tag == "aid-group") {
                    String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error);
                    if (error != "") {
                        if (outError != NULL) *outError = error;
                        return Vector<String8>();
                    }

                    categories.add(category);
                }
            }
        }
    }
    aidAsset->close();
    return categories;
}
static bool getAppInfo(const String8& path, AppInfo& outInfo) {
    memset(&outInfo, 0, sizeof(outInfo));

    AssetManager assetManager;
    int32_t cookie = 0;
    if (!assetManager.addAssetPath(path, &cookie)) {
        return false;
    }

    Asset* asset = assetManager.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER);
    if (asset == NULL) {
        return false;
    }

    ResXMLTree xml;
    if (xml.setTo(asset->getBuffer(true), asset->getLength(), false) != NO_ERROR) {
        delete asset;
        return false;
    }

    const String16 kAndroidNamespace("http://schemas.android.com/apk/res/android");
    const String16 kManifestTag("manifest");
    const String16 kApplicationTag("application");
    const String16 kUsesSdkTag("uses-sdk");
    const String16 kVersionCodeAttr("versionCode");
    const String16 kMultiArchAttr("multiArch");
    const String16 kMinSdkVersionAttr("minSdkVersion");

    ResXMLParser::event_code_t event;
    while ((event = xml.next()) != ResXMLParser::BAD_DOCUMENT &&
            event != ResXMLParser::END_DOCUMENT) {
        if (event != ResXMLParser::START_TAG) {
            continue;
        }

        size_t len;
        const char16_t* name = xml.getElementName(&len);
        String16 name16(name, len);
        if (name16 == kManifestTag) {
            ssize_t idx = xml.indexOfAttribute(
                    kAndroidNamespace.string(), kAndroidNamespace.size(),
                    kVersionCodeAttr.string(), kVersionCodeAttr.size());
            if (idx >= 0) {
                outInfo.versionCode = xml.getAttributeData(idx);
            }

        } else if (name16 == kApplicationTag) {
            ssize_t idx = xml.indexOfAttribute(
                    kAndroidNamespace.string(), kAndroidNamespace.size(),
                    kMultiArchAttr.string(), kMultiArchAttr.size());
            if (idx >= 0) {
                outInfo.multiArch = xml.getAttributeData(idx) != 0;
            }

        } else if (name16 == kUsesSdkTag) {
            ssize_t idx = xml.indexOfAttribute(
                    kAndroidNamespace.string(), kAndroidNamespace.size(),
                    kMinSdkVersionAttr.string(), kMinSdkVersionAttr.size());
            if (idx >= 0) {
                uint16_t type = xml.getAttributeDataType(idx);
                if (type >= Res_value::TYPE_FIRST_INT && type <= Res_value::TYPE_LAST_INT) {
                    outInfo.minSdkVersion = xml.getAttributeData(idx);
                } else if (type == Res_value::TYPE_STRING) {
                    String8 minSdk8(xml.getStrings().string8ObjectAt(idx));
                    char* endPtr;
                    int minSdk = strtol(minSdk8.string(), &endPtr, 10);
                    if (endPtr != minSdk8.string() + minSdk8.size()) {
                        fprintf(stderr, "warning: failed to parse android:minSdkVersion '%s'\n",
                                minSdk8.string());
                    } else {
                        outInfo.minSdkVersion = minSdk;
                    }
                } else {
                    fprintf(stderr, "warning: unrecognized value for android:minSdkVersion.\n");
                }
            }
        }
    }

    delete asset;
    return true;
}