Java 类soot.jimple.infoflow.android.axml.AXmlAttribute 实例源码

项目:FuzzDroid    文件:UtilApk.java   
@SuppressWarnings("unchecked")
private static void addMaxPrioForSMSReceiver(ProcessManifest manifest) {
    for(AXmlNode receiver : manifest.getReceivers()) {
        for(AXmlNode receiverChild : receiver.getChildren()) {
            if(receiverChild.getTag().equals("intent-filter")) {
                //search for SMS receiver
                for(AXmlNode childChild : receiverChild.getChildren()) {
                    if(childChild.getTag().equals("action")) {
                        if(childChild.hasAttribute("name") && ((String)childChild.getAttribute("name").getValue()).equalsIgnoreCase("android.provider.Telephony.SMS_RECEIVED")){
                            //prepare the priority filter
                            if(receiverChild.hasAttribute("priority")) 
                                ((AXmlAttribute<Integer>)receiverChild.getAttribute("priority")).setValue(Integer.MAX_VALUE);
                            else {
                                AXmlAttribute<Integer> attr = new AXmlAttribute<Integer>("priority", Integer.MAX_VALUE, ANDROID_NAMESPACE);
                                receiverChild.addAttribute(attr);
                            }
                        }
                    }
                }
            }
        }
    }
}
项目:JAADAS    文件:ProcessManifest.java   
private boolean isComponentExported(AXmlNode node)
{
    boolean accessible = false;
    AXmlAttribute<?> attrEnabled = node.getAttribute("enabled");
    if (attrEnabled == null || !attrEnabled.getValue().equals(Boolean.FALSE)) {
        AXmlAttribute<?> attrExported = node.getAttribute("exported");
        if (attrExported != null){
            if(attrExported.getValue().equals(Boolean.TRUE)) {
                accessible = true;
            }
            else if(attrExported.getValue().equals(Boolean.FALSE))
            {
                accessible = false;
            }
        }
        else
        {
            if(node.getChildrenWithTag("intent-filter").size() != 0) {
                accessible = true;
            }
        }
    }
    return accessible;
}
项目:JAADAS    文件:ProcessManifest.java   
private void checkAndAddComponent(Set<String> entryPoints, AXmlNode node) {
    AXmlAttribute<?> attrEnabled = node.getAttribute("enabled");
    if (attrEnabled == null || !attrEnabled.getValue().equals(Boolean.FALSE)) {
        AXmlAttribute<?> attr = node.getAttribute("name");
        if (attr != null)
            entryPoints.add(expandClassName((String) attr.getValue()));
        else {
            // This component does not have a name, so this might be obfuscated
            // malware. We apply a heuristic.
            for (Entry<String, AXmlAttribute<?>> a : node.getAttributes().entrySet())
                if (a.getValue().getName().isEmpty()
                        && a.getValue().getType() == AxmlVisitor.TYPE_STRING) {
                    String name = (String) a.getValue().getValue();
                    if (isValidComponentName(name))
                        entryPoints.add(expandClassName(name));
                }
        }
    }
}
项目:JAADAS    文件:ProcessManifest.java   
/**
 * Gets the permissions this application requests
 * @return The permissions requested by this application
 * @return
 */
public Set<String> getPermissions() {
    List<AXmlNode> usesPerms = this.manifest.getChildrenWithTag("uses-permission");
    Set<String> permissions = new HashSet<String>();
    for (AXmlNode perm : usesPerms) {
        AXmlAttribute<?> attr = perm.getAttribute("name");
        if (attr != null)
            permissions.add((String) attr.getValue());
        else {
            // The required "name" attribute is missing, so we collect all empty
            // attributes as a best-effort solution for broken malware apps
            for (AXmlAttribute<?> a : perm.getAttributes().values())
                if (a.getType() == AxmlVisitor.TYPE_STRING && a.getName().isEmpty())
                    permissions.add((String) a.getValue());
        }
    }
    return permissions;
}
项目:DroidRA    文件:ProcessManifest.java   
private void checkAndAddComponent(Set<String> entryPoints, AXmlNode node) {
    AXmlAttribute<?> attrEnabled = node.getAttribute("enabled");
    if (attrEnabled == null || !attrEnabled.getValue().equals(Boolean.FALSE)) {
        AXmlAttribute<?> attr = node.getAttribute("name");
        if (attr != null)
            entryPoints.add(expandClassName((String) attr.getValue()));
        else {
            // This component does not have a name, so this might be obfuscated
            // malware. We apply a heuristic.
            for (Entry<String, AXmlAttribute<?>> a : node.getAttributes().entrySet())
                if (a.getValue().getName().isEmpty()
                        && a.getValue().getType() == AxmlVisitor.TYPE_STRING) {
                    String name = (String) a.getValue().getValue();
                    if (isValidComponentName(name))
                        entryPoints.add(expandClassName(name));
                }
        }
    }
}
项目:DroidRA    文件:ProcessManifest.java   
/**
 * Gets the permissions this application requests
 * @return The permissions requested by this application
 * @return
 */
