/* * Copyright (C) 2010 Ryszard Wiśniewski * Copyright (C) 2010 Connor Tumbleson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package brut.androlib.res.decoder; import android.util.TypedValue; import java.util.regex.Pattern; /** * AXmlResourceParser specifically for parsing encoded AndroidManifest.xml. */ public class AndroidManifestResourceParser extends AXmlResourceParser { /** * Pattern for matching numeric string meta-data values. aapt automatically infers the * type for a manifest meta-data value based on the string in the unencoded XML. However, * some apps intentionally coerce integers to be strings by prepending an escaped space. * For details/discussion, see https://stackoverflow.com/questions/2154945/how-to-force-a-meta-data-value-to-type-string * With aapt1, the escaped space is dropped when encoded. For aapt2, the escaped space is preserved. */ private static final Pattern PATTERN_NUMERIC_STRING = Pattern.compile("\\s?\\d+"); @Override public String getAttributeValue(int index) { String value = super.getAttributeValue(index); if (value == null) { return ""; } if (!isNumericStringMetadataAttributeValue(index, value)) { return value; } // Patch the numeric string value by prefixing it with an escaped space. // Otherwise, when the decoded app is rebuilt, aapt will incorrectly encode // the value as an int or float (depending on aapt version), breaking the original // app functionality. return "\\ " + value.trim(); } private boolean isNumericStringMetadataAttributeValue(int index, String value) { return "meta-data".equalsIgnoreCase(super.getName()) && "value".equalsIgnoreCase(super.getAttributeName(index)) && super.getAttributeValueType(index) == TypedValue.TYPE_STRING && PATTERN_NUMERIC_STRING.matcher(value).matches(); } }