diff --git a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/AndroidResource.java b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/AndroidResource.java index 92f35bd..f3c34ad 100644 --- a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/AndroidResource.java +++ b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/AndroidResource.java @@ -62,7 +62,7 @@ public Boolean isWatchFaceXml() { public Boolean isDrawable() { return "drawable".equals(resourceType); } - public Boolean isFont() { return resourceType.equals("font"); } + public Boolean isFont() { return "font".equals(resourceType); } public Boolean isAsset() { return "asset".equals(resourceType); } diff --git a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/AndroidResourceLoader.java b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/AndroidResourceLoader.java index 03e48d4..08d189f 100644 --- a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/AndroidResourceLoader.java +++ b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/AndroidResourceLoader.java @@ -13,7 +13,6 @@ import java.io.InputStream; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Objects; @@ -32,33 +31,29 @@ * Where obfuscation has been applied, the mapping is derived, for the creation of the * AndroidResource objects. For example, for a logical resource res/raw/watchface.xml, an APK * may in fact store this as res/aB.xml. The resources.arsc file contains this mapping, and the - * AndroidResourceTable provides a list of resources with their logical types, names and data. + * AndroidResourceLoader provides a stream of resources with their logical types, names and data. *

* Note that more than one AndroidResource object can exist for the given dimensions. For example, * if there is a drawable and a drawable-fr folder, then there may be multiple AndroidResource * entries for drawables with the same type, name and extension. The configuration detail, e.g. * "fr" or "default", is not currently exposed in the AndroidResource objects as it isn't used. */ -public class AndroidResourceTable { +public class AndroidResourceLoader { // Only certain resource types are of interest to the evaluator, notably, not string resources. private static final Set RESOURCE_TYPES = Set.of("raw", "xml", "drawable", "font", "asset"); private static final String RESOURCES_FILE_NAME = "resources.arsc"; - private final List resourceTable; - private AndroidResourceTable(List resources) { - this.resourceTable = resources; - } + private AndroidResourceLoader() { } /** - * Creates the table from a path to an AAB structure on the file system. + * Creates a resource stream from a path to an AAB structure on the file system. * * @param aabPath The path to the root of the AAB directory. - * @return The constructed table. + * @return A stream of Android resource objects. * @throws IOException when the resources file cannot be found, or other IO errors occur. */ static Stream streamFromAabDirectory(Path aabPath) throws IOException { Stream childrenFilesStream = java.nio.file.Files.walk(aabPath); - ArrayList resources = new ArrayList<>(); int relativePathOffset = aabPath.toString().length() + 1; return childrenFilesStream @@ -93,37 +88,12 @@ static Stream streamFromAabDirectory(Path aabPath) throws IOExc } /** - * Creates the table from an APK file. + * Creates a stream of resource objects from the AAB file. * - * @param zipFile The zip file object representing the APK. - * @return The constructed table. - * @throws IOException when the resources file cannot be found, or other IO errors occur. - */ -// static Stream createFromApkFile(ZipFile zipFile) throws IOException { -// ZipEntry arscEntry = new ZipEntry(RESOURCES_FILE_NAME); -// -// AndroidResourceTable table; -// try (InputStream is = zipFile.getInputStream(arscEntry)) { -// Function fn = (Path path) -> { -// try { -// return zipFile.getInputStream(new ZipEntry(path.toString())).readAllBytes(); -// } catch (IOException e) { -// throw new RuntimeException(e); -// } -// }; -// table = createTable(is, fn); -// } -// return table; -// } - - /** - * Creates the table from an APK file. - * - * @param aabZipFile The zip file object representing the APK. - * @return The constructed table. + * @param aabZipFile The zip file object representing the AAB. + * @return A stream of resource objects. */ static Stream streamFromAabFile(ZipFile aabZipFile) { - ArrayList resources = new ArrayList<>(); return aabZipFile.stream() .map( zipEntry -> { @@ -157,14 +127,13 @@ static Stream streamFromAabFile(ZipFile aabZipFile) { } /** - * Creates the table from a base split entry within an archive + * Creates a resource stream from a base split entry within an archive * * @param baseSplitZipStream The zip entry for the base split. - * @return The constructed table. + * @return A stream of resource objects. * @throws IOException when the resources file cannot be found, or other IO errors occur. */ static Stream streamFromMokkaZip(ZipInputStream baseSplitZipStream) throws IOException { - List resources = new ArrayList<>(); Iterator iterator = new Iterator() { @@ -176,13 +145,11 @@ public boolean hasNext() { zipEntry = baseSplitZipStream.getNextEntry(); // Advance over entries in the zip that aren't relevant. while (zipEntry != null && !AndroidResource.isValidResourcePath(zipEntry.getName())) { - System.out.println("Not valid:" + (zipEntry.getName())); zipEntry = baseSplitZipStream.getNextEntry(); } } catch (IOException e) { throw new RuntimeException(e); } - System.out.println("Has next:" + (zipEntry != null)); return zipEntry != null; } @@ -204,26 +171,14 @@ public AndroidResource next() { false); } - /** Read all bytes from an input stream to a new byte array. */ - static byte[] readAllBytes(InputStream inputStream) throws IOException { - int len; - byte[] buffer = new byte[1024]; - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - while ((len = inputStream.read(buffer)) > 0) { - bos.write(buffer, 0, len); - } - return bos.toByteArray(); - } - - /** - * Creates the table from an InputStream. + * Creates a resource stream from an APK zip file. + * + * APK files can have their resources obfuscated, so it is necessary to extract the mapping + * between the original path and the path in the obfuscated zip. * - * @param is The InputStream - * @param fileDataProducer A lambda that returns the raw bytes of a file, given a path, which - * may represent a zip file, or a directory (for example), that is the - * source of those bytes. - * @return The constructed table. + * @param zipFile The APK zip file + * @return A stream of resource objects. * @throws IOException when errors loading resources occur. */ static Stream streamFromApkFile(ZipFile zipFile) throws IOException { @@ -247,29 +202,36 @@ static Stream streamFromApkFile(ZipFile zipFile) throws IOExcep .toList(); return typeChunks.stream() - .flatMap(c -> c.getEntries().values().stream()) - .filter(t -> RESOURCE_TYPES.contains(t.typeName())) - .filter(t -> t.value().type() == BinaryResourceValue.Type.STRING) - .map(entry -> { - Path path = Path.of(stringPool.getString(entry.value().data())); - byte[] data = null; - try { - data = zipFile.getInputStream(new ZipEntry(path.toString())).readAllBytes(); - } catch (IOException e) { - throw new RuntimeException(e); - } + .flatMap(c -> c.getEntries().values().stream()) + .filter(t -> RESOURCE_TYPES.contains(t.typeName())) + .filter(t -> t.value().type() == BinaryResourceValue.Type.STRING) + .map(entry -> { + Path path = Path.of(stringPool.getString(entry.value().data())); + byte[] data = null; + try { + data = zipFile.getInputStream(new ZipEntry(path.toString())).readAllBytes(); + } catch (IOException e) { + throw new RuntimeException(e); + } - return new AndroidResource( - entry.parent().getTypeName(), - entry.key(), - Files.getFileExtension(path.toString()), - path, - data - ); - }); + return new AndroidResource( + entry.parent().getTypeName(), + entry.key(), + Files.getFileExtension(path.toString()), + path, + data + ); + }); } - public List getAllResources() { - return this.resourceTable; + /** Read all bytes from an input stream to a new byte array. */ + static byte[] readAllBytes(InputStream inputStream) throws IOException { + int len; + byte[] buffer = new byte[1024]; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + while ((len = inputStream.read(buffer)) > 0) { + bos.write(buffer, 0, len); + } + return bos.toByteArray(); } -} +} \ No newline at end of file diff --git a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/InputPackage.java b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/InputPackage.java index acc5402..bf500f3 100644 --- a/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/InputPackage.java +++ b/play-validations/memory-footprint/src/main/java/com/google/wear/watchface/dfx/memory/InputPackage.java @@ -95,8 +95,7 @@ static InputPackage openFromAabDirectory(File aabDirectory) { @Override public Stream getWatchFaceFiles() { try { - AndroidResourceTable table = AndroidResourceTable.createFromAabDirectory(rootPath); - return table.getAllResources().stream(); + return AndroidResourceLoader.streamFromAabDirectory(rootPath); } catch (IOException e) { throw new RuntimeException(e); } @@ -118,8 +117,7 @@ static InputPackage openFromApkFile(String apkPath) throws IOException { @Override public Stream getWatchFaceFiles() { try { - AndroidResourceTable table = AndroidResourceTable.createFromApkFile(zipFile); - return table.getAllResources().stream(); + return AndroidResourceLoader.streamFromApkFile(zipFile); } catch (IOException e) { throw new RuntimeException(e); } @@ -145,8 +143,7 @@ static InputPackage openFromAabFile(String aabPath) throws IOException { return new InputPackage() { @Override public Stream getWatchFaceFiles() { - AndroidResourceTable table = AndroidResourceTable.createFromAabFile(zipFile); - return table.getAllResources().stream(); + return AndroidResourceLoader.streamFromAabFile(zipFile); } @Override @@ -183,8 +180,7 @@ static InputPackage openFromMokkaZip(String zipPath) throws IOException { @Override public Stream getWatchFaceFiles() { try { - AndroidResourceTable table = AndroidResourceTable.createFromMokkaZip(baseSplitApkZip); - return table.getAllResources().stream(); + return AndroidResourceLoader.streamFromMokkaZip(baseSplitApkZip); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/play-validations/memory-footprint/src/test/java/com/google/wear/watchface/dfx/memory/DrawableResourceDetailsTest.java b/play-validations/memory-footprint/src/test/java/com/google/wear/watchface/dfx/memory/DrawableResourceDetailsTest.java index 029de1d..79c793f 100644 --- a/play-validations/memory-footprint/src/test/java/com/google/wear/watchface/dfx/memory/DrawableResourceDetailsTest.java +++ b/play-validations/memory-footprint/src/test/java/com/google/wear/watchface/dfx/memory/DrawableResourceDetailsTest.java @@ -241,7 +241,7 @@ Optional getDrawableResourceDetails(String name) throws return fromPackageFile( new InputPackage.PackageFile( FileSystems.getDefault().getPath("res", "drawable", name), - AndroidResourceTable.readAllBytes(is))); + AndroidResourceLoader.readAllBytes(is))); } } }