public Set<String> getPermissions() {
    List<AXmlNode> usesPerms = this.manifest.getChildrenWithTag("uses-permission");
    Set<String> permissions = new HashSet<String>();
    for (AXmlNode perm : usesPerms) {
        AXmlAttribute<?> attr = perm.getAttribute("name");
        if (attr != null)
            permissions.add((String) attr.getValue());
        else {
            // The required "name" attribute is missing, so we collect all empty
            // attributes as a best-effort solution for broken malware apps
            for (AXmlAttribute<?> a : perm.getAttributes().values())
                if (a.getType() == AxmlVisitor.TYPE_STRING && a.getName().isEmpty())
                    permissions.add((String) a.getValue());
        }
    }
    return permissions;
}
项目:FuzzDroid    文件:UtilApk.java   
private static void addComponentCallerService(ProcessManifest androidManifest) {
    AXmlNode componentCallerService = new AXmlNode("service", null, androidManifest.getApplication());
    AXmlAttribute<String> nameAttribute = new AXmlAttribute<String>("name", UtilInstrumenter.COMPONENT_CALLER_SERVICE_HELPER,  ANDROID_NAMESPACE);
    AXmlAttribute<String> exportedAttribute = new AXmlAttribute<String>("exported", "false",  ANDROID_NAMESPACE);
    componentCallerService.addAttribute(nameAttribute);
    componentCallerService.addAttribute(exportedAttribute);

    androidManifest.addService(componentCallerService);
}
项目:FuzzDroid    文件:UtilApk.java   
private static void addHookinHelperAsApplicationIfNecessary(ProcessManifest androidManifest){
    AXmlNode application = androidManifest.getApplication();
    if(!application.hasAttribute("name")) {
        AXmlAttribute<String> nameAttribute = new AXmlAttribute<String>("name", UtilInstrumenter.HELPER_APPLICATION_FOR_HOOKING,  ANDROID_NAMESPACE);
        application.addAttribute(nameAttribute);
    }
}
项目:FuzzDroid    文件:UtilApk.java   
private static void addTracingService(ProcessManifest androidManifest) {
    AXmlNode tracingService = new AXmlNode("service", null, androidManifest.getApplication());
    AXmlAttribute<String> nameAttribute = new AXmlAttribute<String>("name", UtilInstrumenter.HELPER_SERVICE_FOR_PATH_TRACKING,  ANDROID_NAMESPACE);
    AXmlAttribute<String> exportedAttribute = new AXmlAttribute<String>("exported", "false",  ANDROID_NAMESPACE);
    tracingService.addAttribute(nameAttribute);
    tracingService.addAttribute(exportedAttribute);

    androidManifest.addService(tracingService);
}
项目:JAADAS    文件:LayoutFileParser.java   
/**
 * Parses the attributes required for a layout file inclusion
 * @param layoutFile The full path and file name of the file being parsed
 * @param rootNode The AXml node containing the attributes
 */
