
org.robolectric.res.android.CppApkAssets Maven / Gradle / Ivy
package org.robolectric.res.android;
// transliterated from
// https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/include/androidfw/ApkAssets.h
// and
// https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/ApkAssets.cpp
import static org.robolectric.res.android.CppAssetManager.FileType.kFileTypeDirectory;
import static org.robolectric.res.android.CppAssetManager.FileType.kFileTypeRegular;
import static org.robolectric.res.android.ZipFileRO.OpenArchive;
import static org.robolectric.res.android.ZipFileRO.kCompressDeflated;
import com.google.common.io.ByteStreams;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.zip.ZipEntry;
import org.robolectric.res.android.Asset.AccessMode;
import org.robolectric.res.android.CppAssetManager.FileType;
import org.robolectric.res.android.Idmap.LoadedIdmap;
import org.robolectric.res.android.ZipFileRO.ZipEntryRO;
//
// #ifndef APKASSETS_H_
// #define APKASSETS_H_
//
// #include
// #include
//
// #include "android-base/macros.h"
// #include "ziparchive/zip_archive.h"
//
// #include "androidfw/Asset.h"
// #include "androidfw/LoadedArsc.h"
// #include "androidfw/misc.h"
//
// namespace android {
//
// // Holds an APK.
@SuppressWarnings("NewApi")
public class CppApkAssets {
private static final String kResourcesArsc = "resources.arsc";
// public:
// static std::unique_ptr Load(const String& path, bool system = false);
// static std::unique_ptr LoadAsSharedLibrary(const String& path,
// bool system = false);
//
// std::unique_ptr Open(const String& path,
// Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const;
//
// bool ForEachFile(const String& path,
// const std::function& f) const;
CppApkAssets() {
this.zipFileRO = null;
}
public CppApkAssets(ZipArchiveHandle zip_handle_, String path_) {
this.zip_handle_ = zip_handle_;
this.path_ = path_;
this.zipFileRO = new ZipFileRO(zip_handle_, zip_handle_.zipFile.getName());
}
public String GetPath() {
return path_;
}
// This is never nullptr.
public LoadedArsc GetLoadedArsc() {
return loaded_arsc_;
}
// private:
// DISALLOW_COPY_AND_ASSIGN(ApkAssets);
//
// static std::unique_ptr LoadImpl(const String& path, bool system,
// bool load_as_shared_library);
//
// ApkAssets() = default;
//
// struct ZipArchivePtrCloser {
// void operator()(::ZipArchiveHandle handle) { ::CloseArchive(handle); }
// };
//
// using ZipArchivePtr =
// std::unique_ptr::type,
// ZipArchivePtrCloser>;
ZipArchiveHandle zip_handle_;
private final ZipFileRO zipFileRO;
private String path_;
Asset resources_asset_;
Asset idmap_asset_;
private LoadedArsc loaded_arsc_;
// };
//
// } // namespace android
//
// #endif // APKASSETS_H_
//
// #define ATRACE_TAG ATRACE_TAG_RESOURCES
//
// #include "androidfw/ApkAssets.h"
//
// #include
//
// #include "android-base/logging.h"
// #include "utils/FileMap.h"
// #include "utils/Trace.h"
// #include "ziparchive/zip_archive.h"
//
// #include "androidfw/Asset.h"
// #include "androidfw/Util.h"
//
// namespace android {
//
// Creates an ApkAssets.
// If `system` is true, the package is marked as a system package, and allows some functions to
// filter out this package when computing what configurations/resources are available.
// std::unique_ptr ApkAssets::Load(const String& path, bool system) {
public static CppApkAssets Load(String path, boolean system) {
return LoadImpl(/*{}*/ -1 /*fd*/, path, null, null, system, false /*load_as_shared_library*/);
}
// Creates an ApkAssets, but forces any package with ID 0x7f to be loaded as a shared library.
// If `system` is true, the package is marked as a system package, and allows some functions to
// filter out this package when computing what configurations/resources are available.
// std::unique_ptr ApkAssets::LoadAsSharedLibrary(const String& path,
// bool system) {
public static CppApkAssets LoadAsSharedLibrary(String path, boolean system) {
return LoadImpl(/*{}*/ -1 /*fd*/, path, null, null, system, true /*load_as_shared_library*/);
}
// Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay
// data.
// If `system` is true, the package is marked as a system package, and allows some functions to
// filter out this package when computing what configurations/resources are available.
// std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap_path,
// bool system) {
@SuppressWarnings("DoNotCallSuggester")
public static CppApkAssets LoadOverlay(String idmap_path, boolean system) {
throw new UnsupportedOperationException();
// Asset idmap_asset = CreateAssetFromFile(idmap_path);
// if (idmap_asset == null) {
// return {};
// }
//
// StringPiece idmap_data(
// reinterpret_cast(idmap_asset.getBuffer(true /*wordAligned*/)),
// static_cast(idmap_asset.getLength()));
// LoadedIdmap loaded_idmap = LoadedIdmap.Load(idmap_data);
// if (loaded_idmap == null) {
// System.err.println( + "failed to load IDMAP " + idmap_path;
// return {};
// }
// return LoadImpl({} /*fd*/, loaded_idmap.OverlayApkPath(), std.move(idmap_asset),
// std.move(loaded_idmap), system, false /*load_as_shared_library*/);
}
// Creates an ApkAssets from the given file descriptor, and takes ownership of the file
// descriptor. The `friendly_name` is some name that will be used to identify the source of
// this ApkAssets in log messages and other debug scenarios.
// If `system` is true, the package is marked as a system package, and allows some functions to
// filter out this package when computing what configurations/resources are available.
// If `force_shared_lib` is true, any package with ID 0x7f is loaded as a shared library.
// std::unique_ptr ApkAssets::LoadFromFd(unique_fd fd,
// const std::string& friendly_name,
// bool system, bool force_shared_lib) {
// public static ApkAssets LoadFromFd(unique_fd fd,
// String friendly_name,
// boolean system, boolean force_shared_lib) {
// return LoadImpl(std.move(fd), friendly_name, null /*idmap_asset*/, null /*loaded_idmap*/,
// system, force_shared_lib);
// }
// Creates an ApkAssets of the format ARSC from the given file descriptor, and takes ownership of
// the file descriptor.
public static CppApkAssets loadArscFromFd(FileDescriptor fd) {
CppApkAssets loadedApk = new CppApkAssets();
try {
byte[] bytes = ByteStreams.toByteArray(new FileInputStream(fd));
StringPiece data = new StringPiece(ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN), 0);
loadedApk.loaded_arsc_ = LoadedArsc.Load(data, null, false, false);
} catch (IOException e) {
// logError("Error loading assets from fd: " + e.getLocalizedMessage());
return null;
}
return loadedApk;
}
// std::unique_ptr ApkAssets::CreateAssetFromFile(const std::string& path) {
@SuppressWarnings("DoNotCallSuggester")
static Asset CreateAssetFromFile(String path) {
throw new UnsupportedOperationException();
// unique_fd fd(base.utf8.open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
// if (fd == -1) {
// System.err.println( + "Failed to open file '" + path + "': " +
// SystemErrorCodeToString(errno);
// return {};
// }
//
// long file_len = lseek64(fd, 0, SEEK_END);
// if (file_len < 0) {
// System.err.println( + "Failed to get size of file '" + path + "': " +
// SystemErrorCodeToString(errno);
// return {};
// }
//
// std.unique_ptr file_map = util.make_unique();
// if (!file_map.create(path.c_str(), fd, 0, static_cast(file_len), true /*readOnly*/))
// {
// System.err.println( + "Failed to mmap file '" + path + "': " +
// SystemErrorCodeToString(errno);
// return {};
// }
// return Asset.createFromUncompressedMap(std.move(file_map), Asset.AccessMode.ACCESS_RANDOM);
}
/** Measure performance implications of loading {@link CppApkAssets}. */
static CppApkAssets LoadImpl(
int fd,
String path,
Asset idmap_asset,
LoadedIdmap loaded_idmap,
boolean system,
boolean load_as_shared_library) {
Ref unmanaged_handle = new Ref<>(null);
int result;
if (fd >= 0) {
throw new UnsupportedOperationException();
// result =
// OpenArchiveFd(fd.release(), path, &unmanaged_handle, true /*assume_ownership*/);
} else {
result = OpenArchive(path, unmanaged_handle);
}
if (result != 0) {
System.err.println("Failed to open APK '" + path + "' " + ErrorCodeString(result));
return null;
}
// Wrap the handle in a unique_ptr so it gets automatically closed.
CppApkAssets loaded_apk = new CppApkAssets(unmanaged_handle.get(), path);
// Find the resource table.
String entry_name = kResourcesArsc;
Ref entry = new Ref<>(null);
// result = FindEntry(loaded_apk.zip_handle_.get(), entry_name, &entry);
result = ZipFileRO.FindEntry(loaded_apk.zip_handle_, entry_name, entry);
if (result != 0) {
// There is no resources.arsc, so create an empty LoadedArsc and return.
loaded_apk.loaded_arsc_ = LoadedArsc.CreateEmpty();
return loaded_apk;
}
// Open the resource table via mmap unless it is compressed. This logic is taken care of by
// Open.
loaded_apk.resources_asset_ = loaded_apk.Open(kResourcesArsc, AccessMode.ACCESS_BUFFER);
if (loaded_apk.resources_asset_ == null) {
System.err.println("Failed to open '" + kResourcesArsc + "' in APK '" + path + "'.");
return null;
}
// Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain
// valid.
loaded_apk.idmap_asset_ = idmap_asset;
// const StringPiece data(
// reinterpret_cast(loaded_apk.resources_asset_.getBuffer(true
// /*wordAligned*/)),
// loaded_apk.resources_asset_.getLength());
StringPiece data =
new StringPiece(
ByteBuffer.wrap(loaded_apk.resources_asset_.getBuffer(true /*wordAligned*/))
.order(ByteOrder.LITTLE_ENDIAN),
0 /*(int) loaded_apk.resources_asset_.getLength()*/);
loaded_apk.loaded_arsc_ = LoadedArsc.Load(data, loaded_idmap, system, load_as_shared_library);
if (loaded_apk.loaded_arsc_ == null) {
System.err.println("Failed to load '" + kResourcesArsc + "' in APK '" + path + "'.");
return null;
}
// Need to force a move for mingw32.
return loaded_apk;
}
// std::unique_ptr ApkAssets::LoadImpl(
// unique_fd fd, const std::string& path, std::unique_ptr idmap_asset,
// std::unique_ptr loaded_idmap, bool system, bool load_as_shared_library)
// {
private static String ErrorCodeString(int result) {
return "Error " + result;
}
public Asset Open(String path, AccessMode mode) {
if (zip_handle_ == null || zipFileRO == null) {
// In this case, the ApkAssets was loaded from a pure ARSC, and does not have assets.
return null;
}
String name = path;
ZipEntryRO entry;
entry = zipFileRO.findEntryByName(name);
// int result = FindEntry(zip_handle_.get(), name, &entry);
// if (result != 0) {
// LOG(ERROR) + "No entry '" + path + "' found in APK '" + path_ + "'";
// return {};
// }
if (entry == null) {
return null;
}
if (entry.entry.getMethod() == kCompressDeflated) {
// FileMap map = new FileMap();
// if (!map.create(path_, .GetFileDescriptor(zip_handle_), entry.offset,
// entry.getCompressedSize(), true /*readOnly*/)) {
// LOG(ERROR) + "Failed to mmap file '" + path + "' in APK '" + path_ + "'";
// return {};
// }
FileMap map = zipFileRO.createEntryFileMap(entry);
Asset asset = Asset.createFromCompressedMap(map, (int) entry.entry.getSize(), mode);
if (asset == null) {
System.err.println("Failed to decompress '" + path + "'.");
return null;
}
return asset;
} else {
FileMap map = zipFileRO.createEntryFileMap(entry);
// if (!map.create(path_, .GetFileDescriptor(zip_handle_.get()), entry.offset,
// entry.uncompressed_length, true /*readOnly*/)) {
// System.err.println("Failed to mmap file '" + path + "' in APK '" + path_ + "'");
// return null;
// }
Asset asset = Asset.createFromUncompressedMap(map, mode);
if (asset == null) {
System.err.println("Failed to mmap file '" + path + "' in APK '" + path_ + "'");
return null;
}
return asset;
}
}
interface ForEachFileCallback {
void callback(String string, FileType fileType);
}
boolean ForEachFile(String root_path, ForEachFileCallback f) {
if (zip_handle_ == null || zipFileRO == null) {
// In this case, the ApkAssets was loaded from a pure ARSC, and does not have assets.
return false;
}
String root_path_full = root_path;
// if (root_path_full.back() != '/') {
if (!root_path_full.endsWith("/")) {
root_path_full += '/';
}
String prefix = root_path_full;
Enumeration extends ZipEntry> entries = zip_handle_.zipFile.entries();
// if (StartIteration(zip_handle_.get(), &cookie, &prefix, null) != 0) {
// return false;
// }
if (!entries.hasMoreElements()) {
return false;
}
// String name;
// ZipEntry entry;
// We need to hold back directories because many paths will contain them and we want to only
// surface one.
final Set dirs = new HashSet<>();
// int32_t result;
// while ((result = Next(cookie, &entry, &name)) == 0) {
while (entries.hasMoreElements()) {
ZipEntry zipEntry = entries.nextElement();
if (!zipEntry.getName().startsWith(prefix)) {
continue;
}
// StringPiece full_file_path(reinterpret_cast(name.name), name.name_length);
String full_file_path = zipEntry.getName();
// StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
String leaf_file_path = full_file_path.substring(root_path_full.length());
if (!leaf_file_path.isEmpty()) {
// auto iter = stdfind(leaf_file_path.begin(), leaf_file_path.end(), '/');
// if (iter != leaf_file_path.end()) {
// stdstring dir =
// leaf_file_path.substr(0, stddistance(leaf_file_path.begin(), iter)).to_string();
// dirs.insert(stdmove(dir));
if (zipEntry.isDirectory()) {
dirs.add(leaf_file_path.substring(0, leaf_file_path.indexOf("/")));
} else {
f.callback(leaf_file_path, kFileTypeRegular);
}
}
}
// EndIteration(cookie);
// Now present the unique directories.
for (final String dir : dirs) {
f.callback(dir, kFileTypeDirectory);
}
// -1 is end of iteration, anything else is an error.
// return result == -1;
return true;
}
//
} // namespace android
© 2015 - 2025 Weber Informatics LLC | Privacy Policy