org.robolectric.res.android.CppApkAssets Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of resources Show documentation
Show all versions of resources Show documentation
An alternative Android testing framework.
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.Util.CHECK;
import static org.robolectric.res.android.ZipFileRO.OpenArchive;
import static org.robolectric.res.android.ZipFileRO.kCompressDeflated;
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;
import org.robolectric.util.PerfStatsCollector;
//
// #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;
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);
// }
// 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) {
return PerfStatsCollector.getInstance()
.measure(
"load binary " + (system ? "framework" : "app") + " resources",
() ->
LoadImpl_measured(
fd, path, idmap_asset, loaded_idmap, system, load_as_shared_library));
}
// 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) {
static CppApkAssets LoadImpl_measured(
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, Asset.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;
}
private static String ErrorCodeString(int result) {
return "Error " + result;
}
public Asset Open(String path, AccessMode mode) {
CHECK(zip_handle_ != 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) {
CHECK(zip_handle_ != null);
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 - 2024 Weber Informatics LLC | Privacy Policy