private void parseIncludeAttributes(String layoutFile, AXmlNode rootNode) {
    for (Entry<String, AXmlAttribute<?>> entry : rootNode.getAttributes().entrySet()) {
        String attrName = entry.getKey().trim();
        AXmlAttribute<?> attr = entry.getValue();

        if (attrName.equals("layout")) {
            if ((attr.getType() == AxmlVisitor.TYPE_REFERENCE || attr.getType() == AxmlVisitor.TYPE_INT_HEX)
                    && attr.getValue() instanceof Integer) {
                // We need to get the target XML file from the binary manifest
                AbstractResource targetRes = resParser.findResource((Integer) attr.getValue());
                if (targetRes == null) {
                    System.err.println("Target resource " + attr.getValue() + " for layout include not found");
                    return;
                }
                if (!(targetRes instanceof StringResource)) {
                    System.err.println("Invalid target node for include tag in layout XML, was "
                            + targetRes.getClass().getName());
                    return;
                }
                String targetFile = ((StringResource) targetRes).getValue();

                // If we have already processed the target file, we can
                // simply copy the callbacks we have found there
                if (callbackMethods.containsKey(targetFile))
                    for (String callback : callbackMethods.get(targetFile))
                        addCallbackMethod(layoutFile, callback);
                else {
                    // We need to record a dependency to resolve later
                    addToMapSet(includeDependencies, targetFile, layoutFile);
                }
            }
        }
    }
}
项目:JAADAS    文件:ProcessManifest.java   
private void checkPublicAccessibleCompoenent(Set<String> entryPoints, AXmlNode node)
   {
    if (isComponentExported(node)) {
           AXmlAttribute<?> attr = node.getAttribute("name");
           if (attr != null)
               entryPoints.add(expandClassName((String) attr.getValue()));
       }
}
项目:JAADAS    文件:ProcessManifest.java   
public String getPackageName() {
    if (cache_PackageName == null) {
        AXmlAttribute<?> attr = this.manifest.getAttribute("package");
        if (attr != null)
            cache_PackageName = (String) attr.getValue();
    }
    return cache_PackageName;
}
项目:JAADAS    文件:ProcessManifest.java   
/**
 * Gets the minimum SDK version on which this application is supposed to run
 * @return The minimum SDK version on which this application is supposed to run
 */
public int getMinSdkVersion() {
    List<AXmlNode> usesSdk = this.manifest.getChildrenWithTag("uses-sdk");
    if (usesSdk == null || usesSdk.isEmpty())
        return -1;
    AXmlAttribute<?> attr = usesSdk.get(0).getAttribute("minSdkVersion");

    if (attr == null)
        return -1;

    if (attr.getValue() instanceof Integer)
        return (Integer) attr.getValue();

    return Integer.getInteger((String) attr.getValue());        
}
项目:JAADAS    文件:ProcessManifest.java   
/**
 * Gets the target SDK version for which this application was developed
 * @return The target SDK version for which this application was developed
 */
public int targetSdkVersion() {
    List<AXmlNode> usesSdk = this.manifest.getChildrenWithTag("uses-sdk");
    if (usesSdk == null || usesSdk.isEmpty())
        return -1;
    AXmlAttribute<?> attr = usesSdk.get(0).getAttribute("targetSdkVersion");

    if (attr == null)
        return -1;

    if (attr.getValue() instanceof Integer)
        return (Integer) attr.getValue();

    return Integer.getInteger((String) attr.getValue());        
}
项目:DroidRA    文件:ProcessManifest.java   
public String getPackageName() {
    if (cache_PackageName == null) {
        AXmlAttribute<?> attr = this.manifest.getAttribute("package");
        if (attr != null)
            cache_PackageName = (String) attr.getValue();
    }
    return cache_PackageName;
}
项目:DroidRA    文件:ProcessManifest.java   
/**
 * Gets the minimum SDK version on which this application is supposed to run
 * @return The minimum SDK version on which this application is supposed to run
 */
public int getMinSdkVersion() {
    List<AXmlNode> usesSdk = this.manifest.getChildrenWithTag("uses-sdk");
    if (usesSdk == null || usesSdk.isEmpty())
        return -1;
    AXmlAttribute<?> attr = usesSdk.get(0).getAttribute("minSdkVersion");

    if (attr == null)
        return -1;

    if (attr.getValue() instanceof Integer)
        return (Integer) attr.getValue();

    return Integer.parseInt("" + attr.getValue());      
}
项目:DroidRA    文件:ProcessManifest.java   
/**
 * Gets the target SDK version for which this application was developed
 * @return The target SDK version for which this application was developed
 */
