Shared libraries can now export resources for applications to use. Exporting resources works the same way the framework exports resources, by defining the public symbols in res/values/public.xml. Building a shared library requires aapt to be invoked with the --shared-lib option. Shared libraries will be assigned a package ID of 0x00 at build-time. At runtime, all loaded shared libraries will be assigned a new package ID. Currently, shared libraries should not import other shared libraries, as those dependencies will not be loaded at runtime. At runtime, reflection is used to update the package ID of resource symbols in the shared library's R class file. The package name of the R class file is assumed to be the same as the shared library's package name declared in its manifest. This will be customizable in a future commit. See /tests/SharedLibrary/ for examples of a shared library and its client. Bug:12724178 Change-Id: I60c0cb8ab87849f8f8a1a13431562fe8603020a7
246 lines
7.5 KiB
C++
246 lines
7.5 KiB
C++
#include "idmap.h"
|
|
|
|
#include <UniquePtr.h>
|
|
#include <androidfw/ResourceTypes.h>
|
|
#include <androidfw/StreamingZipInflater.h>
|
|
#include <androidfw/ZipFileRO.h>
|
|
#include <private/android_filesystem_config.h> // for AID_SYSTEM
|
|
#include <utils/SortedVector.h>
|
|
#include <utils/String16.h>
|
|
#include <utils/String8.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#define NO_OVERLAY_TAG (-1000)
|
|
|
|
using namespace android;
|
|
|
|
namespace {
|
|
struct Overlay {
|
|
Overlay() {}
|
|
Overlay(const String8& a, const String8& i, int p) :
|
|
apk_path(a), idmap_path(i), priority(p) {}
|
|
|
|
bool operator<(Overlay const& rhs) const
|
|
{
|
|
// Note: order is reversed by design
|
|
return rhs.priority < priority;
|
|
}
|
|
|
|
String8 apk_path;
|
|
String8 idmap_path;
|
|
int priority;
|
|
};
|
|
|
|
bool writePackagesList(const char *filename, const SortedVector<Overlay>& overlayVector)
|
|
{
|
|
FILE* fout = fopen(filename, "w");
|
|
if (fout == NULL) {
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < overlayVector.size(); ++i) {
|
|
const Overlay& overlay = overlayVector[i];
|
|
fprintf(fout, "%s %s\n", overlay.apk_path.string(), overlay.idmap_path.string());
|
|
}
|
|
|
|
fclose(fout);
|
|
|
|
// Make file world readable since Zygote (running as root) will read
|
|
// it when creating the initial AssetManger object
|
|
const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 0644
|
|
if (chmod(filename, mode) == -1) {
|
|
unlink(filename);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
String8 flatten_path(const char *path)
|
|
{
|
|
String16 tmp(path);
|
|
tmp.replaceAll('/', '@');
|
|
return String8(tmp);
|
|
}
|
|
|
|
int mkdir_p(const String8& path, uid_t uid, gid_t gid)
|
|
{
|
|
static const mode_t mode =
|
|
S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH;
|
|
struct stat st;
|
|
|
|
if (stat(path.string(), &st) == 0) {
|
|
return 0;
|
|
}
|
|
if (mkdir_p(path.getPathDir(), uid, gid) < 0) {
|
|
return -1;
|
|
}
|
|
if (mkdir(path.string(), 0755) != 0) {
|
|
return -1;
|
|
}
|
|
if (chown(path.string(), uid, gid) == -1) {
|
|
return -1;
|
|
}
|
|
if (chmod(path.string(), mode) == -1) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name)
|
|
{
|
|
const size_t N = parser.getAttributeCount();
|
|
String16 target;
|
|
int priority = -1;
|
|
for (size_t i = 0; i < N; ++i) {
|
|
size_t len;
|
|
String16 key(parser.getAttributeName(i, &len));
|
|
if (key == String16("targetPackage")) {
|
|
const uint16_t *p = parser.getAttributeStringValue(i, &len);
|
|
if (p) {
|
|
target = String16(p, len);
|
|
}
|
|
} else if (key == String16("priority")) {
|
|
Res_value v;
|
|
if (parser.getAttributeValue(i, &v) == sizeof(Res_value)) {
|
|
priority = v.data;
|
|
if (priority < 0 || priority > 9999) {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (target == String16(target_package_name)) {
|
|
return priority;
|
|
}
|
|
return NO_OVERLAY_TAG;
|
|
}
|
|
|
|
int parse_manifest(const void *data, size_t size, const char *target_package_name)
|
|
{
|
|
ResXMLTree parser;
|
|
parser.setTo(data, size);
|
|
if (parser.getError() != NO_ERROR) {
|
|
ALOGD("%s failed to init xml parser, error=0x%08x\n", __FUNCTION__, parser.getError());
|
|
return -1;
|
|
}
|
|
|
|
ResXMLParser::event_code_t type;
|
|
do {
|
|
type = parser.next();
|
|
if (type == ResXMLParser::START_TAG) {
|
|
size_t len;
|
|
String16 tag(parser.getElementName(&len));
|
|
if (tag == String16("overlay")) {
|
|
return parse_overlay_tag(parser, target_package_name);
|
|
}
|
|
}
|
|
} while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
|
|
|
|
return NO_OVERLAY_TAG;
|
|
}
|
|
|
|
int parse_apk(const char *path, const char *target_package_name)
|
|
{
|
|
UniquePtr<ZipFileRO> zip(ZipFileRO::open(path));
|
|
if (zip.get() == NULL) {
|
|
ALOGW("%s: failed to open zip %s\n", __FUNCTION__, path);
|
|
return -1;
|
|
}
|
|
ZipEntryRO entry;
|
|
if ((entry = zip->findEntryByName("AndroidManifest.xml")) == NULL) {
|
|
ALOGW("%s: failed to find entry AndroidManifest.xml\n", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
size_t uncompLen = 0;
|
|
int method;
|
|
if (!zip->getEntryInfo(entry, &method, &uncompLen, NULL, NULL, NULL, NULL)) {
|
|
ALOGW("%s: failed to read entry info\n", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
if (method != ZipFileRO::kCompressDeflated) {
|
|
ALOGW("%s: cannot handle zip compression method %d\n", __FUNCTION__, method);
|
|
return -1;
|
|
}
|
|
FileMap *dataMap = zip->createEntryFileMap(entry);
|
|
if (!dataMap) {
|
|
ALOGW("%s: failed to create FileMap\n", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
char *buf = new char[uncompLen];
|
|
if (NULL == buf) {
|
|
ALOGW("%s: failed to allocate %d byte\n", __FUNCTION__, uncompLen);
|
|
dataMap->release();
|
|
return -1;
|
|
}
|
|
StreamingZipInflater inflater(dataMap, uncompLen);
|
|
if (inflater.read(buf, uncompLen) < 0) {
|
|
ALOGW("%s: failed to inflate %d byte\n", __FUNCTION__, uncompLen);
|
|
delete[] buf;
|
|
dataMap->release();
|
|
return -1;
|
|
}
|
|
|
|
int priority = parse_manifest(buf, uncompLen, target_package_name);
|
|
delete[] buf;
|
|
dataMap->release();
|
|
return priority;
|
|
}
|
|
}
|
|
|
|
int idmap_scan(const char *overlay_dir, const char *target_package_name,
|
|
const char *target_apk_path, const char *idmap_dir)
|
|
{
|
|
String8 filename = String8(idmap_dir);
|
|
filename.appendPath("overlays.list");
|
|
if (unlink(filename.string()) != 0 && errno != ENOENT) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
DIR *dir = opendir(overlay_dir);
|
|
if (dir == NULL) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
SortedVector<Overlay> overlayVector;
|
|
struct dirent *dirent;
|
|
while ((dirent = readdir(dir)) != NULL) {
|
|
struct stat st;
|
|
char overlay_apk_path[PATH_MAX + 1];
|
|
snprintf(overlay_apk_path, PATH_MAX, "%s/%s", overlay_dir, dirent->d_name);
|
|
if (stat(overlay_apk_path, &st) < 0) {
|
|
continue;
|
|
}
|
|
if (!S_ISREG(st.st_mode)) {
|
|
continue;
|
|
}
|
|
|
|
int priority = parse_apk(overlay_apk_path, target_package_name);
|
|
if (priority < 0) {
|
|
continue;
|
|
}
|
|
|
|
String8 idmap_path(idmap_dir);
|
|
idmap_path.appendPath(flatten_path(overlay_apk_path + 1));
|
|
idmap_path.append("@idmap");
|
|
|
|
if (idmap_create_path(target_apk_path, overlay_apk_path, idmap_path.string()) != 0) {
|
|
ALOGE("error: failed to create idmap for target=%s overlay=%s idmap=%s\n",
|
|
target_apk_path, overlay_apk_path, idmap_path.string());
|
|
continue;
|
|
}
|
|
|
|
Overlay overlay(String8(overlay_apk_path), idmap_path, priority);
|
|
overlayVector.add(overlay);
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
if (!writePackagesList(filename.string(), overlayVector)) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|