Merge branch 'master' into patch-1

This commit is contained in:
Connor Tumbleson 2019-02-27 18:26:02 -05:00 committed by GitHub
commit 8e00de38a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 481 additions and 57 deletions

2
.gitignore vendored
View File

@ -20,7 +20,7 @@ bin/
# IntelliJ
*.iml
.idea/*
/out
**/out/
# Patches
*.patch

View File

@ -1,28 +1,35 @@
env:
- TERM=dumb GROOVY_TURN_OFF_JAVA_WARNINGS=true
global:
- TERM=dumb
- GROOVY_TURN_OFF_JAVA_WARNINGS=true
language: java
dist: trusty
matrix:
include:
- os: linux
jdk: openjdk8
# - os: linux
# jdk: openjdk9
- os: linux
jdk: oraclejdk8
# - os: linux
# jdk: oraclejdk9
- os: osx
osx_image: xcode9.2
# - os: osx
# osx_image: xcode9.4
- os: windows
language: shell
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
install: ./gradlew clean
script: ./gradlew build shadowJar proguard
install:
- chmod +x ./scripts/travis-ci/clean-tests.sh
- ./scripts/travis-ci/clean-tests.sh
script:
- chmod +x ./scripts/travis-ci/run-tests.sh
- ./scripts/travis-ci/run-tests.sh
branches:
only:
- master
- stable
- master

View File

@ -194,7 +194,7 @@ is just a guideline but helps me to release a new version every 3 months.
### Social Spam
The final step is to send this release into the wild via some social posting. Head to the blog
where the release post was and send that link to Twiter, Google and whatever else you use.
where the release post was and send that link to Twitter, Google and whatever else you use.
Relax and watch the bug tracker.
@ -225,8 +225,8 @@ we aren't building the entire AOSP package, the initial build is to just see if
We check out a certain tag. Currently we use
* aapt2 - `android-9.0.0_r3`.
* aapt1 - `android-9.0.0_r3`.
* aapt2 - `android-9.0.0_r22`.
* aapt1 - `android-9.0.0_r22`.
### Including our modified `frameworks/base` package.
@ -254,6 +254,7 @@ we lose the ability to quickly build just the aapt binary. So the Windows proced
1. `source build/envsetup.sh`
2. `lunch sdk-eng`
3. `make OUT_DIR=out-x64 LOCAL_MULTILIB=64 USE_NINJA=false aapt`
4. `strip out-x64/host/darwin-x86/bin/aapt_64`
As of Android Oreo (API 26) all aapt binaries are 64 bit (With exception of Windows).
@ -274,7 +275,7 @@ we lose the ability to quickly build just the aapt2 binary. So the Windows proce
1. `export ANDROID_JAVA_HOME=/Path/To/Jdk`
2. `source build/envsetup.sh`
3. `make OUT_DIR=out-x64 LOCAL_MULTILIB=64 USE_NINJA=false aapt2`
4. `strip out-x64/host/darwin-x86/bin/aapt2`
4. `strip out-x64/host/darwin-x86/bin/aapt2_64`
#### Confirming aapt/aapt2 builds are static

View File