public int targetSdkVersion() {
    List<AXmlNode> usesSdk = this.manifest.getChildrenWithTag("uses-sdk");
    if (usesSdk == null || usesSdk.isEmpty())
        return -1;
    AXmlAttribute<?> attr = usesSdk.get(0).getAttribute("targetSdkVersion");

    if (attr == null)
        return -1;

    if (attr.getValue() instanceof Integer)
        return (Integer) attr.getValue();

    return Integer.parseInt("" + attr.getValue());      
}
项目:DroidForce    文件:UpdateManifestAndCodeForWaitPDP.java   
/**
 * Get the package name of the application
 * @param apkFileLocation
 * @return
 */
public static String getApplicationPackageName(String apkFileLocation) {
    String packageName = null;
    try {
        ProcessManifest pm = new ProcessManifest(apkFileLocation);
        AXmlHandler axmlh = pm.getAXml(); 

        // Find main activity and remove main intent-filter
        List<AXmlNode> anodes = axmlh.getNodesWithTag("manifest");
        for (AXmlNode an: anodes) {
            boolean hasMain = false;
            boolean hasLauncher = false;
            AXmlNode filter = null;

            AXmlAttribute aname = an.getAttribute("package");
            String aval = (String)aname.getValue();
            packageName = aval;
            System.out.println("package: "+ packageName);
            break;

        }
    } catch (IOException | XmlPullParserException ex) {
        System.err.println("Could not read Android manifest file: " + ex.getMessage());
        throw new RuntimeException(ex);
    }

    return packageName;
}
项目:JAADAS    文件:LayoutFileParser.java   
/**
 * Parses the layout file with the given root node
 * @param layoutFile The full path and file name of the file being parsed
 * @param rootNode The root node from where to start parsing
 */
private void parseLayoutNode(String layoutFile, AXmlNode rootNode) {
    if (rootNode.getTag() == null || rootNode.getTag().isEmpty()) {
        System.err.println("Encountered a null or empty node name "
                + "in file " + layoutFile + ", skipping node...");
        return;
    }

    String tname = rootNode.getTag().trim();
    if (tname.equals("dummy")) {
        // dummy root node, ignore it
    }
    // Check for inclusions
    else if (tname.equals("include")) {
        parseIncludeAttributes(layoutFile, rootNode);
    }
    // The "merge" tag merges the next hierarchy level into the current
    // one for flattening hierarchies.
    else if (tname.equals("merge"))  {
        // do not consider any attributes of this elements, just
        // continue with the children
    }
    else if (tname.equals("fragment"))  {
        final AXmlAttribute<?> attr = rootNode.getAttribute("name");
        if (attr == null)
            System.err.println("Fragment without class name detected");
        else {
            if (attr.getType() != AxmlVisitor.TYPE_STRING)
                System.err.println("Invalid targer resource "+attr.getValue()+"for fragment class value");
            getLayoutClass(attr.getValue().toString());
        }
    }
    else {
        final SootClass childClass = getLayoutClass(tname);
        if (childClass != null && (isLayoutClass(childClass) || isViewClass(childClass)))
            parseLayoutAttributes(layoutFile, childClass, rootNode);
    }

    // Parse the child nodes
    for (AXmlNode childNode : rootNode.getChildren())
        parseLayoutNode(layoutFile, childNode);
}
项目:JAADAS    文件:LayoutFileParser.java   
/**
 * Parses the layout attributes in the given AXml node 
 * @param layoutFile The full path and file name of the file being parsed
 * @param layoutClass The class for the attributes are parsed
 * @param rootNode The AXml node containing the attributes
 */
private void parseLayoutAttributes(String layoutFile, SootClass layoutClass, AXmlNode rootNode) {
    boolean isSensitive = false;
    int id = -1;

    for (Entry<String, AXmlAttribute<?>> entry : rootNode.getAttributes().entrySet()) {
        String attrName = entry.getKey().trim();
        AXmlAttribute<?> attr = entry.getValue();

        // On obfuscated Android malware, the attribute name may be empty
        if (attrName.isEmpty())
            continue;

        // Check that we're actually working on an android attribute
        if (!isAndroidNamespace(attr.getNamespace()))
            continue;

        // Read out the field data
        if (attrName.equals("id")
                && (attr.getType() == AxmlVisitor.TYPE_REFERENCE || attr.getType() == AxmlVisitor.TYPE_INT_HEX))
            id = (Integer) attr.getValue();
        else if (attrName.equals("password")) {
            if (attr.getType() == AxmlVisitor.TYPE_INT_HEX)
                isSensitive = ((Integer) attr.getValue()) != 0; // -1 for true, 0 for false
            else if (attr.getType() == AxmlVisitor.TYPE_INT_BOOLEAN)
                isSensitive = (Boolean) attr.getValue();
            else
                throw new RuntimeException("Unknown representation of boolean data type");
        }
        else if (!isSensitive && attrName.equals("inputType") && attr.getType() == AxmlVisitor.TYPE_INT_HEX) {
            int tp = (Integer) attr.getValue();
            isSensitive = ((tp & TYPE_NUMBER_VARIATION_PASSWORD) == TYPE_NUMBER_VARIATION_PASSWORD)
                    || ((tp & TYPE_TEXT_VARIATION_PASSWORD) == TYPE_TEXT_VARIATION_PASSWORD)
                    || ((tp & TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) == TYPE_TEXT_VARIATION_VISIBLE_PASSWORD)
                    || ((tp & TYPE_TEXT_VARIATION_WEB_PASSWORD) == TYPE_TEXT_VARIATION_WEB_PASSWORD);
        }
        else if (isActionListener(attrName)
                && attr.getType() == AxmlVisitor.TYPE_STRING
                && attr.getValue() instanceof String) {
            String strData = ((String) attr.getValue()).trim();
            addCallbackMethod(layoutFile, strData);
        }
        else if (attr.getType() == AxmlVisitor.TYPE_STRING && attrName.equals("text")) {
            // To avoid unrecognized attribute for "text" field
        }
        else if (DEBUG && attr.getType() == AxmlVisitor.TYPE_STRING) {
            System.out.println("Found unrecognized XML attribute:  " + attrName);
        }
    }

    // Register the new user control
    addToMapSet(this.userControls, layoutFile, new LayoutControl(id, layoutClass, isSensitive));
}
项目:JAADAS    文件:ProcessManifest.java   
/**
 * Gets the application's version name as it is displayed to the user
 * @return The application#s version name as in pretty print
 */
public String getVersionName() {
    AXmlAttribute<?> attr = this.manifest.getAttribute("versionName");
    return attr == null ? null : (String) attr.getValue();
}
项目:JAADAS    文件:ProcessManifest.java   
/**
 * Gets the name of the Android application class
 * @return The name of the Android application class
 */
public String getApplicationName() {
    AXmlAttribute<?> attr = this.application.getAttribute("name");
    return attr == null ? null : expandClassName((String) attr.getValue());     
}
项目:JAADAS    文件:ProcessManifest.java   
/**
 * Gets whether this Android application is enabled
 * @return True if this application is enabled, otherwise false
 */
public boolean isApplicationEnabled() {
    AXmlAttribute<?> attr = this.application.getAttribute("enabled");
    return attr == null || !attr.getValue().equals(Boolean.FALSE);
}
项目:JAADAS    文件:ProcessManifest.java   
public boolean isApplicationDebuggable(){
    AXmlAttribute<?> attr = this.application.getAttribute("debuggable");
    return attr != null && attr.getValue().equals(Boolean.TRUE);
}
项目:JAADAS    文件:ProcessManifest.java   
public boolean isApplicationDisallowBackup(){
    AXmlAttribute<?> attr = this.application.getAttribute("allowBackup");
    return attr != null && attr.getValue().equals(Boolean.FALSE);
}
项目:JAADAS    文件:ProcessManifest.java   
/**
 * Adds a new permission to the manifest.
 * @param complete permission name e.g. "android.permission.INTERNET"
 */
public void addPermission(String permissionName) {              
    AXmlNode permission = new AXmlNode("uses-permission", null, manifest);
    AXmlAttribute<String> permissionNameAttr = new AXmlAttribute<String>("name", permissionName,  AXmlHandler.ANDROID_NAMESPACE);       
    permission.addAttribute(permissionNameAttr);
}
项目:DroidRA    文件:ProcessManifest.java   
/**
 * Gets the application's version name as it is displayed to the user
 * @return The application#s version name as in pretty print
 */
public String getVersionName() {
    AXmlAttribute<?> attr = this.manifest.getAttribute("versionName");
    return attr == null ? null : (String) attr.getValue();
}
项目:DroidRA    文件:ProcessManifest.java   
/**
 * Gets the name of the Android application class
 * @return The name of the Android application class
 */