@ -36,6 +36,9 @@ import java.util.logging.*;
public class Main {
public static void main(String[] args) throws IOException, InterruptedException, BrutException {
// headless
System.setProperty("java.awt.headless", "true");
// set verbosity default
Verbosity verbosity = Verbosity.NORMAL;
@ -218,12 +221,18 @@ public class Main {
if (cli.hasOption("p") || cli.hasOption("frame-path")) {
apkOptions.frameworkFolderLocation = cli.getOptionValue("p");
}
if (cli.hasOption("nc") || cli.hasOption("no-crunch")) {
apkOptions.noCrunch = true;
}
// Temporary flag to enable the use of aapt2. This will tranform in time to a use-aapt1 flag, which will be
// legacy and eventually removed.
if (cli.hasOption("use-aapt2")) {
apkOptions.useAapt2 = true;
}
if (cli.hasOption("api") || cli.hasOption("api-level")) {
apkOptions.forceApi = Integer.parseInt(cli.getOptionValue("api"));
}
if (cli.hasOption("o") || cli.hasOption("output")) {
outFile = new File(cli.getOptionValue("o"));
} else {
@ -397,6 +406,11 @@ public class Main {
.desc("Copies original AndroidManifest.xml and META-INF. See project page for more info.")
.build();
Option noCrunchOption = Option.builder("nc")
.longOpt("no-crunch")
.desc("Disable crunching of resource files during the build step.")
.build();
Option tagOption = Option.builder("t")
.longOpt("tag")
.desc("Tag frameworks using <tag>.")
@ -439,6 +453,7 @@ public class Main {
BuildOptions.addOption(aaptOption);
BuildOptions.addOption(originalOption);
BuildOptions.addOption(aapt2Option);
BuildOptions.addOption(noCrunchOption);
}
// add global options
@ -492,6 +507,7 @@ public class Main {
allOptions.addOption(verboseOption);
allOptions.addOption(quietOption);
allOptions.addOption(aapt2Option);
allOptions.addOption(noCrunchOption);
}
private static String verbosityHelp() {

View File

@ -416,7 +416,7 @@ public class Androlib {
if (apkOptions.forceBuildAll || isModified(smaliDir, dex)) {
LOGGER.info("Smaling " + folder + " folder into " + filename + "...");
dex.delete();
SmaliBuilder.build(smaliDir, dex, mMinSdkVersion);
SmaliBuilder.build(smaliDir, dex, apkOptions.forceApi > 0 ? apkOptions.forceApi : mMinSdkVersion);
}
return true;
}

View File

@ -377,6 +377,25 @@ public class ApkDecoder {
private void putSdkInfo(MetaInfo meta) throws AndrolibException {
Map<String, String> info = getResTable().getSdkInfo();
if (info.size() > 0) {
String refValue;
if (info.get("minSdkVersion") != null) {
refValue = ResXmlPatcher.pullValueFromIntegers(mOutDir, info.get("minSdkVersion"));
if (refValue != null) {
info.put("minSdkVersion", refValue);
}
}
if (info.get("targetSdkVersion") != null) {
refValue = ResXmlPatcher.pullValueFromIntegers(mOutDir, info.get("targetSdkVersion"));
if (refValue != null) {
info.put("targetSdkVersion", refValue);
}
}
if (info.get("maxSdkVersion") != null) {
refValue = ResXmlPatcher.pullValueFromIntegers(mOutDir, info.get("maxSdkVersion"));
if (refValue != null) {
info.put("maxSdkVersion", refValue);
}
}
meta.sdkInfo = info;
}
}

View File

@ -28,6 +28,8 @@ public class ApkOptions {
public boolean isFramework = false;
public boolean resourcesAreCompressed = false;
public boolean useAapt2 = false;
public boolean noCrunch = false;
public int forceApi = 0;
public Collection<String> doNotCompress;
public String frameworkFolderLocation = null;

View File

@ -0,0 +1,37 @@
/**
* Copyright (C) 2018 Ryszard Wiśniewski <brut.alll@gmail.com>
* Copyright (C) 2018 Connor Tumbleson <connor.tumbleson@gmail.com>
*
* 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
*
* http://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.err;
import brut.androlib.AndrolibException;
public class AXmlDecodingException extends AndrolibException {
public AXmlDecodingException(Throwable cause) {
super(cause);
}
public AXmlDecodingException(String message, Throwable cause) {
super(message, cause);
}
public AXmlDecodingException(String message) {
super(message);
}
public AXmlDecodingException() {
}
}

View File

@ -0,0 +1,37 @@
/**
* Copyright (C) 2018 Ryszard Wiśniewski <brut.alll@gmail.com>
* Copyright (C) 2018 Connor Tumbleson <connor.tumbleson@gmail.com>
*
* 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
*
* http://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.err;
import brut.androlib.AndrolibException;
public class RawXmlEncounteredException extends AndrolibException {
public RawXmlEncounteredException(Throwable cause) {
super(cause);
}
public RawXmlEncounteredException(String message, Throwable cause) {
super(message, cause);
}
public RawXmlEncounteredException(String message) {
super(message);
}
public RawXmlEncounteredException() {
}
}

View File

@ -29,14 +29,14 @@ import org.jf.smali.*;
*/
public class SmaliMod {
public static boolean assembleSmaliFile(String smali, DexBuilder dexBuilder, boolean verboseErrors,
public static boolean assembleSmaliFile(String smali, DexBuilder dexBuilder, int apiLevel, boolean verboseErrors,
boolean printTokens, File smaliFile) throws IOException, RuntimeException, RecognitionException {
InputStream is = new ByteArrayInputStream(smali.getBytes());
return assembleSmaliFile(is, dexBuilder, verboseErrors, printTokens, smaliFile);
return assembleSmaliFile(is, dexBuilder, apiLevel, verboseErrors, printTokens, smaliFile);
}
public static boolean assembleSmaliFile(InputStream is,DexBuilder dexBuilder, boolean verboseErrors,
public static boolean assembleSmaliFile(InputStream is,DexBuilder dexBuilder, int apiLevel, boolean verboseErrors,
boolean printTokens, File smaliFile) throws IOException, RecognitionException {
// copy our filestream into a tmp file, so we don't overwrite
@ -47,10 +47,10 @@ public class SmaliMod {
IOUtils.copy(is, os);
os.close();
return assembleSmaliFile(tmp,dexBuilder, verboseErrors, printTokens);
return assembleSmaliFile(tmp,dexBuilder, apiLevel, verboseErrors, printTokens);
}
public static boolean assembleSmaliFile(File smaliFile,DexBuilder dexBuilder, boolean verboseErrors,
public static boolean assembleSmaliFile(File smaliFile,DexBuilder dexBuilder, int apiLevel, boolean verboseErrors,
boolean printTokens) throws IOException, RecognitionException {
CommonTokenStream tokens;
@ -77,6 +77,7 @@ public class SmaliMod {
}
smaliParser parser = new smaliParser(tokens);
parser.setApiLevel(apiLevel);
parser.setVerboseErrors(verboseErrors);
smaliParser.smali_file_return result = parser.smali_file();
@ -93,7 +94,7 @@ public class SmaliMod {
treeStream.setTokenStream(tokens);
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
dexGen.setApiLevel(apiLevel);
dexGen.setVerboseErrors(verboseErrors);
dexGen.setDexBuilder(dexBuilder);
dexGen.smali_file();

View File

@ -350,6 +350,10 @@ final public class AndrolibResources {
cmd.add("-v");
}
if (apkOptions.noCrunch) {
cmd.add("--no-crunch");
}
try {
OS.exec(cmd.toArray(new String[0]));
LOGGER.fine("aapt2 compile command ran: ");
@ -485,6 +489,9 @@ final public class AndrolibResources {
if (apkOptions.debugMode) { // inject debuggable="true" into manifest
cmd.add("--debug-mode");
}
if (apkOptions.noCrunch) {
cmd.add("--no-crunch");
}
// force package id so that some frameworks build with correct id
// disable if user adds own aapt (can't know if they have this feature)
if (mPackageId != null && ! customAapt && ! mSharedLibrary) {

View File

@ -24,6 +24,11 @@ import java.util.*;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public final class ResTypeSpec {
public static final String RES_TYPE_NAME_ARRAY = "array";
public static final String RES_TYPE_NAME_PLURALS = "plurals";
public static final String RES_TYPE_NAME_STYLES = "style";
private final String mName;
private final Map<String, ResResSpec> mResSpecs = new LinkedHashMap<String, ResResSpec>();

View File

@ -83,6 +83,13 @@ public abstract class ResScalarValue extends ResIntBasedValue implements
}
}
// Android does not allow values (false) for ids.xml anymore
// https://issuetracker.google.com/issues/80475496
// But it decodes as a ResBoolean, which makes no sense. So force it to empty
if (type.equalsIgnoreCase("id") && !body.isEmpty()) {
body = "";
}
// check for using attrib as node or item
String tagName = item ? "item" : type;

View File

@ -19,6 +19,7 @@ package brut.androlib.res.data.value;
import android.util.TypedValue;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResPackage;
import brut.androlib.res.data.ResTypeSpec;
import brut.util.Duo;
/**
@ -41,7 +42,7 @@ public class ResValueFactory {
}
return new ResReferenceValue(mPackage, 0, null);
case TypedValue.TYPE_REFERENCE:
return newReference(value, rawValue);
return newReference(value, null);
case TypedValue.TYPE_ATTRIBUTE:
return newReference(value, rawValue, true);
case TypedValue.TYPE_STRING:
@ -83,7 +84,7 @@ public class ResValueFactory {
return new ResStringValue(value, rawValue);
}
public ResBagValue bagFactory(int parent, Duo<Integer, ResScalarValue>[] items) throws AndrolibException {
public ResBagValue bagFactory(int parent, Duo<Integer, ResScalarValue>[] items, ResTypeSpec resTypeSpec) throws AndrolibException {
ResReferenceValue parentVal = newReference(parent, null);
if (items.length == 0) {
@ -93,14 +94,25 @@ public class ResValueFactory {
if (key == ResAttr.BAG_KEY_ATTR_TYPE) {
return ResAttr.factory(parentVal, items, this, mPackage);
}
// Android O Preview added an unknown enum for ResTable_map. This is hardcoded as 0 for now.
if (key == ResArrayValue.BAG_KEY_ARRAY_START || key == 0) {
String resTypeName = resTypeSpec.getName();
// Android O Preview added an unknown enum for c. This is hardcoded as 0 for now.
if (ResTypeSpec.RES_TYPE_NAME_ARRAY.equals(resTypeName)
|| key == ResArrayValue.BAG_KEY_ARRAY_START || key == 0) {
return new ResArrayValue(parentVal, items);
}
if (key >= ResPluralsValue.BAG_KEY_PLURALS_START && key <= ResPluralsValue.BAG_KEY_PLURALS_END) {
if (ResTypeSpec.RES_TYPE_NAME_PLURALS.equals(resTypeName) ||
(key >= ResPluralsValue.BAG_KEY_PLURALS_START && key <= ResPluralsValue.BAG_KEY_PLURALS_END)) {
return new ResPluralsValue(parentVal, items);
}
return new ResStyleValue(parentVal, items, this);
if (ResTypeSpec.RES_TYPE_NAME_STYLES.equals(resTypeName)) {
return new ResStyleValue(parentVal, items, this);
}
throw new AndrolibException("unsupported res type name for bags. Found: " + resTypeName);
}
public ResReferenceValue newReference(int resID, String rawValue) {

View File

@ -347,7 +347,7 @@ public class ARSCDecoder {
}
}
return factory.bagFactory(parent, items);
return factory.bagFactory(parent, items, mTypeSpec);
}
private ResIntBasedValue readValue() throws IOException, AndrolibException {

View File

@ -852,7 +852,7 @@ public class AXmlResourceParser implements XmlResourceParser {
private final void doNext() throws IOException {
// Delayed initialization.
if (m_strings == null) {
m_reader.skipCheckInt(CHUNK_AXML_FILE);
m_reader.skipCheckInt(CHUNK_AXML_FILE, CHUNK_AXML_FILE_BROKEN);
/*
* chunkSize
@ -1004,7 +1004,7 @@ public class AXmlResourceParser implements XmlResourceParser {
ATTRIBUTE_IX_VALUE_TYPE = 3, ATTRIBUTE_IX_VALUE_DATA = 4,
ATTRIBUTE_LENGTH = 5;
private static final int CHUNK_AXML_FILE = 0x00080003,
private static final int CHUNK_AXML_FILE = 0x00080003, CHUNK_AXML_FILE_BROKEN = 0x00080001,
CHUNK_RESOURCEIDS = 0x00080180, CHUNK_XML_FIRST = 0x00100100,
CHUNK_XML_START_NAMESPACE = 0x00100100,
CHUNK_XML_END_NAMESPACE = 0x00100101,

View File

@ -65,13 +65,21 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom);
int[] xDivs = np.xDivs;
for (int i = 0; i < xDivs.length; i += 2) {
drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]);
if (xDivs.length == 0) {
drawHLine(im2, 0, 1, w);
} else {
for (int i = 0; i < xDivs.length; i += 2) {
drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]);
}
}
int[] yDivs = np.yDivs;
for (int i = 0; i < yDivs.length; i += 2) {
drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]);
if (yDivs.length == 0) {
drawVLine(im2, 0, 1, h);
} else {
for (int i = 0; i < yDivs.length; i += 2) {
drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]);
}
}
// Some images additionally use Optical Bounds

View File

@ -18,6 +18,7 @@ package brut.androlib.res.decoder;
import brut.androlib.AndrolibException;
import brut.androlib.err.CantFind9PatchChunk;
import brut.androlib.err.RawXmlEncounteredException;
import brut.androlib.res.data.ResResource;
import brut.androlib.res.data.value.ResBoolValue;
import brut.androlib.res.data.value.ResFileValue;
@ -118,6 +119,11 @@ public class ResFileDecoder {
}
decode(inDir, inFileName, outDir, outFileName, "xml");
} catch (RawXmlEncounteredException ex) {
// If we got an error to decode XML, lets assume the file is in raw format.
// This is a large assumption, that might increase runtime, but will save us for situations where
// XSD files are AXML`d on aapt1, but left in plaintext in aapt2.
decode(inDir, inFileName, outDir, outFileName, "raw");
} catch (AndrolibException ex) {
LOGGER.log(Level.SEVERE, String.format(
"Could not decode file, replacing by FALSE value: %s",

View File

@ -19,8 +19,9 @@ package brut.androlib.res.decoder;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Logger;
import brut.androlib.err.AXmlDecodingException;
import brut.androlib.err.RawXmlEncounteredException;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.wrapper.XmlPullParserWrapper;
@ -142,9 +143,9 @@ public class XmlPullStreamDecoder implements ResStreamDecoder {
}
ser.flush();
} catch (XmlPullParserException ex) {
throw new AndrolibException("Could not decode XML", ex);
throw new AXmlDecodingException("Could not decode XML", ex);
} catch (IOException ex) {
throw new AndrolibException("Could not decode XML", ex);
throw new RawXmlEncounteredException("Could not decode XML", ex);
}
}
@ -155,6 +156,4 @@ public class XmlPullStreamDecoder implements ResStreamDecoder {
private final XmlPullParser mParser;
private final ExtXmlSerializer mSerial;
private final static Logger LOGGER = Logger.getLogger(XmlPullStreamDecoder.class.getName());
}

View File

@ -197,6 +197,41 @@ public final class ResXmlPatcher {
return null;
}
/**
* Finds key in integers.xml file and returns text value
*
* @param directory Root directory of apk
* @param key Integer reference (ie @integer/foo)
* @return String|null
* @throws AndrolibException
*/
public static String pullValueFromIntegers(File directory, String key) throws AndrolibException {
if (key == null || ! key.contains("@")) {
return null;
}
File file = new File(directory, "/res/values/integers.xml");
key = key.replace("@integer/", "");
if (file.exists()) {
try {
Document doc = loadDocument(file);
XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression expression = xPath.compile("/resources/integer[@name=" + '"' + key + "\"]/text()");
Object result = expression.evaluate(doc, XPathConstants.STRING);
if (result != null) {
return (String) result;
}
} catch (SAXException | ParserConfigurationException | IOException | XPathExpressionException ignored) {
}
}
return null;
}
/**
* Removes attributes like "versionCode" and "versionName" from file.
*

View File

@ -71,7 +71,7 @@ public class SmaliBuilder {
if (fileName.endsWith(".smali")) {
try {
if (!SmaliMod.assembleSmaliFile(inFile, dexBuilder, false, false)) {
if (!SmaliMod.assembleSmaliFile(inFile, dexBuilder, mApiLevel, false, false)) {
throw new AndrolibException("Could not smali file: " + fileName);
}
} catch (IOException | RecognitionException ex) {

View File

@ -198,6 +198,16 @@ public class BuildAndDecodeTest extends BaseTest {
compareXmlFiles("res/xml/references.xml");
}
@Test
public void xmlXsdFileTest() throws BrutException {
compareXmlFiles("res/xml/ww_box_styles_schema.xsd");
}
@Test
public void xmlIdsEmptyTest() throws BrutException {
compareXmlFiles("res/values/ids.xml");
}
@Test
public void xmlReferenceAttributeTest() throws BrutException {
compareXmlFiles("res/layout/issue1040.xml");
@ -231,7 +241,7 @@ public class BuildAndDecodeTest extends BaseTest {
@Test
public void qualifiersTest() throws BrutException {
compareValuesFiles("values-mcc004-mnc4-en-rUS-ldrtl-sw100dp-w200dp-h300dp"
+ "-xlarge-long-round-highdr-land-desk-night-xhdpi-finger-keyssoft-12key"
+ "-long-round-highdr-land-desk-night-xhdpi-finger-keyssoft-12key"
+ "-navhidden-dpad-v26/strings.xml");
}

View File

@ -77,8 +77,8 @@ public class DebugTagRetainedTest extends BaseTest {
String expected = TestUtils.replaceNewlines("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>" +
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" android:compileSdkVersion=\"23\" " +
"android:compileSdkVersionCodename=\"6.0-2438415\" package=\"com.ibotpeaches.issue1235\" platformBuildVersionCode=\"23\" " +
"platformBuildVersionName=\"6.0-2438415\"> <application android:debuggable=\"true\"/></manifest>");
"android:compileSdkVersionCodename=\"6.0-2438415\" package=\"com.ibotpeaches.issue1235\" platformBuildVersionCode=\"20\" " +
"platformBuildVersionName=\"4.4W.2-1537038\"> <application android:debuggable=\"true\"/></manifest>");
byte[] encoded = Files.readAllBytes(Paths.get(sTmpDir + File.separator + apk + File.separator + "AndroidManifest.xml"));
String obtained = TestUtils.replaceNewlines(new String(encoded));

View File

@ -72,7 +72,7 @@ public class ProviderAttributeTest extends BaseTest {
apkDecoder.decode();
String expected = TestUtils.replaceNewlines("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>\n" +
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" android:compileSdkVersion=\"23\" android:compileSdkVersionCodename=\"6.0-2438415\" package=\"com.ibotpeaches.issue636\" platformBuildVersionCode=\"23\" platformBuildVersionName=\"6.0-2438415\">\n" +
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" android:compileSdkVersion=\"23\" android:compileSdkVersionCodename=\"6.0-2438415\" package=\"com.ibotpeaches.issue636\" platformBuildVersionCode=\"22\" platformBuildVersionName=\"5.1-1756733\">\n" +
" <application android:allowBackup=\"true\" android:debuggable=\"true\" android:icon=\"@mipmap/ic_launcher\" android:label=\"@string/app_name\" android:theme=\"@style/AppTheme\">\n" +
" <provider android:authorities=\"com.ibotpeaches.issue636.Provider\" android:exported=\"false\" android:grantUriPermissions=\"true\" android:label=\"@string/app_name\" android:multiprocess=\"false\" android:name=\"com.ibotpeaches.issue636.Provider\"/>\n" +
" <provider android:authorities=\"com.ibotpeaches.issue636.ProviderTwo\" android:exported=\"false\" android:grantUriPermissions=\"true\" android:label=\"@string/app_name\" android:multiprocess=\"false\" android:name=\"com.ibotpeaches.issue636.ProviderTwo\"/>\n" +

View File

@ -84,6 +84,11 @@ public class BuildAndDecodeTest extends BaseTest {
compareXmlFiles("res/navigation/nav_graph.xml");
}
@Test
public void xmlIdsEmptyTest() throws BrutException {
compareXmlFiles("res/values/ids.xml");
}
@Test
public void leadingDollarSignResourceNameTest() throws BrutException {
compareXmlFiles("res/drawable/$avd_hide_password__0.xml");
@ -93,4 +98,9 @@ public class BuildAndDecodeTest extends BaseTest {
public void confirmManifestStructureTest() throws BrutException {
compareXmlFiles("AndroidManifest.xml");
}
@Test
public void xmlXsdFileTest() throws BrutException {
compareXmlFiles("res/xml/ww_box_styles_schema.xsd");
}
}

View File

@ -0,0 +1,55 @@
package brut.androlib.decode;
import brut.androlib.ApkDecoder;
import brut.androlib.BaseTest;
import brut.androlib.TestUtils;
import brut.androlib.res.data.ResTable;
import brut.androlib.res.data.value.ResArrayValue;
import brut.androlib.res.data.value.ResValue;
import brut.common.BrutException;
import brut.directory.ExtFile;
import brut.util.OS;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import static junit.framework.Assert.assertTrue;
public class DecodeArrayTest extends BaseTest {
@BeforeClass
public static void beforeClass() throws Exception {
TestUtils.cleanFrameworkFile();
sTmpDir = new ExtFile(OS.createTempDirectory());
TestUtils.copyResourceDir(MissingVersionManifestTest.class, "decode/issue1994/", sTmpDir);
}
@AfterClass
public static void afterClass() throws BrutException {
OS.rmdir(sTmpDir);
}
@Test
public void decodeStringArray() throws BrutException {
String apk = "issue1994.apk";
ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk));
ResTable resTable = apkDecoder.getResTable();
ResValue value = resTable.getResSpec(0x7f020001).getDefaultResource().getValue();
assertTrue("Not a ResArrayValue. Found: " + value.getClass(), value instanceof ResArrayValue);
}
@Test
public void decodeArray() throws BrutException {
String apk = "issue1994.apk";
ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk));
ResTable resTable = apkDecoder.getResTable();
ResValue value = resTable.getResSpec(0x7f020000).getDefaultResource().getValue();
assertTrue("Not a ResArrayValue. Found: " + value.getClass(), value instanceof ResArrayValue);
}
}

View File

@ -64,7 +64,7 @@ public class ExternalEntityTest extends BaseTest {
String expected = TestUtils.replaceNewlines("<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<manifest android:versionCode=\"1\" android:versionName=\"1.0\" android:compileSdkVersion=\"23\" android:compileSdkVersionCodename=\"6.0-2438415\" " +
"hardwareAccelerated=\"true\" package=\"com.ibotpeaches.doctype\" platformBuildVersionCode=\"23\" platformBuildVersionName=\"6.0-2438415\" " +
"hardwareAccelerated=\"true\" package=\"com.ibotpeaches.doctype\" platformBuildVersionCode=\"24\" platformBuildVersionName=\"6.0-2456767\" " +
"xmlns:android=\"http://schemas.android.com/apk/res/android\"> <supports-screens android:anyDensity=\"true\" android:smallScreens=\"true\" " +
"android:normalScreens=\"true\" android:largeScreens=\"true\" android:resizeable=\"true\" android:xlargeScreens=\"true\" /></manifest>");

View File

@ -0,0 +1,73 @@
/**
* Copyright (C) 2018 Ryszard Wiśniewski <brut.alll@gmail.com>
* Copyright (C) 2018 Connor Tumbleson <connor.tumbleson@gmail.com>
*
* 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
*
* http://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.decode;
import brut.androlib.BaseTest;
import brut.androlib.TestUtils;
import brut.androlib.res.decoder.Res9patchStreamDecoder;
import brut.common.BrutException;
import brut.directory.ExtFile;
import brut.util.OS;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import static org.junit.Assert.*;
public class MissingDiv9PatchTest extends BaseTest {
@BeforeClass
public static void beforeClass() throws Exception {
TestUtils.cleanFrameworkFile();
sTmpDir = new ExtFile(OS.createTempDirectory());
TestUtils.copyResourceDir(MissingDiv9PatchTest.class, "decode/issue1522/", sTmpDir);
}
@AfterClass
public static void afterClass() throws BrutException {
OS.rmdir(sTmpDir);
}
@Test
public void assertMissingDivAdded() throws Exception {
InputStream inputStream = getFileInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Res9patchStreamDecoder decoder = new Res9patchStreamDecoder();
decoder.decode(inputStream, outputStream);
BufferedImage image = ImageIO.read(new ByteArrayInputStream(outputStream.toByteArray()));
int height = image.getHeight() - 1;
// First and last pixel will be invisible, so lets check the first column and ensure its all black
for (int y = 1; y < height; y++) {
assertEquals("y coordinate failed at: " + y, NP_COLOR, image.getRGB(0, y));
}
}
private FileInputStream getFileInputStream() throws IOException {
File file = new File(sTmpDir, "pip_dismiss_scrim.9.png");
return new FileInputStream(file.toPath().toString());
}
private static final int NP_COLOR = 0xff000000;
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<item type="id" name="test_attr1" />
</resources>

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<public type="string" name="hello_world" id="0x7f020000" />
<public type="xml" name="ww_box_styles_schema" id="0x7f1500df" />
</resources>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="test">
<xs:complexType>
<xs:sequence>
<xs:element name="person" type="xs:string"/>
<xs:element name="address">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="address" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<attr format="integer" name="test_attr1"/>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<item type="id" name="test_attr1" />
</resources>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="test">
<xs:complexType>
<xs:sequence>
<xs:element name="person" type="xs:string"/>
<xs:element name="address">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="address" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

View File

@ -35,17 +35,20 @@ public class AaptManager {
File aaptBinary;
String aaptVersion = getAaptBinaryName(version);
if (! OSDetection.is64Bit() && ! OSDetection.isWindows()) {
if (! OSDetection.is64Bit() && OSDetection.isMacOSX()) {
throw new BrutException("32 bit OS detected. No 32 bit binaries available.");
}
// Set the 64 bit flag
aaptVersion += OSDetection.is64Bit() ? "_64" : "";
try {
if (OSDetection.isMacOSX()) {
aaptBinary = Jar.getResourceAsFile("/prebuilt/" + aaptVersion + "/macosx/" + aaptVersion, AaptManager.class);
aaptBinary = Jar.getResourceAsFile("/prebuilt/macosx/" + aaptVersion, AaptManager.class);
} else if (OSDetection.isUnix()) {
aaptBinary = Jar.getResourceAsFile("/prebuilt/" + aaptVersion + "/linux/" + aaptVersion, AaptManager.class);
aaptBinary = Jar.getResourceAsFile("/prebuilt/linux/" + aaptVersion, AaptManager.class);
} else if (OSDetection.isWindows()) {
aaptBinary = Jar.getResourceAsFile("/prebuilt/" + aaptVersion + "/windows/" + aaptVersion + ".exe", AaptManager.class);
aaptBinary = Jar.getResourceAsFile("/prebuilt/windows/" + aaptVersion + ".exe", AaptManager.class);
} else {
throw new BrutException("Could not identify platform: " + OSDetection.returnOS());
}

View File

@ -42,11 +42,11 @@ public class ExtDataInput extends DataInputDelegate {
skipBytes(4);
}
public void skipCheckInt(int expected) throws IOException {
public void skipCheckInt(int expected1, int expected2) throws IOException {
int got = readInt();
if (got != expected) {
if (got != expected1 && got != expected2) {
throw new IOException(String.format(
"Expected: 0x%08x, got: 0x%08x", expected, got));
"Expected: 0x%08x or 0x%08x, got: 0x%08x", expected1, expected2, got));
}
}

View File

@ -107,7 +107,7 @@ subprojects {
ext {
depends = [
baksmali: 'org.smali:baksmali:2.2.5',
baksmali: 'org.smali:baksmali:2.2.6',
commons_cli: 'commons-cli:commons-cli:1.4',
commons_io: 'commons-io:commons-io:2.4',
commons_lang: 'org.apache.commons:commons-lang3:3.1',
@ -115,7 +115,7 @@ subprojects {
junit: 'junit:junit:4.12',
proguard_gradle: 'net.sf.proguard:proguard-gradle:6.0.3',
snakeyaml: 'org.yaml:snakeyaml:1.18:android',
smali: 'org.smali:smali:2.2.5',
smali: 'org.smali:smali:2.2.6',
xmlpull: 'xpp3:xpp3:1.1.4c',
xmlunit: 'xmlunit:xmlunit:1.6',
]

View File

@ -0,0 +1,12 @@
#!/usr/bin/env sh
if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then
export GRADLE_OPTS=-Dorg.gradle.daemon=false
choco install jdk8
export PATH=$PATH:"/c/Program Files/Java/jdk1.8.0_201/bin"
./gradlew.bat clean
else
./gradlew clean
fi
exit $?

View File

@ -1,2 +1,11 @@
#!/usr/bin/env sh
./gradlew build shadowJar
if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then
export GRADLE_OPTS=-Dorg.gradle.daemon=false
export PATH=$PATH:"/c/Program Files/Java/jdk1.8.0_201/bin"
./gradlew.bat build shadowJar proguard
else
./gradlew build shadowJar proguard
fi
exit $?