public String getApplicationName() {
    AXmlAttribute<?> attr = this.application.getAttribute("name");
    return attr == null ? null : expandClassName((String) attr.getValue());     
}
项目:DroidRA    文件:ProcessManifest.java   
/**
 * Gets whether this Android application is enabled
 * @return True if this application is enabled, otherwise false
 */
public boolean isApplicationEnabled() {
    AXmlAttribute<?> attr = this.application.getAttribute("enabled");
    return attr == null || !attr.getValue().equals(Boolean.FALSE);
}
项目:DroidRA    文件:ProcessManifest.java   
/**
 * Adds a new permission to the manifest.
 * @param complete permission name e.g. "android.permission.INTERNET"
 */
public void addPermission(String permissionName) {              
    AXmlNode permission = new AXmlNode("uses-permission", null, manifest);
    AXmlAttribute<String> permissionNameAttr = new AXmlAttribute<String>("name", permissionName,  AXmlHandler.ANDROID_NAMESPACE);       
    permission.addAttribute(permissionNameAttr);
}
项目:DroidForce    文件:UpdateManifestAndCodeForWaitPDP.java   
/**
 * Get the name of the main activity in the AndroidManifest.xml file
 * @param apkFileLocation
 * @return
 */
public static String getMainActivityName(String apkFileLocation) {
    String mainActivityName = null;
    try {
        ProcessManifest pm = new ProcessManifest(apkFileLocation);
        AXmlHandler axmlh = pm.getAXml(); 

        // Find main activity and remove main intent-filter
        List<AXmlNode> anodes = axmlh.getNodesWithTag("activity");
        for (AXmlNode an: anodes) {
            boolean hasMain = false;
            boolean hasLauncher = false;
            AXmlNode filter = null;

            AXmlAttribute aname = an.getAttribute("name");
            String aval = (String)aname.getValue();
            System.out.println("activity: "+ aval);
            for (AXmlNode ch : an.getChildren()) {
                System.out.println("children: "+ ch);
            }
            List<AXmlNode> fnodes = an.getChildrenWithTag("intent-filter");
            for (AXmlNode fn: fnodes) {

                hasMain = false;
                hasLauncher = false;

                // check action
                List<AXmlNode> acnodes = fn.getChildrenWithTag("action");
                for (AXmlNode acn: acnodes) {
                    AXmlAttribute acname = acn.getAttribute("name");
                    String acval = (String)acname.getValue();
                    System.out.println("action: "+ acval);
                    if (acval.equals("android.intent.action.MAIN")) {
                        hasMain = true;
                    }
                }
                // check category
                List<AXmlNode> catnodes = fn.getChildrenWithTag("category");
                for (AXmlNode catn: catnodes) {
                    AXmlAttribute catname = catn.getAttribute("name");
                    String catval = (String)catname.getValue();
                    System.out.println("category: "+ catval);
                    if (catval.equals("android.intent.category.LAUNCHER")) {
                        hasLauncher = true;
                        filter = fn;
                    }
                }
                if (hasLauncher && hasMain) {
                    break;
                }
            }

            if (hasLauncher && hasMain) {
                // replace name with the activity waiting for the connection to the PDP
                System.out.println("main activity is: "+ aval);
                System.out.println("excluding filter: "+ filter);
                filter.exclude();
                mainActivityName = aval;
                break;
            }

        }
    } catch (IOException | XmlPullParserException ex) {
        System.err.println("Could not read Android manifest file: " + ex.getMessage());
        throw new RuntimeException(ex);
    }

    return mainActivityName;
}
项目:JAADAS    文件:ProcessManifest.java   
/**
 * Gets the version code of the application. This code is used to compare
 * versions for updates.
 * @return The version code of the application
 */
public int getVersionCode() {
    AXmlAttribute<?> attr = this.manifest.getAttribute("versionCode");
    return attr == null ? -1 : Integer.getInteger((String) attr.getValue());
}
项目:DroidRA    文件:ProcessManifest.java   
/**
 * Gets the version code of the application. This code is used to compare
 * versions for updates.
 * @return The version code of the application
 */
public int getVersionCode() {
    AXmlAttribute<?> attr = this.manifest.getAttribute("versionCode");
    return attr == null ? -1 : Integer.parseInt("" + attr.getValue());
}