Merge changes I0539656f,I4c9f29da,Iac679828 into sc-dev
* changes: OverlayManager Fabricated RROs Add fabricated RRO generation to libidmap2 Query overlays using package and name
This commit is contained in:
committed by
Android (Google) Code Review
commit
1c319ea03f
@@ -369,6 +369,7 @@ filegroup {
|
||||
":framework_native_aidl",
|
||||
":gatekeeper_aidl",
|
||||
":gsiservice_aidl",
|
||||
":idmap2_core_aidl",
|
||||
":incidentcompanion_aidl",
|
||||
":inputconstants_aidl",
|
||||
":installd_aidl",
|
||||
|
||||
@@ -51,13 +51,18 @@ cc_library {
|
||||
static: {
|
||||
enabled: false,
|
||||
},
|
||||
static_libs: [
|
||||
"libidmap2_protos",
|
||||
],
|
||||
shared_libs: [
|
||||
"libandroidfw",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libutils",
|
||||
"libziparchive",
|
||||
"libidmap2_policies",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libutils",
|
||||
"libz",
|
||||
"libziparchive",
|
||||
],
|
||||
},
|
||||
host: {
|
||||
@@ -68,14 +73,29 @@ cc_library {
|
||||
"libandroidfw",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libutils",
|
||||
"libziparchive",
|
||||
"libidmap2_policies",
|
||||
"libidmap2_protos",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libutils",
|
||||
"libz",
|
||||
"libziparchive",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libidmap2_protos",
|
||||
srcs: [
|
||||
"libidmap2/proto/*.proto",
|
||||
],
|
||||
host_supported: true,
|
||||
proto: {
|
||||
type: "lite",
|
||||
export_proto_headers: true,
|
||||
},
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libidmap2_policies",
|
||||
defaults: [
|
||||
@@ -88,9 +108,6 @@ cc_library {
|
||||
enabled: true,
|
||||
},
|
||||
android: {
|
||||
static: {
|
||||
enabled: false,
|
||||
},
|
||||
shared_libs: [
|
||||
"libandroidfw",
|
||||
],
|
||||
@@ -119,6 +136,7 @@ cc_test {
|
||||
srcs: [
|
||||
"tests/BinaryStreamVisitorTests.cpp",
|
||||
"tests/CommandLineOptionsTests.cpp",
|
||||
"tests/FabricatedOverlayTests.cpp",
|
||||
"tests/FileUtilsTests.cpp",
|
||||
"tests/Idmap2BinaryTests.cpp",
|
||||
"tests/IdmapTests.cpp",
|
||||
@@ -130,20 +148,27 @@ cc_test {
|
||||
"tests/ResourceUtilsTests.cpp",
|
||||
"tests/ResultTests.cpp",
|
||||
"tests/XmlParserTests.cpp",
|
||||
"tests/ZipFileTests.cpp",
|
||||
],
|
||||
static_libs: ["libgmock"],
|
||||
required: [
|
||||
"idmap2",
|
||||
],
|
||||
static_libs: [
|
||||
"libgmock",
|
||||
"libidmap2_protos",
|
||||
],
|
||||
target: {
|
||||
android: {
|
||||
shared_libs: [
|
||||
"libandroidfw",
|
||||
"libbase",
|
||||
"libidmap2",
|
||||
"libidmap2_policies",
|
||||
"liblog",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libutils",
|
||||
"libz",
|
||||
"libz",
|
||||
"libziparchive",
|
||||
"libidmap2_policies",
|
||||
],
|
||||
},
|
||||
host: {
|
||||
@@ -152,10 +177,11 @@ cc_test {
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libidmap2",
|
||||
"libidmap2_policies",
|
||||
"liblog",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libutils",
|
||||
"libziparchive",
|
||||
"libidmap2_policies",
|
||||
],
|
||||
shared_libs: [
|
||||
"libz",
|
||||
@@ -189,6 +215,9 @@ cc_binary {
|
||||
"idmap2/Lookup.cpp",
|
||||
"idmap2/Main.cpp",
|
||||
],
|
||||
static_libs: [
|
||||
"libidmap2_protos",
|
||||
],
|
||||
target: {
|
||||
android: {
|
||||
shared_libs: [
|
||||
@@ -196,9 +225,11 @@ cc_binary {
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libidmap2",
|
||||
"libutils",
|
||||
"libziparchive",
|
||||
"libidmap2_policies",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libutils",
|
||||
"libz",
|
||||
"libziparchive",
|
||||
],
|
||||
},
|
||||
host: {
|
||||
@@ -207,10 +238,11 @@ cc_binary {
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libidmap2",
|
||||
"libidmap2_policies",
|
||||
"liblog",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libutils",
|
||||
"libziparchive",
|
||||
"libidmap2_policies",
|
||||
],
|
||||
shared_libs: [
|
||||
"libz",
|
||||
@@ -236,11 +268,14 @@ cc_binary {
|
||||
"libbinder",
|
||||
"libcutils",
|
||||
"libidmap2",
|
||||
"libidmap2_policies",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libutils",
|
||||
"libziparchive",
|
||||
"libidmap2_policies",
|
||||
],
|
||||
static_libs: [
|
||||
"libc++fs",
|
||||
"libidmap2_protos",
|
||||
"libidmap2daidl",
|
||||
],
|
||||
init_rc: ["idmap2d/idmap2d.rc"],
|
||||
@@ -248,28 +283,41 @@ cc_binary {
|
||||
|
||||
cc_library_static {
|
||||
name: "libidmap2daidl",
|
||||
defaults: [
|
||||
"idmap2_defaults",
|
||||
],
|
||||
tidy: false,
|
||||
host_supported: false,
|
||||
srcs: [
|
||||
":idmap2_aidl",
|
||||
":idmap2_core_aidl",
|
||||
],
|
||||
header_libs: [
|
||||
"libbinder_headers",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
],
|
||||
aidl: {
|
||||
export_aidl_headers: true,
|
||||
local_include_dirs: [
|
||||
"idmap2d/aidl/core",
|
||||
"idmap2d/aidl/services/",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "idmap2_core_aidl",
|
||||
srcs: [
|
||||
"idmap2d/aidl/core/android/os/FabricatedOverlayInternal.aidl",
|
||||
"idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl",
|
||||
"idmap2d/aidl/core/android/os/FabricatedOverlayInfo.aidl",
|
||||
],
|
||||
path: "idmap2d/aidl/core/",
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "idmap2_aidl",
|
||||
srcs: [
|
||||
"idmap2d/aidl/android/os/IIdmap2.aidl",
|
||||
"idmap2d/aidl/services/android/os/IIdmap2.aidl",
|
||||
],
|
||||
path: "idmap2d/aidl",
|
||||
path: "idmap2d/aidl/services/",
|
||||
}
|
||||
|
||||
aidl_interface {
|
||||
@@ -281,7 +329,7 @@ aidl_interface {
|
||||
filegroup {
|
||||
name: "overlayable_policy_aidl_files",
|
||||
srcs: [
|
||||
"idmap2d/aidl/android/os/OverlayablePolicy.aidl",
|
||||
"idmap2d/aidl/services/android/os/OverlayablePolicy.aidl",
|
||||
],
|
||||
path: "idmap2d/aidl",
|
||||
path: "idmap2d/aidl/services/",
|
||||
}
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
|
||||
using android::idmap2::Error;
|
||||
using android::idmap2::IdmapHeader;
|
||||
using android::idmap2::OverlayResourceContainer;
|
||||
using android::idmap2::Result;
|
||||
using android::idmap2::TargetResourceContainer;
|
||||
using android::idmap2::Unit;
|
||||
|
||||
Result<Unit> Verify(const std::string& idmap_path, const std::string& target_path,
|
||||
@@ -39,11 +41,20 @@ Result<Unit> Verify(const std::string& idmap_path, const std::string& target_pat
|
||||
return Error("failed to parse idmap header");
|
||||
}
|
||||
|
||||
const auto header_ok = header->IsUpToDate(target_path, overlay_path, overlay_name,
|
||||
fulfilled_policies, enforce_overlayable);
|
||||
auto target = TargetResourceContainer::FromPath(target_path);
|
||||
if (!target) {
|
||||
return Error("failed to load target '%s'", target_path.c_str());
|
||||
}
|
||||
|
||||
auto overlay = OverlayResourceContainer::FromPath(overlay_path);
|
||||
if (!overlay) {
|
||||
return Error("failed to load overlay '%s'", overlay_path.c_str());
|
||||
}
|
||||
|
||||
const auto header_ok = header->IsUpToDate(**target, **overlay, overlay_name, fulfilled_policies,
|
||||
enforce_overlayable);
|
||||
if (!header_ok) {
|
||||
return Error(header_ok.GetError(), "idmap not up to date");
|
||||
}
|
||||
|
||||
return Unit{};
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "androidfw/ResourceTypes.h"
|
||||
@@ -31,12 +30,13 @@
|
||||
#include "idmap2/PolicyUtils.h"
|
||||
#include "idmap2/SysTrace.h"
|
||||
|
||||
using android::ApkAssets;
|
||||
using android::idmap2::BinaryStreamVisitor;
|
||||
using android::idmap2::CommandLineOptions;
|
||||
using android::idmap2::Error;
|
||||
using android::idmap2::Idmap;
|
||||
using android::idmap2::OverlayResourceContainer;
|
||||
using android::idmap2::Result;
|
||||
using android::idmap2::TargetResourceContainer;
|
||||
using android::idmap2::Unit;
|
||||
using android::idmap2::utils::kIdmapFilePermissionMask;
|
||||
using android::idmap2::utils::PoliciesToBitmaskResult;
|
||||
@@ -93,18 +93,18 @@ Result<Unit> Create(const std::vector<std::string>& args) {
|
||||
fulfilled_policies |= PolicyFlags::PUBLIC;
|
||||
}
|
||||
|
||||
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
|
||||
if (!target_apk) {
|
||||
return Error("failed to load apk %s", target_apk_path.c_str());
|
||||
const auto target = TargetResourceContainer::FromPath(target_apk_path);
|
||||
if (!target) {
|
||||
return Error("failed to load target '%s'", target_apk_path.c_str());
|
||||
}
|
||||
|
||||
const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
|
||||
if (!overlay_apk) {
|
||||
return Error("failed to load apk %s", overlay_apk_path.c_str());
|
||||
const auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
|
||||
if (!overlay) {
|
||||
return Error("failed to load apk overlay '%s'", overlay_apk_path.c_str());
|
||||
}
|
||||
|
||||
const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, overlay_name,
|
||||
fulfilled_policies, !ignore_overlayable);
|
||||
const auto idmap = Idmap::FromContainers(**target, **overlay, overlay_name, fulfilled_policies,
|
||||
!ignore_overlayable);
|
||||
if (!idmap) {
|
||||
return Error(idmap.GetError(), "failed to create idmap");
|
||||
}
|
||||
@@ -112,13 +112,14 @@ Result<Unit> Create(const std::vector<std::string>& args) {
|
||||
umask(kIdmapFilePermissionMask);
|
||||
std::ofstream fout(idmap_path);
|
||||
if (fout.fail()) {
|
||||
return Error("failed to open idmap path %s", idmap_path.c_str());
|
||||
return Error("failed to open idmap path '%s'", idmap_path.c_str());
|
||||
}
|
||||
|
||||
BinaryStreamVisitor visitor(fout);
|
||||
(*idmap)->accept(&visitor);
|
||||
fout.close();
|
||||
if (fout.fail()) {
|
||||
return Error("failed to write to idmap path %s", idmap_path.c_str());
|
||||
return Error("failed to write to idmap path '%s'", idmap_path.c_str());
|
||||
}
|
||||
|
||||
return Unit{};
|
||||
|
||||
@@ -34,13 +34,14 @@
|
||||
#include "idmap2/PolicyUtils.h"
|
||||
#include "idmap2/SysTrace.h"
|
||||
|
||||
using android::ApkAssets;
|
||||
using android::base::StringPrintf;
|
||||
using android::idmap2::BinaryStreamVisitor;
|
||||
using android::idmap2::CommandLineOptions;
|
||||
using android::idmap2::Error;
|
||||
using android::idmap2::Idmap;
|
||||
using android::idmap2::OverlayResourceContainer;
|
||||
using android::idmap2::Result;
|
||||
using android::idmap2::TargetResourceContainer;
|
||||
using android::idmap2::Unit;
|
||||
using android::idmap2::utils::kIdmapCacheDir;
|
||||
using android::idmap2::utils::kIdmapFilePermissionMask;
|
||||
@@ -91,9 +92,9 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
|
||||
fulfilled_policies |= PolicyFlags::PUBLIC;
|
||||
}
|
||||
|
||||
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
|
||||
if (!target_apk) {
|
||||
return Error("failed to load apk %s", target_apk_path.c_str());
|
||||
const auto target = TargetResourceContainer::FromPath(target_apk_path);
|
||||
if (!target) {
|
||||
return Error("failed to load target '%s'", target_apk_path.c_str());
|
||||
}
|
||||
|
||||
std::vector<std::string> idmap_paths;
|
||||
@@ -108,14 +109,14 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
|
||||
// TODO(b/175014391): Support multiple overlay tags in OverlayConfig
|
||||
if (!Verify(idmap_path, target_apk_path, overlay_apk_path, "", fulfilled_policies,
|
||||
!ignore_overlayable)) {
|
||||
const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
|
||||
if (!overlay_apk) {
|
||||
const auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
|
||||
if (!overlay) {
|
||||
LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str();
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, "", fulfilled_policies,
|
||||
!ignore_overlayable);
|
||||
const auto idmap =
|
||||
Idmap::FromContainers(**target, **overlay, "", fulfilled_policies, !ignore_overlayable);
|
||||
if (!idmap) {
|
||||
LOG(WARNING) << "failed to create idmap";
|
||||
continue;
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
#include "idmap2/Result.h"
|
||||
#include "idmap2/SysTrace.h"
|
||||
#include "idmap2/XmlParser.h"
|
||||
#include "idmap2/ZipFile.h"
|
||||
#include "utils/String16.h"
|
||||
#include "utils/String8.h"
|
||||
|
||||
@@ -52,10 +51,10 @@ using android::base::StringPrintf;
|
||||
using android::idmap2::CommandLineOptions;
|
||||
using android::idmap2::Error;
|
||||
using android::idmap2::IdmapHeader;
|
||||
using android::idmap2::OverlayResourceContainer;
|
||||
using android::idmap2::ResourceId;
|
||||
using android::idmap2::Result;
|
||||
using android::idmap2::Unit;
|
||||
using android::idmap2::utils::ExtractOverlayManifestInfo;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -195,12 +194,17 @@ Result<Unit> Lookup(const std::vector<std::string>& args) {
|
||||
}
|
||||
apk_assets.push_back(std::move(target_apk));
|
||||
|
||||
auto manifest_info = ExtractOverlayManifestInfo(idmap_header->GetOverlayPath(),
|
||||
idmap_header->GetOverlayName());
|
||||
auto overlay = OverlayResourceContainer::FromPath(idmap_header->GetOverlayPath());
|
||||
if (!overlay) {
|
||||
return overlay.GetError();
|
||||
}
|
||||
|
||||
auto manifest_info = (*overlay)->FindOverlayInfo(idmap_header->GetOverlayName());
|
||||
if (!manifest_info) {
|
||||
return manifest_info.GetError();
|
||||
}
|
||||
target_package_name = manifest_info->target_package;
|
||||
|
||||
target_package_name = (*manifest_info).target_package;
|
||||
} else if (target_path != idmap_header->GetTargetPath()) {
|
||||
return Error("different target APKs (expected target APK %s but %s has target APK %s)",
|
||||
target_path.c_str(), idmap_path.c_str(), idmap_header->GetTargetPath().c_str());
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
#include <sys/stat.h> // umask
|
||||
#include <sys/types.h> // umask
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
@@ -35,19 +35,20 @@
|
||||
#include "idmap2/Idmap.h"
|
||||
#include "idmap2/Result.h"
|
||||
#include "idmap2/SysTrace.h"
|
||||
#include "idmap2/ZipFile.h"
|
||||
#include "utils/String8.h"
|
||||
|
||||
using android::IPCThreadState;
|
||||
using android::base::StringPrintf;
|
||||
using android::binder::Status;
|
||||
using android::idmap2::BinaryStreamVisitor;
|
||||
using android::idmap2::GetPackageCrc;
|
||||
using android::idmap2::FabricatedOverlay;
|
||||
using android::idmap2::FabricatedOverlayContainer;
|
||||
using android::idmap2::Idmap;
|
||||
using android::idmap2::IdmapHeader;
|
||||
using android::idmap2::ZipFile;
|
||||
using android::idmap2::OverlayResourceContainer;
|
||||
using android::idmap2::TargetResourceContainer;
|
||||
using android::idmap2::utils::kIdmapCacheDir;
|
||||
using android::idmap2::utils::kIdmapFilePermissionMask;
|
||||
using android::idmap2::utils::RandomStringForPath;
|
||||
using android::idmap2::utils::UidHasWriteAccessToPath;
|
||||
|
||||
using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
|
||||
@@ -69,39 +70,24 @@ PolicyBitmask ConvertAidlArgToPolicyBitmask(int32_t arg) {
|
||||
return static_cast<PolicyBitmask>(arg);
|
||||
}
|
||||
|
||||
Status GetCrc(const std::string& apk_path, uint32_t* out_crc) {
|
||||
const auto zip = ZipFile::Open(apk_path);
|
||||
if (!zip) {
|
||||
return error(StringPrintf("failed to open apk %s", apk_path.c_str()));
|
||||
}
|
||||
|
||||
const auto crc = GetPackageCrc(*zip);
|
||||
if (!crc) {
|
||||
return error(crc.GetErrorMessage());
|
||||
}
|
||||
|
||||
*out_crc = *crc;
|
||||
return ok();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace android::os {
|
||||
|
||||
Status Idmap2Service::getIdmapPath(const std::string& overlay_apk_path,
|
||||
Status Idmap2Service::getIdmapPath(const std::string& overlay_path,
|
||||
int32_t user_id ATTRIBUTE_UNUSED, std::string* _aidl_return) {
|
||||
assert(_aidl_return);
|
||||
SYSTRACE << "Idmap2Service::getIdmapPath " << overlay_apk_path;
|
||||
*_aidl_return = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
|
||||
SYSTRACE << "Idmap2Service::getIdmapPath " << overlay_path;
|
||||
*_aidl_return = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path);
|
||||
return ok();
|
||||
}
|
||||
|
||||
Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path,
|
||||
int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
|
||||
Status Idmap2Service::removeIdmap(const std::string& overlay_path, int32_t user_id ATTRIBUTE_UNUSED,
|
||||
bool* _aidl_return) {
|
||||
assert(_aidl_return);
|
||||
SYSTRACE << "Idmap2Service::removeIdmap " << overlay_apk_path;
|
||||
SYSTRACE << "Idmap2Service::removeIdmap " << overlay_path;
|
||||
const uid_t uid = IPCThreadState::self()->getCallingUid();
|
||||
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
|
||||
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path);
|
||||
if (!UidHasWriteAccessToPath(uid, idmap_path)) {
|
||||
*_aidl_return = false;
|
||||
return error(base::StringPrintf("failed to unlink %s: calling uid %d lacks write access",
|
||||
@@ -115,85 +101,78 @@ Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path,
|
||||
return ok();
|
||||
}
|
||||
|
||||
Status Idmap2Service::verifyIdmap(const std::string& target_apk_path,
|
||||
const std::string& overlay_apk_path, int32_t fulfilled_policies,
|
||||
Status Idmap2Service::verifyIdmap(const std::string& target_path, const std::string& overlay_path,
|
||||
const std::string& overlay_name, int32_t fulfilled_policies,
|
||||
bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED,
|
||||
bool* _aidl_return) {
|
||||
SYSTRACE << "Idmap2Service::verifyIdmap " << overlay_apk_path;
|
||||
SYSTRACE << "Idmap2Service::verifyIdmap " << overlay_path;
|
||||
assert(_aidl_return);
|
||||
|
||||
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
|
||||
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path);
|
||||
std::ifstream fin(idmap_path);
|
||||
const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
|
||||
fin.close();
|
||||
if (!header) {
|
||||
*_aidl_return = false;
|
||||
return error("failed to parse idmap header");
|
||||
LOG(WARNING) << "failed to parse idmap header of '" << idmap_path << "'";
|
||||
return ok();
|
||||
}
|
||||
|
||||
uint32_t target_crc;
|
||||
if (target_apk_path == kFrameworkPath && android_crc_) {
|
||||
target_crc = *android_crc_;
|
||||
} else {
|
||||
auto target_crc_status = GetCrc(target_apk_path, &target_crc);
|
||||
if (!target_crc_status.isOk()) {
|
||||
*_aidl_return = false;
|
||||
return target_crc_status;
|
||||
}
|
||||
|
||||
// Loading the framework zip can take several milliseconds. Cache the crc of the framework
|
||||
// resource APK to reduce repeated work during boot.
|
||||
if (target_apk_path == kFrameworkPath) {
|
||||
android_crc_ = target_crc;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t overlay_crc;
|
||||
auto overlay_crc_status = GetCrc(overlay_apk_path, &overlay_crc);
|
||||
if (!overlay_crc_status.isOk()) {
|
||||
const auto target = GetTargetContainer(target_path);
|
||||
if (!target) {
|
||||
*_aidl_return = false;
|
||||
return overlay_crc_status;
|
||||
LOG(WARNING) << "failed to load target '" << target_path << "'";
|
||||
return ok();
|
||||
}
|
||||
|
||||
const auto overlay = OverlayResourceContainer::FromPath(overlay_path);
|
||||
if (!overlay) {
|
||||
*_aidl_return = false;
|
||||
LOG(WARNING) << "failed to load overlay '" << overlay_path << "'";
|
||||
return ok();
|
||||
}
|
||||
|
||||
// TODO(162841629): Support passing overlay name to idmap2d verify
|
||||
auto up_to_date =
|
||||
header->IsUpToDate(target_apk_path, overlay_apk_path, "", target_crc, overlay_crc,
|
||||
header->IsUpToDate(*GetPointer(*target), **overlay, overlay_name,
|
||||
ConvertAidlArgToPolicyBitmask(fulfilled_policies), enforce_overlayable);
|
||||
|
||||
*_aidl_return = static_cast<bool>(up_to_date);
|
||||
return *_aidl_return ? ok() : error(up_to_date.GetErrorMessage());
|
||||
if (!up_to_date) {
|
||||
LOG(WARNING) << "idmap '" << idmap_path
|
||||
<< "' not up to date : " << up_to_date.GetErrorMessage();
|
||||
}
|
||||
return ok();
|
||||
}
|
||||
|
||||
Status Idmap2Service::createIdmap(const std::string& target_apk_path,
|
||||
const std::string& overlay_apk_path, int32_t fulfilled_policies,
|
||||
Status Idmap2Service::createIdmap(const std::string& target_path, const std::string& overlay_path,
|
||||
const std::string& overlay_name, int32_t fulfilled_policies,
|
||||
bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED,
|
||||
std::optional<std::string>* _aidl_return) {
|
||||
assert(_aidl_return);
|
||||
SYSTRACE << "Idmap2Service::createIdmap " << target_apk_path << " " << overlay_apk_path;
|
||||
SYSTRACE << "Idmap2Service::createIdmap " << target_path << " " << overlay_path;
|
||||
_aidl_return->reset();
|
||||
|
||||
const PolicyBitmask policy_bitmask = ConvertAidlArgToPolicyBitmask(fulfilled_policies);
|
||||
|
||||
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
|
||||
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path);
|
||||
const uid_t uid = IPCThreadState::self()->getCallingUid();
|
||||
if (!UidHasWriteAccessToPath(uid, idmap_path)) {
|
||||
return error(base::StringPrintf("will not write to %s: calling uid %d lacks write accesss",
|
||||
idmap_path.c_str(), uid));
|
||||
}
|
||||
|
||||
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
|
||||
if (!target_apk) {
|
||||
return error("failed to load apk " + target_apk_path);
|
||||
const auto target = GetTargetContainer(target_path);
|
||||
if (!target) {
|
||||
return error("failed to load target '%s'" + target_path);
|
||||
}
|
||||
|
||||
const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
|
||||
if (!overlay_apk) {
|
||||
return error("failed to load apk " + overlay_apk_path);
|
||||
const auto overlay = OverlayResourceContainer::FromPath(overlay_path);
|
||||
if (!overlay) {
|
||||
return error("failed to load apk overlay '%s'" + overlay_path);
|
||||
}
|
||||
|
||||
// TODO(162841629): Support passing overlay name to idmap2d create
|
||||
const auto idmap =
|
||||
Idmap::FromApkAssets(*target_apk, *overlay_apk, "", policy_bitmask, enforce_overlayable);
|
||||
const auto idmap = Idmap::FromContainers(*GetPointer(*target), **overlay, overlay_name,
|
||||
policy_bitmask, enforce_overlayable);
|
||||
if (!idmap) {
|
||||
return error(idmap.GetErrorMessage());
|
||||
}
|
||||
@@ -220,4 +199,155 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path,
|
||||
return ok();
|
||||
}
|
||||
|
||||
idmap2::Result<Idmap2Service::TargetResourceContainerPtr> Idmap2Service::GetTargetContainer(
|
||||
const std::string& target_path) {
|
||||
if (target_path == kFrameworkPath) {
|
||||
if (framework_apk_cache_ == nullptr) {
|
||||
// Initialize the framework APK cache.
|
||||
auto target = TargetResourceContainer::FromPath(target_path);
|
||||
if (!target) {
|
||||
return target.GetError();
|
||||
}
|
||||
framework_apk_cache_ = std::move(*target);
|
||||
}
|
||||
return {framework_apk_cache_.get()};
|
||||
}
|
||||
|
||||
auto target = TargetResourceContainer::FromPath(target_path);
|
||||
if (!target) {
|
||||
return target.GetError();
|
||||
}
|
||||
return {std::move(*target)};
|
||||
}
|
||||
|
||||
Status Idmap2Service::createFabricatedOverlay(
|
||||
const os::FabricatedOverlayInternal& overlay,
|
||||
std::optional<os::FabricatedOverlayInfo>* _aidl_return) {
|
||||
idmap2::FabricatedOverlay::Builder builder(overlay.packageName, overlay.overlayName,
|
||||
overlay.targetPackageName);
|
||||
if (!overlay.targetOverlayable.empty()) {
|
||||
builder.SetOverlayable(overlay.targetOverlayable);
|
||||
}
|
||||
|
||||
for (const auto& res : overlay.entries) {
|
||||
builder.SetResourceValue(res.resourceName, res.dataType, res.data);
|
||||
}
|
||||
|
||||
// Generate the file path of the fabricated overlay and ensure it does not collide with an
|
||||
// existing path. Re-registering a fabricated overlay will always result in an updated path.
|
||||
std::string path;
|
||||
std::string file_name;
|
||||
do {
|
||||
constexpr size_t kSuffixLength = 4;
|
||||
const std::string random_suffix = RandomStringForPath(kSuffixLength);
|
||||
file_name = StringPrintf("%s-%s-%s.frro", overlay.packageName.c_str(),
|
||||
overlay.overlayName.c_str(), random_suffix.c_str());
|
||||
path = StringPrintf("%s/%s", kIdmapCacheDir, file_name.c_str());
|
||||
|
||||
// Invoking std::filesystem::exists with a file name greater than 255 characters will cause this
|
||||
// process to abort since the name exceeds the maximum file name size.
|
||||
const size_t kMaxFileNameLength = 255;
|
||||
if (file_name.size() > kMaxFileNameLength) {
|
||||
return error(
|
||||
base::StringPrintf("fabricated overlay file name '%s' longer than %zu characters",
|
||||
file_name.c_str(), kMaxFileNameLength));
|
||||
}
|
||||
} while (std::filesystem::exists(path));
|
||||
|
||||
const uid_t uid = IPCThreadState::self()->getCallingUid();
|
||||
if (!UidHasWriteAccessToPath(uid, path)) {
|
||||
return error(base::StringPrintf("will not write to %s: calling uid %d lacks write access",
|
||||
path.c_str(), uid));
|
||||
}
|
||||
|
||||
// Persist the fabricated overlay.
|
||||
umask(kIdmapFilePermissionMask);
|
||||
std::ofstream fout(path);
|
||||
if (fout.fail()) {
|
||||
return error("failed to open frro path " + path);
|
||||
}
|
||||
const auto frro = builder.Build();
|
||||
if (!frro) {
|
||||
return error(StringPrintf("failed to serialize '%s:%s': %s", overlay.packageName.c_str(),
|
||||
overlay.overlayName.c_str(), frro.GetErrorMessage().c_str()));
|
||||
}
|
||||
auto result = frro->ToBinaryStream(fout);
|
||||
if (!result) {
|
||||
unlink(path.c_str());
|
||||
return error("failed to write to frro path " + path + ": " + result.GetErrorMessage());
|
||||
}
|
||||
if (fout.fail()) {
|
||||
unlink(path.c_str());
|
||||
return error("failed to write to frro path " + path);
|
||||
}
|
||||
|
||||
os::FabricatedOverlayInfo out_info;
|
||||
out_info.packageName = overlay.packageName;
|
||||
out_info.overlayName = overlay.overlayName;
|
||||
out_info.targetPackageName = overlay.targetPackageName;
|
||||
out_info.targetOverlayable = overlay.targetOverlayable;
|
||||
out_info.path = path;
|
||||
*_aidl_return = out_info;
|
||||
return ok();
|
||||
}
|
||||
|
||||
Status Idmap2Service::getFabricatedOverlayInfos(
|
||||
std::vector<os::FabricatedOverlayInfo>* _aidl_return) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(kIdmapCacheDir)) {
|
||||
if (!android::IsFabricatedOverlay(entry.path())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto overlay = FabricatedOverlayContainer::FromPath(entry.path());
|
||||
if (!overlay) {
|
||||
// This is a sign something went wrong.
|
||||
LOG(ERROR) << "Failed to open '" << entry.path() << "': " << overlay.GetErrorMessage();
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto info = (*overlay)->GetManifestInfo();
|
||||
os::FabricatedOverlayInfo out_info;
|
||||
out_info.packageName = info.package_name;
|
||||
out_info.overlayName = info.name;
|
||||
out_info.targetPackageName = info.target_package;
|
||||
out_info.targetOverlayable = info.target_name;
|
||||
out_info.path = entry.path();
|
||||
_aidl_return->emplace_back(std::move(out_info));
|
||||
}
|
||||
|
||||
return ok();
|
||||
}
|
||||
|
||||
binder::Status Idmap2Service::deleteFabricatedOverlay(const std::string& overlay_path,
|
||||
bool* _aidl_return) {
|
||||
SYSTRACE << "Idmap2Service::deleteFabricatedOverlay " << overlay_path;
|
||||
const uid_t uid = IPCThreadState::self()->getCallingUid();
|
||||
|
||||
if (!UidHasWriteAccessToPath(uid, overlay_path)) {
|
||||
*_aidl_return = false;
|
||||
return error(base::StringPrintf("failed to unlink %s: calling uid %d lacks write access",
|
||||
overlay_path.c_str(), uid));
|
||||
}
|
||||
|
||||
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path);
|
||||
if (!UidHasWriteAccessToPath(uid, idmap_path)) {
|
||||
*_aidl_return = false;
|
||||
return error(base::StringPrintf("failed to unlink %s: calling uid %d lacks write access",
|
||||
idmap_path.c_str(), uid));
|
||||
}
|
||||
|
||||
if (unlink(overlay_path.c_str()) != 0) {
|
||||
*_aidl_return = false;
|
||||
return error("failed to unlink " + overlay_path + ": " + strerror(errno));
|
||||
}
|
||||
|
||||
if (unlink(idmap_path.c_str()) != 0) {
|
||||
*_aidl_return = false;
|
||||
return error("failed to unlink " + idmap_path + ": " + strerror(errno));
|
||||
}
|
||||
|
||||
*_aidl_return = true;
|
||||
return ok();
|
||||
}
|
||||
|
||||
} // namespace android::os
|
||||
|
||||
@@ -18,12 +18,14 @@
|
||||
#define IDMAP2_IDMAP2D_IDMAP2SERVICE_H_
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <android/os/BnIdmap2.h>
|
||||
#include <android/os/FabricatedOverlayInfo.h>
|
||||
#include <binder/BinderService.h>
|
||||
#include <idmap2/ResourceContainer.h>
|
||||
#include <idmap2/Result.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "android/os/BnIdmap2.h"
|
||||
|
||||
namespace android::os {
|
||||
|
||||
class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 {
|
||||
@@ -32,27 +34,58 @@ class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 {
|
||||
return "idmap";
|
||||
}
|
||||
|
||||
binder::Status getIdmapPath(const std::string& overlay_apk_path, int32_t user_id,
|
||||
binder::Status getIdmapPath(const std::string& overlay_path, int32_t user_id,
|
||||
std::string* _aidl_return) override;
|
||||
|
||||
binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id,
|
||||
binder::Status removeIdmap(const std::string& overlay_path, int32_t user_id,
|
||||
bool* _aidl_return) override;
|
||||
|
||||
binder::Status verifyIdmap(const std::string& target_apk_path,
|
||||
const std::string& overlay_apk_path, int32_t fulfilled_policies,
|
||||
binder::Status verifyIdmap(const std::string& target_path, const std::string& overlay_path,
|
||||
const std::string& overlay_name, int32_t fulfilled_policies,
|
||||
bool enforce_overlayable, int32_t user_id,
|
||||
bool* _aidl_return) override;
|
||||
|
||||
binder::Status createIdmap(const std::string& target_apk_path,
|
||||
const std::string& overlay_apk_path, int32_t fulfilled_policies,
|
||||
binder::Status createIdmap(const std::string& target_path, const std::string& overlay_path,
|
||||
const std::string& overlay_name, int32_t fulfilled_policies,
|
||||
bool enforce_overlayable, int32_t user_id,
|
||||
std::optional<std::string>* _aidl_return) override;
|
||||
|
||||
binder::Status createFabricatedOverlay(
|
||||
const os::FabricatedOverlayInternal& overlay,
|
||||
std::optional<os::FabricatedOverlayInfo>* _aidl_return) override;
|
||||
|
||||
binder::Status deleteFabricatedOverlay(const std::string& overlay_path,
|
||||
bool* _aidl_return) override;
|
||||
|
||||
binder::Status getFabricatedOverlayInfos(
|
||||
std::vector<os::FabricatedOverlayInfo>* _aidl_return) override;
|
||||
|
||||
private:
|
||||
// Cache the crc of the android framework package since the crc cannot change without a reboot.
|
||||
std::optional<uint32_t> android_crc_;
|
||||
// idmap2d is killed after a period of inactivity, so any information stored on this class should
|
||||
// be able to be recalculated if idmap2 dies and restarts.
|
||||
std::unique_ptr<idmap2::TargetResourceContainer> framework_apk_cache_;
|
||||
|
||||
std::vector<os::FabricatedOverlayInfo> fabricated_overlays_;
|
||||
|
||||
template <typename T>
|
||||
using MaybeUniquePtr = std::variant<std::unique_ptr<T>, T*>;
|
||||
|
||||
using TargetResourceContainerPtr = MaybeUniquePtr<idmap2::TargetResourceContainer>;
|
||||
idmap2::Result<TargetResourceContainerPtr> GetTargetContainer(const std::string& target_path);
|
||||
|
||||
template <typename T>
|
||||
WARN_UNUSED static const T* GetPointer(const MaybeUniquePtr<T>& ptr);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
const T* Idmap2Service::GetPointer(const MaybeUniquePtr<T>& ptr) {
|
||||
auto u = std::get_if<T*>(&ptr);
|
||||
if (u != nullptr) {
|
||||
return *u;
|
||||
}
|
||||
return std::get<std::unique_ptr<T>>(ptr).get();
|
||||
}
|
||||
|
||||
} // namespace android::os
|
||||
|
||||
#endif // IDMAP2_IDMAP2D_IDMAP2SERVICE_H_
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* 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 android.os;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
parcelable FabricatedOverlayInfo {
|
||||
@utf8InCpp String path;
|
||||
@utf8InCpp String packageName;
|
||||
@utf8InCpp String overlayName;
|
||||
@utf8InCpp String targetPackageName;
|
||||
@utf8InCpp String targetOverlayable;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* 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 android.os;
|
||||
|
||||
import android.os.FabricatedOverlayInternalEntry;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
parcelable FabricatedOverlayInternal {
|
||||
@utf8InCpp String packageName;
|
||||
@utf8InCpp String overlayName;
|
||||
@utf8InCpp String targetPackageName;
|
||||
@utf8InCpp String targetOverlayable;
|
||||
List<FabricatedOverlayInternalEntry> entries;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* 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 android.os;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
parcelable FabricatedOverlayInternalEntry {
|
||||
@utf8InCpp String resourceName;
|
||||
int dataType;
|
||||
int data;
|
||||
}
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package android.os;
|
||||
|
||||
import android.os.FabricatedOverlayInternal;
|
||||
import android.os.FabricatedOverlayInfo;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -23,13 +26,18 @@ interface IIdmap2 {
|
||||
@utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId);
|
||||
boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId);
|
||||
boolean verifyIdmap(@utf8InCpp String targetApkPath,
|
||||
@utf8InCpp String overlayApkPath,
|
||||
@utf8InCpp String overlayApkPath,
|
||||
@utf8InCpp String overlayName,
|
||||
int fulfilledPolicies,
|
||||
boolean enforceOverlayable,
|
||||
int userId);
|
||||
@nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath,
|
||||
@utf8InCpp String overlayApkPath,
|
||||
@utf8InCpp String overlayName,
|
||||
int fulfilledPolicies,
|
||||
boolean enforceOverlayable,
|
||||
int userId);
|
||||
@nullable FabricatedOverlayInfo createFabricatedOverlay(in FabricatedOverlayInternal overlay);
|
||||
List<FabricatedOverlayInfo> getFabricatedOverlayInfos();
|
||||
boolean deleteFabricatedOverlay(@utf8InCpp String path);
|
||||
}
|
||||
105
cmds/idmap2/include/idmap2/FabricatedOverlay.h
Normal file
105
cmds/idmap2/include/idmap2/FabricatedOverlay.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef IDMAP2_INCLUDE_IDMAP2_FABRICATEDOVERLAY_H
|
||||
#define IDMAP2_INCLUDE_IDMAP2_FABRICATEDOVERLAY_H
|
||||
|
||||
#include <libidmap2/proto/fabricated_v1.pb.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "idmap2/ResourceContainer.h"
|
||||
#include "idmap2/Result.h"
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
struct FabricatedOverlay {
|
||||
struct Builder {
|
||||
Builder(const std::string& package_name, const std::string& name,
|
||||
const std::string& target_package_name);
|
||||
|
||||
Builder& SetOverlayable(const std::string& name);
|
||||
|
||||
Builder& SetResourceValue(const std::string& resource_name, uint8_t data_type,
|
||||
uint32_t data_value);
|
||||
|
||||
WARN_UNUSED Result<FabricatedOverlay> Build();
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
std::string resource_name;
|
||||
DataType data_type;
|
||||
DataValue data_value;
|
||||
};
|
||||
|
||||
std::string package_name_;
|
||||
std::string name_;
|
||||
std::string target_package_name_;
|
||||
std::string target_overlayable_;
|
||||
std::vector<Entry> entries_;
|
||||
};
|
||||
|
||||
Result<Unit> ToBinaryStream(std::ostream& stream) const;
|
||||
static Result<FabricatedOverlay> FromBinaryStream(std::istream& stream);
|
||||
|
||||
private:
|
||||
struct SerializedData {
|
||||
std::unique_ptr<uint8_t[]> data;
|
||||
size_t data_size;
|
||||
uint32_t crc;
|
||||
};
|
||||
|
||||
Result<SerializedData*> InitializeData() const;
|
||||
Result<uint32_t> GetCrc() const;
|
||||
|
||||
FabricatedOverlay(pb::FabricatedOverlay&& overlay, std::optional<uint32_t> crc_from_disk = {});
|
||||
|
||||
pb::FabricatedOverlay overlay_pb_;
|
||||
std::optional<uint32_t> crc_from_disk_;
|
||||
mutable std::optional<SerializedData> data_;
|
||||
|
||||
friend struct FabricatedOverlayContainer;
|
||||
};
|
||||
|
||||
struct FabricatedOverlayContainer : public OverlayResourceContainer {
|
||||
static Result<std::unique_ptr<FabricatedOverlayContainer>> FromPath(std::string path);
|
||||
static std::unique_ptr<FabricatedOverlayContainer> FromOverlay(FabricatedOverlay&& overlay);
|
||||
|
||||
WARN_UNUSED OverlayManifestInfo GetManifestInfo() const;
|
||||
|
||||
// inherited from OverlayResourceContainer
|
||||
WARN_UNUSED Result<OverlayManifestInfo> FindOverlayInfo(const std::string& name) const override;
|
||||
WARN_UNUSED Result<OverlayData> GetOverlayData(const OverlayManifestInfo& info) const override;
|
||||
|
||||
// inherited from ResourceContainer
|
||||
WARN_UNUSED Result<uint32_t> GetCrc() const override;
|
||||
WARN_UNUSED const std::string& GetPath() const override;
|
||||
WARN_UNUSED Result<std::string> GetResourceName(ResourceId id) const override;
|
||||
|
||||
~FabricatedOverlayContainer() override;
|
||||
|
||||
private:
|
||||
FabricatedOverlayContainer(FabricatedOverlay&& overlay, std::string&& path);
|
||||
FabricatedOverlay overlay_;
|
||||
std::string path_;
|
||||
};
|
||||
|
||||
} // namespace android::idmap2
|
||||
|
||||
#endif // IDMAP2_INCLUDE_IDMAP2_FABRICATEDOVERLAY_H
|
||||
@@ -17,6 +17,7 @@
|
||||
#ifndef IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
|
||||
#define IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
|
||||
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
namespace android::idmap2::utils {
|
||||
@@ -26,6 +27,8 @@ constexpr const mode_t kIdmapFilePermissionMask = 0133; // u=rw,g=r,o=r
|
||||
|
||||
bool UidHasWriteAccessToPath(uid_t uid, const std::string& path);
|
||||
|
||||
std::string RandomStringForPath(size_t length);
|
||||
|
||||
} // namespace android::idmap2::utils
|
||||
|
||||
#endif // IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
* debug_info
|
||||
* data := data_header target_entry* target_inline_entry* overlay_entry*
|
||||
* string_pool
|
||||
* data_header := target_package_id overlay_package_id padding(2) target_entry_count
|
||||
* target_inline_entry_count overlay_entry_count string_pool_index
|
||||
* data_header := target_entry_count target_inline_entry_count overlay_entry_count
|
||||
* string_pool_index
|
||||
* target_entry := target_id overlay_id
|
||||
* target_inline_entry := target_id Res_value::size padding(1) Res_value::type
|
||||
* Res_value::value
|
||||
@@ -68,11 +68,10 @@
|
||||
#include <vector>
|
||||
|
||||
#include "android-base/macros.h"
|
||||
#include "androidfw/ApkAssets.h"
|
||||
#include "androidfw/ResourceTypes.h"
|
||||
#include "androidfw/StringPiece.h"
|
||||
#include "idmap2/ResourceContainer.h"
|
||||
#include "idmap2/ResourceMapping.h"
|
||||
#include "idmap2/ZipFile.h"
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
@@ -85,9 +84,6 @@ static constexpr const uint32_t kIdmapMagic = android::kIdmapMagic;
|
||||
// current version of the idmap binary format; must be incremented when the format is changed
|
||||
static constexpr const uint32_t kIdmapCurrentVersion = android::kIdmapCurrentVersion;
|
||||
|
||||
// Retrieves a crc generated using all of the files within the zip that can affect idmap generation.
|
||||
Result<uint32_t> GetPackageCrc(const ZipFile& zip_info);
|
||||
|
||||
class IdmapHeader {
|
||||
public:
|
||||
static std::unique_ptr<const IdmapHeader> FromBinaryStream(std::istream& stream);
|
||||
@@ -135,9 +131,9 @@ class IdmapHeader {
|
||||
// Invariant: anytime the idmap data encoding is changed, the idmap version
|
||||
// field *must* be incremented. Because of this, we know that if the idmap
|
||||
// header is up-to-date the entire file is up-to-date.
|
||||
Result<Unit> IsUpToDate(const std::string& target_path, const std::string& overlay_path,
|
||||
const std::string& overlay_name, PolicyBitmask fulfilled_policies,
|
||||
bool enforce_overlayable) const;
|
||||
Result<Unit> IsUpToDate(const TargetResourceContainer& target,
|
||||
const OverlayResourceContainer& overlay, const std::string& overlay_name,
|
||||
PolicyBitmask fulfilled_policies, bool enforce_overlayable) const;
|
||||
|
||||
Result<Unit> IsUpToDate(const std::string& target_path, const std::string& overlay_path,
|
||||
const std::string& overlay_name, uint32_t target_crc,
|
||||
@@ -169,14 +165,6 @@ class IdmapData {
|
||||
public:
|
||||
static std::unique_ptr<const Header> FromBinaryStream(std::istream& stream);
|
||||
|
||||
inline PackageId GetTargetPackageId() const {
|
||||
return target_package_id_;
|
||||
}
|
||||
|
||||
inline PackageId GetOverlayPackageId() const {
|
||||
return overlay_package_id_;
|
||||
}
|
||||
|
||||
inline uint32_t GetTargetEntryCount() const {
|
||||
return target_entry_count;
|
||||
}
|
||||
@@ -196,8 +184,6 @@ class IdmapData {
|
||||
void accept(Visitor* v) const;
|
||||
|
||||
private:
|
||||
PackageId target_package_id_;
|
||||
PackageId overlay_package_id_;
|
||||
uint32_t target_entry_count;
|
||||
uint32_t target_entry_inline_count;
|
||||
uint32_t overlay_entry_count;
|
||||
@@ -275,11 +261,10 @@ class Idmap {
|
||||
// file is used; change this in the next version of idmap to use a named
|
||||
// package instead; also update FromApkAssets to take additional parameters:
|
||||
// the target and overlay package names
|
||||
static Result<std::unique_ptr<const Idmap>> FromApkAssets(const ApkAssets& target_apk_assets,
|
||||
const ApkAssets& overlay_apk_assets,
|
||||
const std::string& overlay_name,
|
||||
const PolicyBitmask& fulfilled_policies,
|
||||
bool enforce_overlayable);
|
||||
static Result<std::unique_ptr<const Idmap>> FromContainers(
|
||||
const TargetResourceContainer& target, const OverlayResourceContainer& overlay,
|
||||
const std::string& overlay_name, const PolicyBitmask& fulfilled_policies,
|
||||
bool enforce_overlayable);
|
||||
|
||||
const std::unique_ptr<const IdmapHeader>& GetHeader() const {
|
||||
return header_;
|
||||
|
||||
@@ -41,9 +41,8 @@ class PrettyPrintVisitor : public Visitor {
|
||||
|
||||
private:
|
||||
std::ostream& stream_;
|
||||
AssetManager2 target_am_;
|
||||
AssetManager2 overlay_am_;
|
||||
std::vector<std::unique_ptr<const ApkAssets>> apk_assets_;
|
||||
std::unique_ptr<TargetResourceContainer> target_;
|
||||
std::unique_ptr<OverlayResourceContainer> overlay_;
|
||||
};
|
||||
|
||||
} // namespace idmap2
|
||||
|
||||
@@ -49,10 +49,9 @@ class RawPrintVisitor : public Visitor {
|
||||
void pad(size_t padding);
|
||||
|
||||
std::ostream& stream_;
|
||||
std::vector<std::unique_ptr<const ApkAssets>> apk_assets_;
|
||||
AssetManager2 target_am_;
|
||||
AssetManager2 overlay_am_;
|
||||
size_t offset_;
|
||||
std::unique_ptr<TargetResourceContainer> target_;
|
||||
std::unique_ptr<OverlayResourceContainer> overlay_;
|
||||
};
|
||||
|
||||
} // namespace idmap2
|
||||
|
||||
106
cmds/idmap2/include/idmap2/ResourceContainer.h
Normal file
106
cmds/idmap2/include/idmap2/ResourceContainer.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef IDMAP2_INCLUDE_IDMAP2_RESOURCECONTAINER_H
|
||||
#define IDMAP2_INCLUDE_IDMAP2_RESOURCECONTAINER_H
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "idmap2/Policies.h"
|
||||
#include "idmap2/ResourceUtils.h"
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
struct ResourceContainer {
|
||||
WARN_UNUSED virtual Result<uint32_t> GetCrc() const = 0;
|
||||
WARN_UNUSED virtual const std::string& GetPath() const = 0;
|
||||
WARN_UNUSED virtual Result<std::string> GetResourceName(ResourceId id) const = 0;
|
||||
|
||||
virtual ~ResourceContainer() = default;
|
||||
};
|
||||
|
||||
struct TargetResourceContainer : public ResourceContainer {
|
||||
static Result<std::unique_ptr<TargetResourceContainer>> FromPath(std::string path);
|
||||
|
||||
WARN_UNUSED virtual Result<bool> DefinesOverlayable() const = 0;
|
||||
WARN_UNUSED virtual Result<const android::OverlayableInfo*> GetOverlayableInfo(
|
||||
ResourceId id) const = 0;
|
||||
WARN_UNUSED virtual Result<ResourceId> GetResourceId(const std::string& name) const = 0;
|
||||
|
||||
~TargetResourceContainer() override = default;
|
||||
};
|
||||
|
||||
struct OverlayManifestInfo {
|
||||
std::string package_name; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
std::string name; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
ResourceId resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
};
|
||||
|
||||
struct OverlayData {
|
||||
struct ResourceIdValue {
|
||||
// The overlay resource id.
|
||||
ResourceId overlay_id;
|
||||
|
||||
// Whether or not references to the overlay resource id should be rewritten to its corresponding
|
||||
// target id during resource resolution.
|
||||
bool rewrite_id;
|
||||
};
|
||||
|
||||
struct Value {
|
||||
std::string resource_name;
|
||||
std::variant<ResourceIdValue, TargetValue> value;
|
||||
};
|
||||
|
||||
struct InlineStringPoolData {
|
||||
// The binary data of the android::ResStringPool string pool.
|
||||
std::unique_ptr<uint8_t[]> data;
|
||||
|
||||
// The length of the binary data.
|
||||
uint32_t data_length;
|
||||
|
||||
// The offset added to TargetValue#data_value (the index of the string in the inline string
|
||||
// pool) in order to prevent the indices of the overlay resource table string pool from
|
||||
// colliding with the inline string pool indices.
|
||||
uint32_t string_pool_offset;
|
||||
};
|
||||
|
||||
// The overlay's mapping of target resource name to overlaid value. Use a vector to enforce that
|
||||
// the overlay pairs are inserted into the ResourceMapping in the specified ordered.
|
||||
std::vector<Value> pairs;
|
||||
|
||||
// If the overlay maps a target resource to a string literal (not a string resource), then the
|
||||
// this field contains information about the string pool in which the string literal resides so it
|
||||
// can be inlined into an idmap.
|
||||
std::optional<InlineStringPoolData> string_pool_data;
|
||||
};
|
||||
|
||||
struct OverlayResourceContainer : public ResourceContainer {
|
||||
static Result<std::unique_ptr<OverlayResourceContainer>> FromPath(std::string path);
|
||||
|
||||
WARN_UNUSED virtual Result<OverlayManifestInfo> FindOverlayInfo(
|
||||
const std::string& name) const = 0;
|
||||
WARN_UNUSED virtual Result<OverlayData> GetOverlayData(const OverlayManifestInfo& info) const = 0;
|
||||
|
||||
~OverlayResourceContainer() override = default;
|
||||
};
|
||||
|
||||
} // namespace android::idmap2
|
||||
|
||||
#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCECONTAINER_H
|
||||
@@ -17,30 +17,22 @@
|
||||
#ifndef IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
|
||||
#define IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
|
||||
|
||||
#include <androidfw/ApkAssets.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "androidfw/ApkAssets.h"
|
||||
#include "idmap2/FabricatedOverlay.h"
|
||||
#include "idmap2/LogInfo.h"
|
||||
#include "idmap2/Policies.h"
|
||||
#include "idmap2/ResourceUtils.h"
|
||||
#include "idmap2/Result.h"
|
||||
#include "idmap2/XmlParser.h"
|
||||
|
||||
using android::idmap2::utils::OverlayManifestInfo;
|
||||
|
||||
using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
struct TargetValue {
|
||||
typedef uint8_t DataType;
|
||||
typedef uint32_t DataValue;
|
||||
DataType data_type;
|
||||
DataValue data_value;
|
||||
};
|
||||
|
||||
using TargetResourceMap = std::map<ResourceId, std::variant<ResourceId, TargetValue>>;
|
||||
using OverlayResourceMap = std::map<ResourceId, ResourceId>;
|
||||
|
||||
@@ -49,94 +41,60 @@ class ResourceMapping {
|
||||
// Creates a ResourceMapping using the target and overlay APKs. Setting enforce_overlayable to
|
||||
// `false` disables all overlayable and policy enforcement: this is intended for backwards
|
||||
// compatibility pre-Q and unit tests.
|
||||
static Result<ResourceMapping> FromApkAssets(const ApkAssets& target_apk_assets,
|
||||
const ApkAssets& overlay_apk_assets,
|
||||
const OverlayManifestInfo& overlay_info,
|
||||
const PolicyBitmask& fulfilled_policies,
|
||||
bool enforce_overlayable, LogInfo& log_info);
|
||||
static Result<ResourceMapping> FromContainers(const TargetResourceContainer& target,
|
||||
const OverlayResourceContainer& overlay,
|
||||
const OverlayManifestInfo& overlay_info,
|
||||
const PolicyBitmask& fulfilled_policies,
|
||||
bool enforce_overlayable, LogInfo& log_info);
|
||||
|
||||
// Retrieves the mapping of target resource id to overlay value.
|
||||
inline const TargetResourceMap& GetTargetToOverlayMap() const {
|
||||
return target_map_;
|
||||
}
|
||||
WARN_UNUSED const TargetResourceMap& GetTargetToOverlayMap() const;
|
||||
|
||||
// Retrieves the mapping of overlay resource id to target resource id. This allows a reference to
|
||||
// an overlay resource to appear as a reference to its corresponding target resource at runtime.
|
||||
OverlayResourceMap GetOverlayToTargetMap() const;
|
||||
|
||||
// Retrieves the build-time package id of the target package.
|
||||
inline uint32_t GetTargetPackageId() const {
|
||||
return target_package_id_;
|
||||
}
|
||||
|
||||
// Retrieves the build-time package id of the overlay package.
|
||||
inline uint32_t GetOverlayPackageId() const {
|
||||
return overlay_package_id_;
|
||||
}
|
||||
WARN_UNUSED const OverlayResourceMap& GetOverlayToTargetMap() const;
|
||||
|
||||
// Retrieves the offset that was added to the index of inline string overlay values so the indices
|
||||
// do not collide with the indices of the overlay resource table string pool.
|
||||
inline uint32_t GetStringPoolOffset() const {
|
||||
return string_pool_offset_;
|
||||
}
|
||||
WARN_UNUSED uint32_t GetStringPoolOffset() const;
|
||||
|
||||
// Retrieves the raw string pool data from the xml referenced in android:resourcesMap.
|
||||
inline const StringPiece GetStringPoolData() const {
|
||||
return StringPiece(reinterpret_cast<const char*>(string_pool_data_.get()),
|
||||
string_pool_data_length_);
|
||||
}
|
||||
WARN_UNUSED StringPiece GetStringPoolData() const;
|
||||
|
||||
private:
|
||||
ResourceMapping() = default;
|
||||
|
||||
// Maps a target resource id to an overlay resource id.
|
||||
// If rewrite_overlay_reference is `true` then references to the overlay
|
||||
// resource should appear as a reference to its corresponding target resource at runtime.
|
||||
Result<Unit> AddMapping(ResourceId target_resource, ResourceId overlay_resource,
|
||||
bool rewrite_overlay_reference);
|
||||
|
||||
// Maps a target resource id to a data type and value combination.
|
||||
// The `data_type` is the runtime format of the data value (see Res_value::dataType).
|
||||
Result<Unit> AddMapping(ResourceId target_resource, TargetValue::DataType data_type,
|
||||
TargetValue::DataValue data_value);
|
||||
|
||||
// Removes the overlay value mapping for the target resource.
|
||||
void RemoveMapping(ResourceId target_resource);
|
||||
|
||||
// Parses the mapping of target resources to overlay resources to generate a ResourceMapping.
|
||||
static Result<ResourceMapping> CreateResourceMapping(const AssetManager2* target_am,
|
||||
const LoadedPackage* target_package,
|
||||
const LoadedPackage* overlay_package,
|
||||
size_t string_pool_offset,
|
||||
const XmlParser& overlay_parser,
|
||||
LogInfo& log_info);
|
||||
|
||||
// Generates a ResourceMapping that maps target resources to overlay resources by name. To overlay
|
||||
// a target resource, a resource must exist in the overlay with the same type and entry name as
|
||||
// the target resource.
|
||||
static Result<ResourceMapping> CreateResourceMappingLegacy(const AssetManager2* target_am,
|
||||
const AssetManager2* overlay_am,
|
||||
const LoadedPackage* target_package,
|
||||
const LoadedPackage* overlay_package,
|
||||
LogInfo& log_info);
|
||||
|
||||
// Removes resources that do not pass policy or overlayable checks of the target package.
|
||||
void FilterOverlayableResources(const AssetManager2* target_am,
|
||||
const LoadedPackage* target_package,
|
||||
const LoadedPackage* overlay_package,
|
||||
const OverlayManifestInfo& overlay_info,
|
||||
const PolicyBitmask& fulfilled_policies, LogInfo& log_info);
|
||||
// Maps a target resource id to an overlay resource id or a android::Res_value value.
|
||||
//
|
||||
// If `allow_rewriting_` is true, then the overlay-to-target map will be populated if the target
|
||||
// resource id is mapped to an overlay resource id.
|
||||
Result<Unit> AddMapping(ResourceId target_resource,
|
||||
const std::variant<OverlayData::ResourceIdValue, TargetValue>& value);
|
||||
|
||||
TargetResourceMap target_map_;
|
||||
std::multimap<ResourceId, ResourceId> overlay_map_;
|
||||
|
||||
uint32_t target_package_id_ = 0;
|
||||
uint32_t overlay_package_id_ = 0;
|
||||
OverlayResourceMap overlay_map_;
|
||||
uint32_t string_pool_offset_ = 0;
|
||||
uint32_t string_pool_data_length_ = 0;
|
||||
std::unique_ptr<uint8_t[]> string_pool_data_ = nullptr;
|
||||
};
|
||||
|
||||
inline const TargetResourceMap& ResourceMapping::GetTargetToOverlayMap() const {
|
||||
return target_map_;
|
||||
}
|
||||
|
||||
inline const OverlayResourceMap& ResourceMapping::GetOverlayToTargetMap() const {
|
||||
return overlay_map_;
|
||||
}
|
||||
|
||||
inline uint32_t ResourceMapping::GetStringPoolOffset() const {
|
||||
return string_pool_offset_;
|
||||
}
|
||||
|
||||
inline StringPiece ResourceMapping::GetStringPoolData() const {
|
||||
return StringPiece(reinterpret_cast<const char*>(string_pool_data_.get()),
|
||||
string_pool_data_length_);
|
||||
}
|
||||
|
||||
} // namespace android::idmap2
|
||||
|
||||
#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
|
||||
|
||||
@@ -22,19 +22,26 @@
|
||||
|
||||
#include "androidfw/AssetManager2.h"
|
||||
#include "idmap2/Result.h"
|
||||
#include "idmap2/ZipFile.h"
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
// use typedefs to let the compiler warn us about implicit casts
|
||||
typedef uint32_t ResourceId; // 0xpptteeee
|
||||
typedef uint8_t PackageId; // pp in 0xpptteeee
|
||||
typedef uint8_t TypeId; // tt in 0xpptteeee
|
||||
typedef uint16_t EntryId; // eeee in 0xpptteeee
|
||||
|
||||
#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
|
||||
#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
|
||||
|
||||
// use typedefs to let the compiler warn us about implicit casts
|
||||
using ResourceId = uint32_t; // 0xpptteeee
|
||||
using PackageId = uint8_t; // pp in 0xpptteeee
|
||||
using TypeId = uint8_t; // tt in 0xpptteeee
|
||||
using EntryId = uint16_t; // eeee in 0xpptteeee
|
||||
|
||||
using DataType = uint8_t; // Res_value::dataType
|
||||
using DataValue = uint32_t; // Res_value::data
|
||||
|
||||
struct TargetValue {
|
||||
DataType data_type;
|
||||
DataValue data_value;
|
||||
};
|
||||
|
||||
namespace utils {
|
||||
|
||||
// Returns whether the Res_value::data_type represents a dynamic or regular resource reference.
|
||||
@@ -43,20 +50,10 @@ bool IsReference(uint8_t data_type);
|
||||
// Converts the Res_value::data_type to a human-readable string representation.
|
||||
StringPiece DataTypeToString(uint8_t data_type);
|
||||
|
||||
struct OverlayManifestInfo {
|
||||
std::string name; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
uint32_t resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
};
|
||||
|
||||
Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
|
||||
const std::string& name);
|
||||
|
||||
// Retrieves the type and entry name of the resource in the AssetManager in the form type/entry.
|
||||
Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid);
|
||||
|
||||
} // namespace utils
|
||||
|
||||
} // namespace android::idmap2
|
||||
|
||||
#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
|
||||
|
||||
@@ -30,8 +30,7 @@
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
class XmlParser {
|
||||
public:
|
||||
struct XmlParser {
|
||||
using Event = ResXMLParser::event_code_t;
|
||||
class iterator;
|
||||
|
||||
@@ -127,23 +126,19 @@ class XmlParser {
|
||||
};
|
||||
|
||||
// Creates a new xml parser beginning at the first tag.
|
||||
static Result<std::unique_ptr<const XmlParser>> Create(const void* data, size_t size,
|
||||
bool copy_data = false);
|
||||
~XmlParser();
|
||||
static Result<XmlParser> Create(const void* data, size_t size, bool copy_data = false);
|
||||
|
||||
inline iterator tree_iterator() const {
|
||||
return iterator(tree_);
|
||||
return iterator(*tree_);
|
||||
}
|
||||
|
||||
inline const ResStringPool& get_strings() const {
|
||||
return tree_.getStrings();
|
||||
return tree_->getStrings();
|
||||
}
|
||||
|
||||
private:
|
||||
XmlParser() = default;
|
||||
mutable ResXMLTree tree_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(XmlParser);
|
||||
explicit XmlParser(std::unique_ptr<ResXMLTree> tree);
|
||||
mutable std::unique_ptr<ResXMLTree> tree_;
|
||||
};
|
||||
|
||||
} // namespace android::idmap2
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_
|
||||
#define IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "android-base/macros.h"
|
||||
#include "idmap2/Result.h"
|
||||
#include "ziparchive/zip_archive.h"
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
struct MemoryChunk {
|
||||
size_t size;
|
||||
uint8_t buf[0];
|
||||
|
||||
static std::unique_ptr<MemoryChunk> Allocate(size_t size);
|
||||
|
||||
private:
|
||||
MemoryChunk() {
|
||||
}
|
||||
};
|
||||
|
||||
class ZipFile {
|
||||
public:
|
||||
static std::unique_ptr<const ZipFile> Open(const std::string& path);
|
||||
|
||||
std::unique_ptr<const MemoryChunk> Uncompress(const std::string& entryPath) const;
|
||||
Result<uint32_t> Crc(const std::string& entryPath) const;
|
||||
|
||||
~ZipFile();
|
||||
|
||||
private:
|
||||
explicit ZipFile(const ::ZipArchiveHandle handle) : handle_(handle) {
|
||||
}
|
||||
|
||||
const ::ZipArchiveHandle handle_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ZipFile);
|
||||
};
|
||||
|
||||
} // namespace android::idmap2
|
||||
|
||||
#endif // IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_
|
||||
@@ -87,10 +87,6 @@ void BinaryStreamVisitor::visit(const IdmapData& data) {
|
||||
}
|
||||
|
||||
void BinaryStreamVisitor::visit(const IdmapData::Header& header) {
|
||||
Write8(header.GetTargetPackageId());
|
||||
Write8(header.GetOverlayPackageId());
|
||||
Write8(0U); // padding
|
||||
Write8(0U); // padding
|
||||
Write32(header.GetTargetEntryCount());
|
||||
Write32(header.GetTargetInlineEntryCount());
|
||||
Write32(header.GetOverlayEntryCount());
|
||||
|
||||
302
cmds/idmap2/libidmap2/FabricatedOverlay.cpp
Normal file
302
cmds/idmap2/libidmap2/FabricatedOverlay.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "idmap2/FabricatedOverlay.h"
|
||||
|
||||
#include <androidfw/ResourceUtils.h>
|
||||
#include <google/protobuf/io/coded_stream.h>
|
||||
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
|
||||
#include <utils/ByteOrder.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
namespace {
|
||||
bool Read32(std::istream& stream, uint32_t* out) {
|
||||
uint32_t value;
|
||||
if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) {
|
||||
*out = dtohl(value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Write32(std::ostream& stream, uint32_t value) {
|
||||
uint32_t x = htodl(value);
|
||||
stream.write(reinterpret_cast<char*>(&x), sizeof(uint32_t));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
FabricatedOverlay::FabricatedOverlay(pb::FabricatedOverlay&& overlay,
|
||||
std::optional<uint32_t> crc_from_disk)
|
||||
: overlay_pb_(std::forward<pb::FabricatedOverlay>(overlay)), crc_from_disk_(crc_from_disk) {
|
||||
}
|
||||
|
||||
FabricatedOverlay::Builder::Builder(const std::string& package_name, const std::string& name,
|
||||
const std::string& target_package_name) {
|
||||
package_name_ = package_name;
|
||||
name_ = name;
|
||||
target_package_name_ = target_package_name;
|
||||
}
|
||||
|
||||
FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetOverlayable(const std::string& name) {
|
||||
target_overlayable_ = name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
|
||||
const std::string& resource_name, uint8_t data_type, uint32_t data_value) {
|
||||
entries_.emplace_back(Entry{resource_name, data_type, data_value});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() {
|
||||
std::map<std::string, std::map<std::string, std::map<std::string, TargetValue>>> entries;
|
||||
for (const auto& res_entry : entries_) {
|
||||
StringPiece package_substr;
|
||||
StringPiece type_name;
|
||||
StringPiece entry_name;
|
||||
if (!android::ExtractResourceName(StringPiece(res_entry.resource_name), &package_substr,
|
||||
&type_name, &entry_name)) {
|
||||
return Error("failed to parse resource name '%s'", res_entry.resource_name.c_str());
|
||||
}
|
||||
|
||||
std::string package_name =
|
||||
package_substr.empty() ? target_package_name_ : package_substr.to_string();
|
||||
if (type_name.empty()) {
|
||||
return Error("resource name '%s' missing type name", res_entry.resource_name.c_str());
|
||||
}
|
||||
|
||||
if (entry_name.empty()) {
|
||||
return Error("resource name '%s' missing entry name", res_entry.resource_name.c_str());
|
||||
}
|
||||
|
||||
auto package = entries.find(package_name);
|
||||
if (package == entries.end()) {
|
||||
package = entries
|
||||
.insert(std::make_pair<>(
|
||||
package_name, std::map<std::string, std::map<std::string, TargetValue>>()))
|
||||
.first;
|
||||
}
|
||||
|
||||
auto type = package->second.find(type_name.to_string());
|
||||
if (type == package->second.end()) {
|
||||
type =
|
||||
package->second
|
||||
.insert(std::make_pair<>(type_name.to_string(), std::map<std::string, TargetValue>()))
|
||||
.first;
|
||||
}
|
||||
|
||||
auto entry = type->second.find(entry_name.to_string());
|
||||
if (entry == type->second.end()) {
|
||||
entry = type->second.insert(std::make_pair<>(entry_name.to_string(), TargetValue())).first;
|
||||
}
|
||||
|
||||
entry->second = TargetValue{res_entry.data_type, res_entry.data_value};
|
||||
}
|
||||
|
||||
pb::FabricatedOverlay overlay_pb;
|
||||
overlay_pb.set_package_name(package_name_);
|
||||
overlay_pb.set_name(name_);
|
||||
overlay_pb.set_target_package_name(target_package_name_);
|
||||
overlay_pb.set_target_overlayable(target_overlayable_);
|
||||
|
||||
for (const auto& package : entries) {
|
||||
auto package_pb = overlay_pb.add_packages();
|
||||
package_pb->set_name(package.first);
|
||||
|
||||
for (const auto& type : package.second) {
|
||||
auto type_pb = package_pb->add_types();
|
||||
type_pb->set_name(type.first);
|
||||
|
||||
for (const auto& entry : type.second) {
|
||||
auto entry_pb = type_pb->add_entries();
|
||||
entry_pb->set_name(entry.first);
|
||||
pb::ResourceValue* value = entry_pb->mutable_res_value();
|
||||
value->set_data_type(entry.second.data_type);
|
||||
value->set_data_value(entry.second.data_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FabricatedOverlay(std::move(overlay_pb));
|
||||
}
|
||||
|
||||
Result<FabricatedOverlay> FabricatedOverlay::FromBinaryStream(std::istream& stream) {
|
||||
uint32_t magic;
|
||||
if (!Read32(stream, &magic)) {
|
||||
return Error("Failed to read fabricated overlay magic.");
|
||||
}
|
||||
|
||||
if (magic != kFabricatedOverlayMagic) {
|
||||
return Error("Not a fabricated overlay file.");
|
||||
}
|
||||
|
||||
uint32_t version;
|
||||
if (!Read32(stream, &version)) {
|
||||
return Error("Failed to read fabricated overlay version.");
|
||||
}
|
||||
|
||||
if (version != 1) {
|
||||
return Error("Invalid fabricated overlay version '%u'.", version);
|
||||
}
|
||||
|
||||
uint32_t crc;
|
||||
if (!Read32(stream, &crc)) {
|
||||
return Error("Failed to read fabricated overlay version.");
|
||||
}
|
||||
|
||||
pb::FabricatedOverlay overlay{};
|
||||
if (!overlay.ParseFromIstream(&stream)) {
|
||||
return Error("Failed read fabricated overlay proto.");
|
||||
}
|
||||
|
||||
// If the proto version is the latest version, then the contents of the proto must be the same
|
||||
// when the proto is re-serialized; otherwise, the crc must be calculated because migrating the
|
||||
// proto to the latest version will likely change the contents of the fabricated overlay.
|
||||
return FabricatedOverlay(std::move(overlay), version == kFabricatedOverlayCurrentVersion
|
||||
? std::optional<uint32_t>(crc)
|
||||
: std::nullopt);
|
||||
}
|
||||
|
||||
Result<FabricatedOverlay::SerializedData*> FabricatedOverlay::InitializeData() const {
|
||||
if (!data_.has_value()) {
|
||||
auto size = overlay_pb_.ByteSizeLong();
|
||||
auto data = std::unique_ptr<uint8_t[]>(new uint8_t[size]);
|
||||
|
||||
// Ensure serialization is deterministic
|
||||
google::protobuf::io::ArrayOutputStream array_stream(data.get(), size);
|
||||
google::protobuf::io::CodedOutputStream output_stream(&array_stream);
|
||||
output_stream.SetSerializationDeterministic(true);
|
||||
overlay_pb_.SerializeWithCachedSizes(&output_stream);
|
||||
if (output_stream.HadError() || size != output_stream.ByteCount()) {
|
||||
return Error("Failed to serialize fabricated overlay.");
|
||||
}
|
||||
|
||||
// Calculate the crc using the proto data and the version.
|
||||
uint32_t crc = crc32(0L, Z_NULL, 0);
|
||||
crc = crc32(crc, reinterpret_cast<const uint8_t*>(&kFabricatedOverlayCurrentVersion),
|
||||
sizeof(uint32_t));
|
||||
crc = crc32(crc, data.get(), size);
|
||||
data_ = SerializedData{std::move(data), size, crc};
|
||||
}
|
||||
return &(*data_);
|
||||
}
|
||||
Result<uint32_t> FabricatedOverlay::GetCrc() const {
|
||||
if (crc_from_disk_.has_value()) {
|
||||
return *crc_from_disk_;
|
||||
}
|
||||
auto data = InitializeData();
|
||||
if (!data) {
|
||||
return data.GetError();
|
||||
}
|
||||
return (*data)->crc;
|
||||
}
|
||||
|
||||
Result<Unit> FabricatedOverlay::ToBinaryStream(std::ostream& stream) const {
|
||||
auto data = InitializeData();
|
||||
if (!data) {
|
||||
return data.GetError();
|
||||
}
|
||||
|
||||
Write32(stream, kFabricatedOverlayMagic);
|
||||
Write32(stream, kFabricatedOverlayCurrentVersion);
|
||||
Write32(stream, (*data)->crc);
|
||||
stream.write(reinterpret_cast<const char*>((*data)->data.get()), (*data)->data_size);
|
||||
if (stream.bad()) {
|
||||
return Error("Failed to write serialized fabricated overlay.");
|
||||
}
|
||||
|
||||
return Unit{};
|
||||
}
|
||||
|
||||
using FabContainer = FabricatedOverlayContainer;
|
||||
FabContainer::FabricatedOverlayContainer(FabricatedOverlay&& overlay, std::string&& path)
|
||||
: overlay_(std::forward<FabricatedOverlay>(overlay)), path_(std::forward<std::string>(path)) {
|
||||
}
|
||||
|
||||
FabContainer::~FabricatedOverlayContainer() = default;
|
||||
|
||||
Result<std::unique_ptr<FabContainer>> FabContainer::FromPath(std::string path) {
|
||||
std::fstream fin(path);
|
||||
auto overlay = FabricatedOverlay::FromBinaryStream(fin);
|
||||
if (!overlay) {
|
||||
return overlay.GetError();
|
||||
}
|
||||
return std::unique_ptr<FabContainer>(
|
||||
new FabricatedOverlayContainer(std::move(*overlay), std::move(path)));
|
||||
}
|
||||
|
||||
std::unique_ptr<FabricatedOverlayContainer> FabContainer::FromOverlay(FabricatedOverlay&& overlay) {
|
||||
return std::unique_ptr<FabContainer>(
|
||||
new FabricatedOverlayContainer(std::move(overlay), {} /* path */));
|
||||
}
|
||||
|
||||
OverlayManifestInfo FabContainer::GetManifestInfo() const {
|
||||
const pb::FabricatedOverlay& overlay_pb = overlay_.overlay_pb_;
|
||||
return OverlayManifestInfo{
|
||||
.package_name = overlay_pb.package_name(),
|
||||
.name = overlay_pb.name(),
|
||||
.target_package = overlay_pb.target_package_name(),
|
||||
.target_name = overlay_pb.target_overlayable(),
|
||||
};
|
||||
}
|
||||
|
||||
Result<OverlayManifestInfo> FabContainer::FindOverlayInfo(const std::string& name) const {
|
||||
const OverlayManifestInfo info = GetManifestInfo();
|
||||
if (name != info.name) {
|
||||
return Error("Failed to find name '%s' in fabricated overlay", name.c_str());
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
Result<OverlayData> FabContainer::GetOverlayData(const OverlayManifestInfo& info) const {
|
||||
const pb::FabricatedOverlay& overlay_pb = overlay_.overlay_pb_;
|
||||
if (info.name != overlay_pb.name()) {
|
||||
return Error("Failed to find name '%s' in fabricated overlay", info.name.c_str());
|
||||
}
|
||||
|
||||
OverlayData result{};
|
||||
for (const auto& package : overlay_pb.packages()) {
|
||||
for (const auto& type : package.types()) {
|
||||
for (const auto& entry : type.entries()) {
|
||||
auto name = base::StringPrintf("%s:%s/%s", package.name().c_str(), type.name().c_str(),
|
||||
entry.name().c_str());
|
||||
const auto& res_value = entry.res_value();
|
||||
result.pairs.emplace_back(OverlayData::Value{
|
||||
name, TargetValue{.data_type = static_cast<uint8_t>(res_value.data_type()),
|
||||
.data_value = res_value.data_value()}});
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Result<uint32_t> FabContainer::GetCrc() const {
|
||||
return overlay_.GetCrc();
|
||||
}
|
||||
|
||||
const std::string& FabContainer::GetPath() const {
|
||||
return path_;
|
||||
}
|
||||
|
||||
Result<std::string> FabContainer::GetResourceName(ResourceId /* id */) const {
|
||||
return Error("Fabricated overlay does not contain resources.");
|
||||
}
|
||||
|
||||
} // namespace android::idmap2
|
||||
@@ -47,4 +47,19 @@ bool UidHasWriteAccessToPath(uid_t uid ATTRIBUTE_UNUSED, const std::string& path
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string RandomStringForPath(const size_t length) {
|
||||
constexpr char kChars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
constexpr size_t kCharLastIndex = sizeof(kChars) - 1;
|
||||
|
||||
std::string out_rand;
|
||||
out_rand.reserve(length);
|
||||
|
||||
std::random_device rd;
|
||||
std::uniform_int_distribution<int> dist(0, kCharLastIndex);
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
out_rand[i] = kChars[dist(rd) % (kCharLastIndex)];
|
||||
}
|
||||
return out_rand;
|
||||
}
|
||||
|
||||
} // namespace android::idmap2::utils
|
||||
|
||||
@@ -20,23 +20,16 @@
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "android-base/macros.h"
|
||||
#include "android-base/stringprintf.h"
|
||||
#include "androidfw/AssetManager2.h"
|
||||
#include "idmap2/ResourceMapping.h"
|
||||
#include "idmap2/ResourceUtils.h"
|
||||
#include "idmap2/Result.h"
|
||||
#include "idmap2/SysTrace.h"
|
||||
#include "idmap2/ZipFile.h"
|
||||
#include "utils/String16.h"
|
||||
#include "utils/String8.h"
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
@@ -93,22 +86,13 @@ bool WARN_UNUSED ReadString(std::istream& stream, std::string* out) {
|
||||
|
||||
} // namespace
|
||||
|
||||
Result<uint32_t> GetPackageCrc(const ZipFile& zip) {
|
||||
const Result<uint32_t> a = zip.Crc("resources.arsc");
|
||||
const Result<uint32_t> b = zip.Crc("AndroidManifest.xml");
|
||||
return a && b
|
||||
? Result<uint32_t>(*a ^ *b)
|
||||
: Error("failed to get CRC for \"%s\"", a ? "AndroidManifest.xml" : "resources.arsc");
|
||||
}
|
||||
|
||||
std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) {
|
||||
std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader());
|
||||
if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (idmap_header->magic_ != kIdmapMagic ||
|
||||
idmap_header->version_ != kIdmapCurrentVersion) {
|
||||
if (idmap_header->magic_ != kIdmapMagic || idmap_header->version_ != kIdmapCurrentVersion) {
|
||||
// Do not continue parsing if the file is not a current version idmap.
|
||||
return nullptr;
|
||||
}
|
||||
@@ -127,32 +111,22 @@ std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& s
|
||||
return std::move(idmap_header);
|
||||
}
|
||||
|
||||
Result<Unit> IdmapHeader::IsUpToDate(const std::string& target_path,
|
||||
const std::string& overlay_path,
|
||||
Result<Unit> IdmapHeader::IsUpToDate(const TargetResourceContainer& target,
|
||||
const OverlayResourceContainer& overlay,
|
||||
const std::string& overlay_name,
|
||||
PolicyBitmask fulfilled_policies,
|
||||
bool enforce_overlayable) const {
|
||||
const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_path);
|
||||
if (!target_zip) {
|
||||
return Error("failed to open target %s", target_path.c_str());
|
||||
}
|
||||
|
||||
const Result<uint32_t> target_crc = GetPackageCrc(*target_zip);
|
||||
const Result<uint32_t> target_crc = target.GetCrc();
|
||||
if (!target_crc) {
|
||||
return Error("failed to get target crc");
|
||||
}
|
||||
|
||||
const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_path);
|
||||
if (!overlay_zip) {
|
||||
return Error("failed to overlay target %s", overlay_path.c_str());
|
||||
}
|
||||
|
||||
const Result<uint32_t> overlay_crc = GetPackageCrc(*overlay_zip);
|
||||
const Result<uint32_t> overlay_crc = overlay.GetCrc();
|
||||
if (!overlay_crc) {
|
||||
return Error("failed to get overlay crc");
|
||||
}
|
||||
|
||||
return IsUpToDate(target_path, overlay_path, overlay_name, *target_crc, *overlay_crc,
|
||||
return IsUpToDate(target.GetPath(), overlay.GetPath(), overlay_name, *target_crc, *overlay_crc,
|
||||
fulfilled_policies, enforce_overlayable);
|
||||
}
|
||||
|
||||
@@ -209,11 +183,7 @@ Result<Unit> IdmapHeader::IsUpToDate(const std::string& target_path,
|
||||
|
||||
std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) {
|
||||
std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header());
|
||||
|
||||
uint8_t padding;
|
||||
if (!Read8(stream, &idmap_data_header->target_package_id_) ||
|
||||
!Read8(stream, &idmap_data_header->overlay_package_id_) || !Read8(stream, &padding) ||
|
||||
!Read8(stream, &padding) || !Read32(stream, &idmap_data_header->target_entry_count) ||
|
||||
if (!Read32(stream, &idmap_data_header->target_entry_count) ||
|
||||
!Read32(stream, &idmap_data_header->target_entry_inline_count) ||
|
||||
!Read32(stream, &idmap_data_header->overlay_entry_count) ||
|
||||
!Read32(stream, &idmap_data_header->string_pool_index_offset)) {
|
||||
@@ -321,8 +291,6 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
|
||||
}
|
||||
|
||||
std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
|
||||
data_header->target_package_id_ = resource_mapping.GetTargetPackageId();
|
||||
data_header->overlay_package_id_ = resource_mapping.GetOverlayPackageId();
|
||||
data_header->target_entry_count = static_cast<uint32_t>(data->target_entries_.size());
|
||||
data_header->target_entry_inline_count =
|
||||
static_cast<uint32_t>(data->target_inline_entries_.size());
|
||||
@@ -332,57 +300,46 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
|
||||
return {std::move(data)};
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const ApkAssets& target_apk_assets,
|
||||
const ApkAssets& overlay_apk_assets,
|
||||
const std::string& overlay_name,
|
||||
const PolicyBitmask& fulfilled_policies,
|
||||
bool enforce_overlayable) {
|
||||
Result<std::unique_ptr<const Idmap>> Idmap::FromContainers(const TargetResourceContainer& target,
|
||||
const OverlayResourceContainer& overlay,
|
||||
const std::string& overlay_name,
|
||||
const PolicyBitmask& fulfilled_policies,
|
||||
bool enforce_overlayable) {
|
||||
SYSTRACE << "Idmap::FromApkAssets";
|
||||
const std::string& target_apk_path = target_apk_assets.GetPath();
|
||||
const std::string& overlay_apk_path = overlay_apk_assets.GetPath();
|
||||
|
||||
const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_apk_path);
|
||||
if (!target_zip) {
|
||||
return Error("failed to open target as zip");
|
||||
}
|
||||
|
||||
const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_apk_path);
|
||||
if (!overlay_zip) {
|
||||
return Error("failed to open overlay as zip");
|
||||
}
|
||||
|
||||
std::unique_ptr<IdmapHeader> header(new IdmapHeader());
|
||||
header->magic_ = kIdmapMagic;
|
||||
header->version_ = kIdmapCurrentVersion;
|
||||
|
||||
Result<uint32_t> crc = GetPackageCrc(*target_zip);
|
||||
if (!crc) {
|
||||
return Error(crc.GetError(), "failed to get zip CRC for target");
|
||||
const auto target_crc = target.GetCrc();
|
||||
if (!target_crc) {
|
||||
return Error(target_crc.GetError(), "failed to get zip CRC for '%s'", target.GetPath().data());
|
||||
}
|
||||
header->target_crc_ = *crc;
|
||||
header->target_crc_ = *target_crc;
|
||||
|
||||
crc = GetPackageCrc(*overlay_zip);
|
||||
if (!crc) {
|
||||
return Error(crc.GetError(), "failed to get zip CRC for overlay");
|
||||
const auto overlay_crc = overlay.GetCrc();
|
||||
if (!overlay_crc) {
|
||||
return Error(overlay_crc.GetError(), "failed to get zip CRC for '%s'",
|
||||
overlay.GetPath().data());
|
||||
}
|
||||
header->overlay_crc_ = *crc;
|
||||
header->overlay_crc_ = *overlay_crc;
|
||||
|
||||
header->fulfilled_policies_ = fulfilled_policies;
|
||||
header->enforce_overlayable_ = enforce_overlayable;
|
||||
header->target_path_ = target_apk_path;
|
||||
header->overlay_path_ = overlay_apk_path;
|
||||
header->target_path_ = target.GetPath();
|
||||
header->overlay_path_ = overlay.GetPath();
|
||||
header->overlay_name_ = overlay_name;
|
||||
|
||||
auto info = utils::ExtractOverlayManifestInfo(overlay_apk_path, overlay_name);
|
||||
auto info = overlay.FindOverlayInfo(overlay_name);
|
||||
if (!info) {
|
||||
return info.GetError();
|
||||
return Error(info.GetError(), "failed to get overlay info for '%s'", overlay.GetPath().data());
|
||||
}
|
||||
|
||||
LogInfo log_info;
|
||||
auto resource_mapping =
|
||||
ResourceMapping::FromApkAssets(target_apk_assets, overlay_apk_assets, *info,
|
||||
fulfilled_policies, enforce_overlayable, log_info);
|
||||
auto resource_mapping = ResourceMapping::FromContainers(
|
||||
target, overlay, *info, fulfilled_policies, enforce_overlayable, log_info);
|
||||
if (!resource_mapping) {
|
||||
return resource_mapping.GetError();
|
||||
return Error(resource_mapping.GetError(), "failed to generate resource map for '%s'",
|
||||
overlay.GetPath().data());
|
||||
}
|
||||
|
||||
auto idmap_data = IdmapData::FromResourceMapping(*resource_mapping);
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry))
|
||||
|
||||
#define TAB " "
|
||||
|
||||
void PrettyPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
|
||||
@@ -36,8 +34,8 @@ void PrettyPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
|
||||
|
||||
void PrettyPrintVisitor::visit(const IdmapHeader& header) {
|
||||
stream_ << "Paths:" << std::endl
|
||||
<< TAB "target apk path : " << header.GetTargetPath() << std::endl
|
||||
<< TAB "overlay apk path : " << header.GetOverlayPath() << std::endl;
|
||||
<< TAB "target path : " << header.GetTargetPath() << std::endl
|
||||
<< TAB "overlay path : " << header.GetOverlayPath() << std::endl;
|
||||
|
||||
if (!header.GetOverlayName().empty()) {
|
||||
stream_ << "Overlay name: " << header.GetOverlayName() << std::endl;
|
||||
@@ -53,14 +51,11 @@ void PrettyPrintVisitor::visit(const IdmapHeader& header) {
|
||||
}
|
||||
}
|
||||
|
||||
if (auto target_apk_ = ApkAssets::Load(header.GetTargetPath())) {
|
||||
target_am_.SetApkAssets({target_apk_.get()});
|
||||
apk_assets_.push_back(std::move(target_apk_));
|
||||
if (auto target = TargetResourceContainer::FromPath(header.GetTargetPath())) {
|
||||
target_ = std::move(*target);
|
||||
}
|
||||
|
||||
if (auto overlay_apk = ApkAssets::Load(header.GetOverlayPath())) {
|
||||
overlay_am_.SetApkAssets({overlay_apk.get()});
|
||||
apk_assets_.push_back(std::move(overlay_apk));
|
||||
if (auto overlay = OverlayResourceContainer::FromPath(header.GetOverlayPath())) {
|
||||
overlay_ = std::move(*overlay);
|
||||
}
|
||||
|
||||
stream_ << "Mapping:" << std::endl;
|
||||
@@ -72,23 +67,20 @@ void PrettyPrintVisitor::visit(const IdmapData::Header& header ATTRIBUTE_UNUSED)
|
||||
void PrettyPrintVisitor::visit(const IdmapData& data) {
|
||||
static constexpr const char* kUnknownResourceName = "???";
|
||||
|
||||
const bool target_package_loaded = !target_am_.GetApkAssets().empty();
|
||||
const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty();
|
||||
|
||||
const ResStringPool string_pool(data.GetStringPoolData().data(), data.GetStringPoolData().size());
|
||||
const size_t string_pool_offset = data.GetHeader()->GetStringPoolIndexOffset();
|
||||
|
||||
for (const auto& target_entry : data.GetTargetEntries()) {
|
||||
std::string target_name = kUnknownResourceName;
|
||||
if (target_package_loaded) {
|
||||
if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) {
|
||||
if (target_ != nullptr) {
|
||||
if (auto name = target_->GetResourceName(target_entry.target_id)) {
|
||||
target_name = *name;
|
||||
}
|
||||
}
|
||||
|
||||
std::string overlay_name = kUnknownResourceName;
|
||||
if (overlay_package_loaded) {
|
||||
if (auto name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id)) {
|
||||
if (overlay_ != nullptr) {
|
||||
if (auto name = overlay_->GetResourceName(target_entry.overlay_id)) {
|
||||
overlay_name = *name;
|
||||
}
|
||||
}
|
||||
@@ -112,8 +104,8 @@ void PrettyPrintVisitor::visit(const IdmapData& data) {
|
||||
}
|
||||
|
||||
std::string target_name = kUnknownResourceName;
|
||||
if (target_package_loaded) {
|
||||
if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) {
|
||||
if (target_ != nullptr) {
|
||||
if (auto name = target_->GetResourceName(target_entry.target_id)) {
|
||||
target_name = *name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,16 +18,13 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <string>
|
||||
|
||||
#include "android-base/macros.h"
|
||||
#include "android-base/stringprintf.h"
|
||||
#include "androidfw/ApkAssets.h"
|
||||
#include "idmap2/PolicyUtils.h"
|
||||
#include "idmap2/ResourceUtils.h"
|
||||
#include "idmap2/Result.h"
|
||||
|
||||
using android::ApkAssets;
|
||||
using android::idmap2::policy::PoliciesToDebugString;
|
||||
|
||||
namespace android::idmap2 {
|
||||
@@ -48,27 +45,19 @@ void RawPrintVisitor::visit(const IdmapHeader& header) {
|
||||
print(header.GetOverlayName(), true /* print_value */, "overlay name");
|
||||
print(header.GetDebugInfo(), false /* print_value */, "debug info");
|
||||
|
||||
auto target_apk_ = ApkAssets::Load(header.GetTargetPath());
|
||||
if (target_apk_) {
|
||||
target_am_.SetApkAssets({target_apk_.get()});
|
||||
apk_assets_.push_back(std::move(target_apk_));
|
||||
if (auto target = TargetResourceContainer::FromPath(header.GetTargetPath())) {
|
||||
target_ = std::move(*target);
|
||||
}
|
||||
|
||||
auto overlay_apk_ = ApkAssets::Load(header.GetOverlayPath());
|
||||
if (overlay_apk_) {
|
||||
overlay_am_.SetApkAssets({overlay_apk_.get()});
|
||||
apk_assets_.push_back(std::move(overlay_apk_));
|
||||
if (auto overlay = OverlayResourceContainer::FromPath(header.GetOverlayPath())) {
|
||||
overlay_ = std::move(*overlay);
|
||||
}
|
||||
}
|
||||
|
||||
void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
|
||||
const bool target_package_loaded = !target_am_.GetApkAssets().empty();
|
||||
const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty();
|
||||
|
||||
for (auto& target_entry : data.GetTargetEntries()) {
|
||||
Result<std::string> target_name(Error(""));
|
||||
if (target_package_loaded) {
|
||||
target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
|
||||
if (target_ != nullptr) {
|
||||
target_name = target_->GetResourceName(target_entry.target_id);
|
||||
}
|
||||
if (target_name) {
|
||||
print(target_entry.target_id, "target id: %s", target_name->c_str());
|
||||
@@ -77,8 +66,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
|
||||
}
|
||||
|
||||
Result<std::string> overlay_name(Error(""));
|
||||
if (overlay_package_loaded) {
|
||||
overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id);
|
||||
if (overlay_ != nullptr) {
|
||||
overlay_name = overlay_->GetResourceName(target_entry.overlay_id);
|
||||
}
|
||||
if (overlay_name) {
|
||||
print(target_entry.overlay_id, "overlay id: %s", overlay_name->c_str());
|
||||
@@ -89,8 +78,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
|
||||
|
||||
for (auto& target_entry : data.GetTargetInlineEntries()) {
|
||||
Result<std::string> target_name(Error(""));
|
||||
if (target_package_loaded) {
|
||||
target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
|
||||
if (target_ != nullptr) {
|
||||
target_name = target_->GetResourceName(target_entry.target_id);
|
||||
}
|
||||
if (target_name) {
|
||||
print(target_entry.target_id, "target id: %s", target_name->c_str());
|
||||
@@ -104,10 +93,10 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
|
||||
utils::DataTypeToString(target_entry.value.data_type).data());
|
||||
|
||||
Result<std::string> overlay_name(Error(""));
|
||||
if (overlay_package_loaded &&
|
||||
if (overlay_ != nullptr &&
|
||||
(target_entry.value.data_value == Res_value::TYPE_REFERENCE ||
|
||||
target_entry.value.data_value == Res_value::TYPE_DYNAMIC_REFERENCE)) {
|
||||
overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.value.data_value);
|
||||
overlay_name = overlay_->GetResourceName(target_entry.value.data_value);
|
||||
}
|
||||
|
||||
if (overlay_name) {
|
||||
@@ -119,8 +108,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
|
||||
|
||||
for (auto& overlay_entry : data.GetOverlayEntries()) {
|
||||
Result<std::string> overlay_name(Error(""));
|
||||
if (overlay_package_loaded) {
|
||||
overlay_name = utils::ResToTypeEntryName(overlay_am_, overlay_entry.overlay_id);
|
||||
if (overlay_ != nullptr) {
|
||||
overlay_name = overlay_->GetResourceName(overlay_entry.overlay_id);
|
||||
}
|
||||
|
||||
if (overlay_name) {
|
||||
@@ -130,8 +119,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
|
||||
}
|
||||
|
||||
Result<std::string> target_name(Error(""));
|
||||
if (target_package_loaded) {
|
||||
target_name = utils::ResToTypeEntryName(target_am_, overlay_entry.target_id);
|
||||
if (target_ != nullptr) {
|
||||
target_name = target_->GetResourceName(overlay_entry.target_id);
|
||||
}
|
||||
|
||||
if (target_name) {
|
||||
@@ -145,9 +134,6 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
|
||||
}
|
||||
|
||||
void RawPrintVisitor::visit(const IdmapData::Header& header) {
|
||||
print(header.GetTargetPackageId(), "target package id");
|
||||
print(header.GetOverlayPackageId(), "overlay package id");
|
||||
align();
|
||||
print(header.GetTargetEntryCount(), "target entry count");
|
||||
print(header.GetTargetInlineEntryCount(), "target inline entry count");
|
||||
print(header.GetOverlayEntryCount(), "overlay entry count");
|
||||
|
||||
448
cmds/idmap2/libidmap2/ResourceContainer.cpp
Normal file
448
cmds/idmap2/libidmap2/ResourceContainer.cpp
Normal file
@@ -0,0 +1,448 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "idmap2/ResourceContainer.h"
|
||||
|
||||
#include "androidfw/ApkAssets.h"
|
||||
#include "androidfw/AssetManager.h"
|
||||
#include "androidfw/Util.h"
|
||||
#include "idmap2/FabricatedOverlay.h"
|
||||
#include "idmap2/XmlParser.h"
|
||||
|
||||
namespace android::idmap2 {
|
||||
namespace {
|
||||
#define REWRITE_PACKAGE(resid, package_id) \
|
||||
(((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U))
|
||||
|
||||
#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24)
|
||||
|
||||
constexpr ResourceId kAttrName = 0x01010003;
|
||||
constexpr ResourceId kAttrResourcesMap = 0x01010609;
|
||||
constexpr ResourceId kAttrTargetName = 0x0101044d;
|
||||
constexpr ResourceId kAttrTargetPackage = 0x01010021;
|
||||
|
||||
// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
|
||||
// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
|
||||
// this assumption tends to work out. That said, the correct thing to do is to scan
|
||||
// resources.arsc for a package with a given name as read from the package manifest instead of
|
||||
// relying on a hard-coded index. This however requires storing the package name in the idmap
|
||||
// header, which in turn requires incrementing the idmap version. Because the initial version of
|
||||
// idmap2 is compatible with idmap, this will have to wait for now.
|
||||
const LoadedPackage* GetPackageAtIndex0(const LoadedArsc* loaded_arsc) {
|
||||
const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages();
|
||||
if (packages.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return loaded_arsc->GetPackageById(packages[0]->GetPackageId());
|
||||
}
|
||||
|
||||
Result<uint32_t> CalculateCrc(const ZipAssetsProvider* zip_assets) {
|
||||
constexpr const char* kResourcesArsc = "resources.arsc";
|
||||
std::optional<uint32_t> res_crc = zip_assets->GetCrc(kResourcesArsc);
|
||||
if (!res_crc) {
|
||||
return Error("failed to get CRC for '%s'", kResourcesArsc);
|
||||
}
|
||||
|
||||
constexpr const char* kManifest = "AndroidManifest.xml";
|
||||
std::optional<uint32_t> man_crc = zip_assets->GetCrc(kManifest);
|
||||
if (!man_crc) {
|
||||
return Error("failed to get CRC for '%s'", kManifest);
|
||||
}
|
||||
|
||||
return *res_crc ^ *man_crc;
|
||||
}
|
||||
|
||||
Result<XmlParser> OpenXmlParser(const std::string& entry_path, const ZipAssetsProvider* zip) {
|
||||
auto manifest = zip->Open(entry_path);
|
||||
if (manifest == nullptr) {
|
||||
return Error("failed to find %s ", entry_path.c_str());
|
||||
}
|
||||
|
||||
auto size = manifest->getLength();
|
||||
auto buffer = manifest->getIncFsBuffer(true /* aligned */).convert<uint8_t>();
|
||||
if (!buffer.verify(size)) {
|
||||
return Error("failed to read entire %s", entry_path.c_str());
|
||||
}
|
||||
|
||||
return XmlParser::Create(buffer.unsafe_ptr(), size, true /* copyData */);
|
||||
}
|
||||
|
||||
Result<XmlParser> OpenXmlParser(ResourceId id, const ZipAssetsProvider* zip,
|
||||
const AssetManager2* am) {
|
||||
const auto ref_table = am->GetDynamicRefTableForCookie(0);
|
||||
if (ref_table == nullptr) {
|
||||
return Error("failed to find dynamic ref table for cookie 0");
|
||||
}
|
||||
|
||||
ref_table->lookupResourceId(&id);
|
||||
auto value = am->GetResource(id);
|
||||
if (!value.has_value()) {
|
||||
return Error("failed to find resource for id 0x%08x", id);
|
||||
}
|
||||
|
||||
if (value->type != Res_value::TYPE_STRING) {
|
||||
return Error("resource for is 0x%08x is not a file", id);
|
||||
}
|
||||
|
||||
auto string_pool = am->GetStringPoolForCookie(value->cookie);
|
||||
auto file = string_pool->string8ObjectAt(value->data);
|
||||
if (!file.has_value()) {
|
||||
return Error("failed to find string for index %d", value->data);
|
||||
}
|
||||
|
||||
return OpenXmlParser(file->c_str(), zip);
|
||||
}
|
||||
|
||||
Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const ZipAssetsProvider* zip,
|
||||
const std::string& name) {
|
||||
Result<XmlParser> xml = OpenXmlParser("AndroidManifest.xml", zip);
|
||||
if (!xml) {
|
||||
return xml.GetError();
|
||||
}
|
||||
|
||||
auto manifest_it = xml->tree_iterator();
|
||||
if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") {
|
||||
return Error("root element tag is not <manifest> in AndroidManifest.xml");
|
||||
}
|
||||
|
||||
std::string package_name;
|
||||
if (auto result_str = manifest_it->GetAttributeStringValue("package")) {
|
||||
package_name = *result_str;
|
||||
} else {
|
||||
return result_str.GetError();
|
||||
}
|
||||
|
||||
for (auto&& it : manifest_it) {
|
||||
if (it.event() != XmlParser::Event::START_TAG || it.name() != "overlay") {
|
||||
continue;
|
||||
}
|
||||
|
||||
OverlayManifestInfo info{};
|
||||
info.package_name = package_name;
|
||||
if (auto result_str = it.GetAttributeStringValue(kAttrName, "android:name")) {
|
||||
if (*result_str != name) {
|
||||
// A value for android:name was found, but either a the name does not match the requested
|
||||
// name, or an <overlay> tag with no name was requested.
|
||||
continue;
|
||||
}
|
||||
info.name = *result_str;
|
||||
} else if (!name.empty()) {
|
||||
// This tag does not have a value for android:name, but an <overlay> tag with a specific name
|
||||
// has been requested.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto result_str = it.GetAttributeStringValue(kAttrTargetPackage, "android:targetPackage")) {
|
||||
info.target_package = *result_str;
|
||||
} else {
|
||||
return Error("android:targetPackage missing from <overlay> in AndroidManifest.xml");
|
||||
}
|
||||
|
||||
if (auto result_str = it.GetAttributeStringValue(kAttrTargetName, "android:targetName")) {
|
||||
info.target_name = *result_str;
|
||||
}
|
||||
|
||||
if (auto result_value = it.GetAttributeValue(kAttrResourcesMap, "android:resourcesMap")) {
|
||||
if (utils::IsReference((*result_value).dataType)) {
|
||||
info.resource_mapping = (*result_value).data;
|
||||
} else {
|
||||
return Error("android:resourcesMap is not a reference in AndroidManifest.xml");
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
return Error("<overlay> with android:name \"%s\" missing from AndroidManifest.xml", name.c_str());
|
||||
}
|
||||
|
||||
Result<OverlayData> CreateResourceMapping(ResourceId id, const ZipAssetsProvider* zip,
|
||||
const AssetManager2* overlay_am,
|
||||
const LoadedArsc* overlay_arsc,
|
||||
const LoadedPackage* overlay_package) {
|
||||
auto parser = OpenXmlParser(id, zip, overlay_am);
|
||||
if (!parser) {
|
||||
return parser.GetError();
|
||||
}
|
||||
|
||||
OverlayData overlay_data{};
|
||||
const uint32_t string_pool_offset = overlay_arsc->GetStringPool()->size();
|
||||
const uint8_t package_id = overlay_package->GetPackageId();
|
||||
auto root_it = parser->tree_iterator();
|
||||
if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") {
|
||||
return Error("root element is not <overlay> tag");
|
||||
}
|
||||
|
||||
auto overlay_it_end = root_it.end();
|
||||
for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) {
|
||||
if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) {
|
||||
return Error("failed to parse overlay xml document");
|
||||
}
|
||||
|
||||
if (overlay_it->event() != XmlParser::Event::START_TAG) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (overlay_it->name() != "item") {
|
||||
return Error("unexpected tag <%s> in <overlay>", overlay_it->name().c_str());
|
||||
}
|
||||
|
||||
Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target");
|
||||
if (!target_resource) {
|
||||
return Error(R"(<item> tag missing expected attribute "target")");
|
||||
}
|
||||
|
||||
Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value");
|
||||
if (!overlay_resource) {
|
||||
return Error(R"(<item> tag missing expected attribute "value")");
|
||||
}
|
||||
|
||||
if (overlay_resource->dataType == Res_value::TYPE_STRING) {
|
||||
overlay_resource->data += string_pool_offset;
|
||||
}
|
||||
|
||||
if (utils::IsReference(overlay_resource->dataType)) {
|
||||
// Only rewrite resources defined within the overlay package to their corresponding target
|
||||
// resource ids at runtime.
|
||||
bool rewrite_id = package_id == EXTRACT_PACKAGE(overlay_resource->data);
|
||||
overlay_data.pairs.emplace_back(OverlayData::Value{
|
||||
*target_resource, OverlayData::ResourceIdValue{overlay_resource->data, rewrite_id}});
|
||||
} else {
|
||||
overlay_data.pairs.emplace_back(
|
||||
OverlayData::Value{*target_resource, TargetValue{.data_type = overlay_resource->dataType,
|
||||
.data_value = overlay_resource->data}});
|
||||
}
|
||||
}
|
||||
|
||||
const auto& string_pool = parser->get_strings();
|
||||
const uint32_t string_pool_data_length = string_pool.bytes();
|
||||
overlay_data.string_pool_data = OverlayData::InlineStringPoolData{
|
||||
.data = std::unique_ptr<uint8_t[]>(new uint8_t[string_pool_data_length]),
|
||||
.data_length = string_pool_data_length,
|
||||
.string_pool_offset = string_pool_offset,
|
||||
};
|
||||
|
||||
// Overlays should not be incrementally installed, so calling unsafe_ptr is fine here.
|
||||
memcpy(overlay_data.string_pool_data->data.get(), string_pool.data().unsafe_ptr(),
|
||||
string_pool_data_length);
|
||||
return overlay_data;
|
||||
}
|
||||
|
||||
OverlayData CreateResourceMappingLegacy(const AssetManager2* overlay_am,
|
||||
const LoadedPackage* overlay_package) {
|
||||
OverlayData overlay_data{};
|
||||
for (const ResourceId overlay_resid : *overlay_package) {
|
||||
if (auto name = utils::ResToTypeEntryName(*overlay_am, overlay_resid)) {
|
||||
// Disable rewriting. Overlays did not support internal references before
|
||||
// android:resourcesMap. Do not introduce new behavior.
|
||||
overlay_data.pairs.emplace_back(OverlayData::Value{
|
||||
*name, OverlayData::ResourceIdValue{overlay_resid, false /* rewrite_id */}});
|
||||
}
|
||||
}
|
||||
return overlay_data;
|
||||
}
|
||||
|
||||
struct ResState {
|
||||
std::unique_ptr<ApkAssets> apk_assets;
|
||||
const LoadedArsc* arsc;
|
||||
const LoadedPackage* package;
|
||||
std::unique_ptr<AssetManager2> am;
|
||||
ZipAssetsProvider* zip_assets;
|
||||
|
||||
static Result<ResState> Initialize(std::unique_ptr<ZipAssetsProvider> zip) {
|
||||
ResState state;
|
||||
state.zip_assets = zip.get();
|
||||
if ((state.apk_assets = ApkAssets::Load(std::move(zip))) == nullptr) {
|
||||
return Error("failed to load apk asset");
|
||||
}
|
||||
|
||||
if ((state.arsc = state.apk_assets->GetLoadedArsc()) == nullptr) {
|
||||
return Error("failed to retrieve loaded arsc");
|
||||
}
|
||||
|
||||
if ((state.package = GetPackageAtIndex0(state.arsc)) == nullptr) {
|
||||
return Error("failed to retrieve loaded package at index 0");
|
||||
}
|
||||
|
||||
state.am = std::make_unique<AssetManager2>();
|
||||
if (!state.am->SetApkAssets({state.apk_assets.get()})) {
|
||||
return Error("failed to create asset manager");
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
struct ApkResourceContainer : public TargetResourceContainer, public OverlayResourceContainer {
|
||||
static Result<std::unique_ptr<ApkResourceContainer>> FromPath(const std::string& path);
|
||||
|
||||
// inherited from TargetResourceContainer
|
||||
Result<bool> DefinesOverlayable() const override;
|
||||
Result<const android::OverlayableInfo*> GetOverlayableInfo(ResourceId id) const override;
|
||||
Result<ResourceId> GetResourceId(const std::string& name) const override;
|
||||
|
||||
// inherited from OverlayResourceContainer
|
||||
Result<OverlayData> GetOverlayData(const OverlayManifestInfo& info) const override;
|
||||
Result<OverlayManifestInfo> FindOverlayInfo(const std::string& name) const override;
|
||||
|
||||
// inherited from ResourceContainer
|
||||
Result<uint32_t> GetCrc() const override;
|
||||
Result<std::string> GetResourceName(ResourceId id) const override;
|
||||
const std::string& GetPath() const override;
|
||||
|
||||
~ApkResourceContainer() override = default;
|
||||
|
||||
private:
|
||||
ApkResourceContainer(std::unique_ptr<ZipAssetsProvider> zip_assets, std::string path);
|
||||
|
||||
Result<const ResState*> GetState() const;
|
||||
ZipAssetsProvider* GetZipAssets() const;
|
||||
|
||||
mutable std::variant<std::unique_ptr<ZipAssetsProvider>, ResState> state_;
|
||||
std::string path_;
|
||||
};
|
||||
|
||||
ApkResourceContainer::ApkResourceContainer(std::unique_ptr<ZipAssetsProvider> zip_assets,
|
||||
std::string path)
|
||||
: state_(std::move(zip_assets)), path_(std::move(path)) {
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<ApkResourceContainer>> ApkResourceContainer::FromPath(
|
||||
const std::string& path) {
|
||||
auto zip_assets = ZipAssetsProvider::Create(path);
|
||||
if (zip_assets == nullptr) {
|
||||
return Error("failed to load zip assets");
|
||||
}
|
||||
return std::unique_ptr<ApkResourceContainer>(
|
||||
new ApkResourceContainer(std::move(zip_assets), path));
|
||||
}
|
||||
|
||||
Result<const ResState*> ApkResourceContainer::GetState() const {
|
||||
if (auto state = std::get_if<ResState>(&state_); state != nullptr) {
|
||||
return state;
|
||||
}
|
||||
|
||||
auto state =
|
||||
ResState::Initialize(std::move(std::get<std::unique_ptr<ZipAssetsProvider>>(state_)));
|
||||
if (!state) {
|
||||
return state.GetError();
|
||||
}
|
||||
|
||||
state_ = std::move(*state);
|
||||
return &std::get<ResState>(state_);
|
||||
}
|
||||
|
||||
ZipAssetsProvider* ApkResourceContainer::GetZipAssets() const {
|
||||
if (auto zip = std::get_if<std::unique_ptr<ZipAssetsProvider>>(&state_); zip != nullptr) {
|
||||
return zip->get();
|
||||
}
|
||||
return std::get<ResState>(state_).zip_assets;
|
||||
}
|
||||
|
||||
Result<bool> ApkResourceContainer::DefinesOverlayable() const {
|
||||
auto state = GetState();
|
||||
if (!state) {
|
||||
return state.GetError();
|
||||
}
|
||||
return (*state)->package->DefinesOverlayable();
|
||||
}
|
||||
|
||||
Result<const android::OverlayableInfo*> ApkResourceContainer::GetOverlayableInfo(
|
||||
ResourceId id) const {
|
||||
auto state = GetState();
|
||||
if (!state) {
|
||||
return state.GetError();
|
||||
}
|
||||
return (*state)->package->GetOverlayableInfo(id);
|
||||
}
|
||||
|
||||
Result<OverlayManifestInfo> ApkResourceContainer::FindOverlayInfo(const std::string& name) const {
|
||||
return ExtractOverlayManifestInfo(GetZipAssets(), name);
|
||||
}
|
||||
|
||||
Result<OverlayData> ApkResourceContainer::GetOverlayData(const OverlayManifestInfo& info) const {
|
||||
const auto state = GetState();
|
||||
if (!state) {
|
||||
return state.GetError();
|
||||
}
|
||||
|
||||
if (info.resource_mapping != 0) {
|
||||
return CreateResourceMapping(info.resource_mapping, GetZipAssets(), (*state)->am.get(),
|
||||
(*state)->arsc, (*state)->package);
|
||||
}
|
||||
return CreateResourceMappingLegacy((*state)->am.get(), (*state)->package);
|
||||
}
|
||||
|
||||
Result<uint32_t> ApkResourceContainer::GetCrc() const {
|
||||
return CalculateCrc(GetZipAssets());
|
||||
}
|
||||
|
||||
const std::string& ApkResourceContainer::GetPath() const {
|
||||
return path_;
|
||||
}
|
||||
|
||||
Result<ResourceId> ApkResourceContainer::GetResourceId(const std::string& name) const {
|
||||
auto state = GetState();
|
||||
if (!state) {
|
||||
return state.GetError();
|
||||
}
|
||||
auto id = (*state)->am->GetResourceId(name, "", (*state)->package->GetPackageName());
|
||||
if (!id.has_value()) {
|
||||
return Error("failed to find resource '%s'", name.c_str());
|
||||
}
|
||||
|
||||
// Retrieve the compile-time resource id of the target resource.
|
||||
return REWRITE_PACKAGE(*id, (*state)->package->GetPackageId());
|
||||
}
|
||||
|
||||
Result<std::string> ApkResourceContainer::GetResourceName(ResourceId id) const {
|
||||
auto state = GetState();
|
||||
if (!state) {
|
||||
return state.GetError();
|
||||
}
|
||||
return utils::ResToTypeEntryName(*(*state)->am, id);
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<TargetResourceContainer>> TargetResourceContainer::FromPath(
|
||||
std::string path) {
|
||||
auto result = ApkResourceContainer::FromPath(path);
|
||||
if (!result) {
|
||||
return result.GetError();
|
||||
}
|
||||
return std::unique_ptr<TargetResourceContainer>(result->release());
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<OverlayResourceContainer>> OverlayResourceContainer::FromPath(
|
||||
std::string path) {
|
||||
// Load the path as a fabricated overlay if the file magic indicates this is a fabricated overlay.
|
||||
if (android::IsFabricatedOverlay(path)) {
|
||||
auto result = FabricatedOverlayContainer::FromPath(path);
|
||||
if (!result) {
|
||||
return result.GetError();
|
||||
}
|
||||
return std::unique_ptr<OverlayResourceContainer>(result->release());
|
||||
}
|
||||
|
||||
// Fallback to loading the container as an APK.
|
||||
auto result = ApkResourceContainer::FromPath(path);
|
||||
if (!result) {
|
||||
return result.GetError();
|
||||
}
|
||||
return std::unique_ptr<OverlayResourceContainer>(result->release());
|
||||
}
|
||||
|
||||
} // namespace android::idmap2
|
||||
@@ -30,19 +30,12 @@
|
||||
|
||||
using android::base::StringPrintf;
|
||||
using android::idmap2::utils::BitmaskToPolicies;
|
||||
using android::idmap2::utils::IsReference;
|
||||
using android::idmap2::utils::ResToTypeEntryName;
|
||||
using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
|
||||
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
namespace {
|
||||
|
||||
#define REWRITE_PACKAGE(resid, package_id) \
|
||||
(((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U))
|
||||
#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24)
|
||||
|
||||
std::string ConcatPolicies(const std::vector<std::string>& policies) {
|
||||
std::string message;
|
||||
for (const std::string& policy : policies) {
|
||||
@@ -55,11 +48,11 @@ std::string ConcatPolicies(const std::vector<std::string>& policies) {
|
||||
return message;
|
||||
}
|
||||
|
||||
Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
|
||||
Result<Unit> CheckOverlayable(const TargetResourceContainer& target,
|
||||
const OverlayManifestInfo& overlay_info,
|
||||
const PolicyBitmask& fulfilled_policies,
|
||||
const ResourceId& target_resource) {
|
||||
static constexpr const PolicyBitmask sDefaultPolicies =
|
||||
constexpr const PolicyBitmask kDefaultPolicies =
|
||||
PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION | PolicyFlags::SYSTEM_PARTITION |
|
||||
PolicyFlags::VENDOR_PARTITION | PolicyFlags::PRODUCT_PARTITION | PolicyFlags::SIGNATURE |
|
||||
PolicyFlags::CONFIG_SIGNATURE;
|
||||
@@ -68,8 +61,13 @@ Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
|
||||
// the overlay is preinstalled, signed with the same signature as the target or signed with the
|
||||
// same signature as reference package defined in SystemConfig under 'overlay-config-signature'
|
||||
// tag.
|
||||
if (!target_package.DefinesOverlayable()) {
|
||||
return (sDefaultPolicies & fulfilled_policies) != 0
|
||||
const Result<bool> defines_overlayable = target.DefinesOverlayable();
|
||||
if (!defines_overlayable) {
|
||||
return Error(defines_overlayable.GetError(), "unable to retrieve overlayable info");
|
||||
}
|
||||
|
||||
if (!*defines_overlayable) {
|
||||
return (kDefaultPolicies & fulfilled_policies) != 0
|
||||
? Result<Unit>({})
|
||||
: Error(
|
||||
"overlay must be preinstalled, signed with the same signature as the target,"
|
||||
@@ -77,367 +75,112 @@ Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
|
||||
" <overlay-config-signature>.");
|
||||
}
|
||||
|
||||
const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource);
|
||||
if (overlayable_info == nullptr) {
|
||||
const auto overlayable_info = target.GetOverlayableInfo(target_resource);
|
||||
if (!overlayable_info) {
|
||||
return overlayable_info.GetError();
|
||||
}
|
||||
|
||||
if (*overlayable_info == nullptr) {
|
||||
// Do not allow non-overlayable resources to be overlaid.
|
||||
return Error("target resource has no overlayable declaration");
|
||||
}
|
||||
|
||||
if (overlay_info.target_name != overlayable_info->name) {
|
||||
if (overlay_info.target_name != (*overlayable_info)->name) {
|
||||
// If the overlay supplies a target overlayable name, the resource must belong to the
|
||||
// overlayable defined with the specified name to be overlaid.
|
||||
return Error(R"(<overlay> android:targetName "%s" does not match overlayable name "%s")",
|
||||
overlay_info.target_name.c_str(), overlayable_info->name.c_str());
|
||||
overlay_info.target_name.c_str(), (*overlayable_info)->name.c_str());
|
||||
}
|
||||
|
||||
// Enforce policy restrictions if the resource is declared as overlayable.
|
||||
if ((overlayable_info->policy_flags & fulfilled_policies) == 0) {
|
||||
if (((*overlayable_info)->policy_flags & fulfilled_policies) == 0) {
|
||||
return Error(R"(overlay with policies "%s" does not fulfill any overlayable policies "%s")",
|
||||
ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(),
|
||||
ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str());
|
||||
ConcatPolicies(BitmaskToPolicies((*overlayable_info)->policy_flags)).c_str());
|
||||
}
|
||||
|
||||
return Result<Unit>({});
|
||||
}
|
||||
|
||||
// TODO(martenkongstad): scan for package name instead of assuming package at index 0
|
||||
//
|
||||
// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
|
||||
// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
|
||||
// this assumption tends to work out. That said, the correct thing to do is to scan
|
||||
// resources.arsc for a package with a given name as read from the package manifest instead of
|
||||
// relying on a hard-coded index. This however requires storing the package name in the idmap
|
||||
// header, which in turn requires incrementing the idmap version. Because the initial version of
|
||||
// idmap2 is compatible with idmap, this will have to wait for now.
|
||||
const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
|
||||
const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
|
||||
if (packages.empty()) {
|
||||
return nullptr;
|
||||
std::string GetDebugResourceName(const ResourceContainer& container, ResourceId resid) {
|
||||
auto name = container.GetResourceName(resid);
|
||||
if (name) {
|
||||
return *name;
|
||||
}
|
||||
int id = packages[0]->GetPackageId();
|
||||
return loaded_arsc.GetPackageById(id);
|
||||
return StringPrintf("0x%08x", resid);
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<Asset>> OpenNonAssetFromResource(const ResourceId& resource_id,
|
||||
const AssetManager2& asset_manager) {
|
||||
auto value = asset_manager.GetResource(resource_id);
|
||||
if (!value.has_value()) {
|
||||
return Error("failed to find resource for id 0x%08x", resource_id);
|
||||
}
|
||||
|
||||
if (value->type != Res_value::TYPE_STRING) {
|
||||
return Error("resource for is 0x%08x is not a file", resource_id);
|
||||
}
|
||||
|
||||
auto string_pool = asset_manager.GetStringPoolForCookie(value->cookie);
|
||||
auto file = string_pool->string8ObjectAt(value->data);
|
||||
if (!file.has_value()) {
|
||||
return Error("failed to find string for index %d", value->data);
|
||||
}
|
||||
|
||||
// Load the overlay resource mappings from the file specified using android:resourcesMap.
|
||||
auto asset = asset_manager.OpenNonAsset(file->c_str(), Asset::AccessMode::ACCESS_BUFFER);
|
||||
if (asset == nullptr) {
|
||||
return Error("file \"%s\" not found", file->c_str());
|
||||
}
|
||||
|
||||
return asset;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManager2* target_am,
|
||||
const LoadedPackage* target_package,
|
||||
const LoadedPackage* overlay_package,
|
||||
size_t string_pool_offset,
|
||||
const XmlParser& overlay_parser,
|
||||
LogInfo& log_info) {
|
||||
ResourceMapping resource_mapping;
|
||||
auto root_it = overlay_parser.tree_iterator();
|
||||
if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") {
|
||||
return Error("root element is not <overlay> tag");
|
||||
Result<ResourceMapping> ResourceMapping::FromContainers(const TargetResourceContainer& target,
|
||||
const OverlayResourceContainer& overlay,
|
||||
const OverlayManifestInfo& overlay_info,
|
||||
const PolicyBitmask& fulfilled_policies,
|
||||
bool enforce_overlayable,
|
||||
LogInfo& log_info) {
|
||||
auto overlay_data = overlay.GetOverlayData(overlay_info);
|
||||
if (!overlay_data) {
|
||||
return overlay_data.GetError();
|
||||
}
|
||||
|
||||
const uint8_t target_package_id = target_package->GetPackageId();
|
||||
const uint8_t overlay_package_id = overlay_package->GetPackageId();
|
||||
auto overlay_it_end = root_it.end();
|
||||
for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) {
|
||||
if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) {
|
||||
return Error("failed to parse overlay xml document");
|
||||
}
|
||||
|
||||
if (overlay_it->event() != XmlParser::Event::START_TAG) {
|
||||
ResourceMapping mapping;
|
||||
for (const auto& overlay_pair : overlay_data->pairs) {
|
||||
const auto target_resid = target.GetResourceId(overlay_pair.resource_name);
|
||||
if (!target_resid) {
|
||||
log_info.Warning(LogMessage() << target_resid.GetErrorMessage());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (overlay_it->name() != "item") {
|
||||
return Error("unexpected tag <%s> in <overlay>", overlay_it->name().c_str());
|
||||
if (enforce_overlayable) {
|
||||
// Filter out resources the overlay is not allowed to override.
|
||||
auto overlayable = CheckOverlayable(target, overlay_info, fulfilled_policies, *target_resid);
|
||||
if (!overlayable) {
|
||||
log_info.Warning(LogMessage() << "overlay '" << overlay.GetPath()
|
||||
<< "' is not allowed to overlay resource '"
|
||||
<< GetDebugResourceName(target, *target_resid)
|
||||
<< "' in target: " << overlayable.GetErrorMessage());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target");
|
||||
if (!target_resource) {
|
||||
return Error(R"(<item> tag missing expected attribute "target")");
|
||||
}
|
||||
|
||||
Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value");
|
||||
if (!overlay_resource) {
|
||||
return Error(R"(<item> tag missing expected attribute "value")");
|
||||
}
|
||||
|
||||
auto target_id_result =
|
||||
target_am->GetResourceId(*target_resource, "", target_package->GetPackageName());
|
||||
if (!target_id_result.has_value()) {
|
||||
log_info.Warning(LogMessage() << "failed to find resource \"" << *target_resource
|
||||
<< "\" in target resources");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Retrieve the compile-time resource id of the target resource.
|
||||
uint32_t target_id = REWRITE_PACKAGE(*target_id_result, target_package_id);
|
||||
|
||||
if (overlay_resource->dataType == Res_value::TYPE_STRING) {
|
||||
overlay_resource->data += string_pool_offset;
|
||||
}
|
||||
|
||||
if (IsReference(overlay_resource->dataType)) {
|
||||
// Only rewrite resources defined within the overlay package to their corresponding target
|
||||
// resource ids at runtime.
|
||||
bool rewrite_reference = overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data);
|
||||
resource_mapping.AddMapping(target_id, overlay_resource->data, rewrite_reference);
|
||||
} else {
|
||||
resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data);
|
||||
if (auto result = mapping.AddMapping(*target_resid, overlay_pair.value); !result) {
|
||||
return Error(result.GetError(), "failed to add mapping for '%s'",
|
||||
GetDebugResourceName(target, *target_resid).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return resource_mapping;
|
||||
auto& string_pool_data = overlay_data->string_pool_data;
|
||||
if (string_pool_data.has_value()) {
|
||||
mapping.string_pool_offset_ = string_pool_data->string_pool_offset;
|
||||
mapping.string_pool_data_ = std::move(string_pool_data->data);
|
||||
mapping.string_pool_data_length_ = string_pool_data->data_length;
|
||||
}
|
||||
|
||||
return std::move(mapping);
|
||||
}
|
||||
|
||||
Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy(
|
||||
const AssetManager2* target_am, const AssetManager2* overlay_am,
|
||||
const LoadedPackage* target_package, const LoadedPackage* overlay_package, LogInfo& log_info) {
|
||||
ResourceMapping resource_mapping;
|
||||
const uint8_t target_package_id = target_package->GetPackageId();
|
||||
const auto end = overlay_package->end();
|
||||
for (auto iter = overlay_package->begin(); iter != end; ++iter) {
|
||||
const ResourceId overlay_resid = *iter;
|
||||
Result<std::string> name = utils::ResToTypeEntryName(*overlay_am, overlay_resid);
|
||||
if (!name) {
|
||||
continue;
|
||||
Result<Unit> ResourceMapping::AddMapping(
|
||||
ResourceId target_resource,
|
||||
const std::variant<OverlayData::ResourceIdValue, TargetValue>& value) {
|
||||
if (target_map_.find(target_resource) != target_map_.end()) {
|
||||
return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
|
||||
}
|
||||
|
||||
// TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
|
||||
// runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
|
||||
|
||||
if (auto overlay_resource = std::get_if<OverlayData::ResourceIdValue>(&value)) {
|
||||
target_map_.insert(std::make_pair(target_resource, overlay_resource->overlay_id));
|
||||
if (overlay_resource->rewrite_id) {
|
||||
// An overlay resource can override multiple target resources at once. Rewrite the overlay
|
||||
// resource as the first target resource it overrides.
|
||||
overlay_map_.insert(std::make_pair(overlay_resource->overlay_id, target_resource));
|
||||
}
|
||||
|
||||
// Find the resource with the same type and entry name within the target package.
|
||||
const std::string full_name =
|
||||
base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str());
|
||||
auto target_resource_result = target_am->GetResourceId(full_name);
|
||||
if (!target_resource_result.has_value()) {
|
||||
log_info.Warning(LogMessage()
|
||||
<< "failed to find resource \"" << full_name << "\" in target resources");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Retrieve the compile-time resource id of the target resource.
|
||||
ResourceId target_resource = REWRITE_PACKAGE(*target_resource_result, target_package_id);
|
||||
resource_mapping.AddMapping(target_resource, overlay_resid,
|
||||
false /* rewrite_overlay_reference */);
|
||||
}
|
||||
|
||||
return resource_mapping;
|
||||
}
|
||||
|
||||
void ResourceMapping::FilterOverlayableResources(const AssetManager2* target_am,
|
||||
const LoadedPackage* target_package,
|
||||
const LoadedPackage* overlay_package,
|
||||
const OverlayManifestInfo& overlay_info,
|
||||
const PolicyBitmask& fulfilled_policies,
|
||||
LogInfo& log_info) {
|
||||
std::set<ResourceId> remove_ids;
|
||||
for (const auto& target_map : target_map_) {
|
||||
const ResourceId target_resid = target_map.first;
|
||||
Result<Unit> success =
|
||||
CheckOverlayable(*target_package, overlay_info, fulfilled_policies, target_resid);
|
||||
if (success) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Attempting to overlay a resource that is not allowed to be overlaid is treated as a
|
||||
// warning.
|
||||
Result<std::string> name = utils::ResToTypeEntryName(*target_am, target_resid);
|
||||
if (!name) {
|
||||
name = StringPrintf("0x%08x", target_resid);
|
||||
}
|
||||
|
||||
log_info.Warning(LogMessage() << "overlay \"" << overlay_package->GetPackageName()
|
||||
<< "\" is not allowed to overlay resource \"" << *name
|
||||
<< "\" in target: " << success.GetErrorMessage());
|
||||
|
||||
remove_ids.insert(target_resid);
|
||||
}
|
||||
|
||||
for (const ResourceId target_resid : remove_ids) {
|
||||
RemoveMapping(target_resid);
|
||||
}
|
||||
}
|
||||
|
||||
Result<ResourceMapping> ResourceMapping::FromApkAssets(const ApkAssets& target_apk_assets,
|
||||
const ApkAssets& overlay_apk_assets,
|
||||
const OverlayManifestInfo& overlay_info,
|
||||
const PolicyBitmask& fulfilled_policies,
|
||||
bool enforce_overlayable,
|
||||
LogInfo& log_info) {
|
||||
AssetManager2 target_asset_manager;
|
||||
if (!target_asset_manager.SetApkAssets({&target_apk_assets})) {
|
||||
return Error("failed to create target asset manager");
|
||||
}
|
||||
|
||||
AssetManager2 overlay_asset_manager;
|
||||
if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets})) {
|
||||
return Error("failed to create overlay asset manager");
|
||||
}
|
||||
|
||||
const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
|
||||
if (target_arsc == nullptr) {
|
||||
return Error("failed to load target resources.arsc");
|
||||
}
|
||||
|
||||
const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
|
||||
if (overlay_arsc == nullptr) {
|
||||
return Error("failed to load overlay resources.arsc");
|
||||
}
|
||||
|
||||
const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
|
||||
if (target_pkg == nullptr) {
|
||||
return Error("failed to load target package from resources.arsc");
|
||||
}
|
||||
|
||||
const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
|
||||
if (overlay_pkg == nullptr) {
|
||||
return Error("failed to load overlay package from resources.arsc");
|
||||
}
|
||||
|
||||
size_t string_pool_data_length = 0U;
|
||||
size_t string_pool_offset = 0U;
|
||||
std::unique_ptr<uint8_t[]> string_pool_data;
|
||||
Result<ResourceMapping> resource_mapping = {{}};
|
||||
if (overlay_info.resource_mapping != 0U) {
|
||||
// Use the dynamic reference table to find the assigned resource id of the map xml.
|
||||
const auto& ref_table = overlay_asset_manager.GetDynamicRefTableForCookie(0);
|
||||
uint32_t resource_mapping_id = overlay_info.resource_mapping;
|
||||
ref_table->lookupResourceId(&resource_mapping_id);
|
||||
|
||||
// Load the overlay resource mappings from the file specified using android:resourcesMap.
|
||||
auto asset = OpenNonAssetFromResource(resource_mapping_id, overlay_asset_manager);
|
||||
if (!asset) {
|
||||
return Error("failed opening xml for android:resourcesMap: %s",
|
||||
asset.GetErrorMessage().c_str());
|
||||
}
|
||||
|
||||
auto parser =
|
||||
XmlParser::Create((*asset)->getBuffer(true /* wordAligned*/), (*asset)->getLength());
|
||||
if (!parser) {
|
||||
return Error("failed opening ResXMLTree");
|
||||
}
|
||||
|
||||
// Copy the xml string pool data before the parse goes out of scope.
|
||||
auto& string_pool = (*parser)->get_strings();
|
||||
string_pool_data_length = string_pool.bytes();
|
||||
string_pool_data.reset(new uint8_t[string_pool_data_length]);
|
||||
|
||||
// Overlays should not be incrementally installed, so calling unsafe_ptr is fine here.
|
||||
memcpy(string_pool_data.get(), string_pool.data().unsafe_ptr(), string_pool_data_length);
|
||||
|
||||
// Offset string indices by the size of the overlay resource table string pool.
|
||||
string_pool_offset = overlay_arsc->GetStringPool()->size();
|
||||
|
||||
resource_mapping = CreateResourceMapping(&target_asset_manager, target_pkg, overlay_pkg,
|
||||
string_pool_offset, *(*parser), log_info);
|
||||
} else {
|
||||
// If no file is specified using android:resourcesMap, it is assumed that the overlay only
|
||||
// defines resources intended to override target resources of the same type and name.
|
||||
resource_mapping = CreateResourceMappingLegacy(&target_asset_manager, &overlay_asset_manager,
|
||||
target_pkg, overlay_pkg, log_info);
|
||||
auto overlay_value = std::get<TargetValue>(value);
|
||||
target_map_.insert(std::make_pair(target_resource, overlay_value));
|
||||
}
|
||||
|
||||
if (!resource_mapping) {
|
||||
return resource_mapping.GetError();
|
||||
}
|
||||
|
||||
if (enforce_overlayable) {
|
||||
// Filter out resources the overlay is not allowed to override.
|
||||
(*resource_mapping)
|
||||
.FilterOverlayableResources(&target_asset_manager, target_pkg, overlay_pkg, overlay_info,
|
||||
fulfilled_policies, log_info);
|
||||
}
|
||||
|
||||
resource_mapping->target_package_id_ = target_pkg->GetPackageId();
|
||||
resource_mapping->overlay_package_id_ = overlay_pkg->GetPackageId();
|
||||
resource_mapping->string_pool_offset_ = string_pool_offset;
|
||||
resource_mapping->string_pool_data_ = std::move(string_pool_data);
|
||||
resource_mapping->string_pool_data_length_ = string_pool_data_length;
|
||||
return std::move(*resource_mapping);
|
||||
}
|
||||
|
||||
OverlayResourceMap ResourceMapping::GetOverlayToTargetMap() const {
|
||||
// An overlay resource can override multiple target resources at once. Rewrite the overlay
|
||||
// resource as the first target resource it overrides.
|
||||
OverlayResourceMap map;
|
||||
for (const auto& mappings : overlay_map_) {
|
||||
map.insert(std::make_pair(mappings.first, mappings.second));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource, ResourceId overlay_resource,
|
||||
bool rewrite_overlay_reference) {
|
||||
if (target_map_.find(target_resource) != target_map_.end()) {
|
||||
return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
|
||||
}
|
||||
|
||||
// TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
|
||||
// runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
|
||||
|
||||
target_map_.insert(std::make_pair(target_resource, overlay_resource));
|
||||
|
||||
if (rewrite_overlay_reference) {
|
||||
overlay_map_.insert(std::make_pair(overlay_resource, target_resource));
|
||||
}
|
||||
return Unit{};
|
||||
}
|
||||
|
||||
Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource,
|
||||
TargetValue::DataType data_type,
|
||||
TargetValue::DataValue data_value) {
|
||||
if (target_map_.find(target_resource) != target_map_.end()) {
|
||||
return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
|
||||
}
|
||||
|
||||
// TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
|
||||
// runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
|
||||
|
||||
target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value}));
|
||||
return Unit{};
|
||||
}
|
||||
|
||||
void ResourceMapping::RemoveMapping(ResourceId target_resource) {
|
||||
auto target_iter = target_map_.find(target_resource);
|
||||
if (target_iter == target_map_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto value = target_iter->second;
|
||||
target_map_.erase(target_iter);
|
||||
|
||||
const ResourceId* overlay_resource = std::get_if<ResourceId>(&value);
|
||||
if (overlay_resource == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto overlay_iter = overlay_map_.equal_range(*overlay_resource);
|
||||
for (auto i = overlay_iter.first; i != overlay_iter.second; ++i) {
|
||||
if (i->second == target_resource) {
|
||||
overlay_map_.erase(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android::idmap2
|
||||
|
||||
@@ -17,27 +17,16 @@
|
||||
#include "idmap2/ResourceUtils.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "androidfw/StringPiece.h"
|
||||
#include "androidfw/Util.h"
|
||||
#include "idmap2/Result.h"
|
||||
#include "idmap2/XmlParser.h"
|
||||
#include "idmap2/ZipFile.h"
|
||||
|
||||
using android::StringPiece16;
|
||||
using android::idmap2::Result;
|
||||
using android::idmap2::XmlParser;
|
||||
using android::idmap2::ZipFile;
|
||||
using android::util::Utf16ToUtf8;
|
||||
|
||||
namespace android::idmap2::utils {
|
||||
namespace {
|
||||
constexpr ResourceId kAttrName = 0x01010003;
|
||||
constexpr ResourceId kAttrResourcesMap = 0x01010609;
|
||||
constexpr ResourceId kAttrTargetName = 0x0101044d;
|
||||
constexpr ResourceId kAttrTargetPackage = 0x01010021;
|
||||
} // namespace
|
||||
|
||||
bool IsReference(uint8_t data_type) {
|
||||
return data_type == Res_value::TYPE_REFERENCE || data_type == Res_value::TYPE_DYNAMIC_REFERENCE;
|
||||
@@ -97,71 +86,4 @@ Result<std::string> ResToTypeEntryName(const AssetManager2& am, uint32_t resid)
|
||||
return out;
|
||||
}
|
||||
|
||||
Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
|
||||
const std::string& name) {
|
||||
std::unique_ptr<const ZipFile> zip = ZipFile::Open(path);
|
||||
if (!zip) {
|
||||
return Error("failed to open %s as a zip file", path.c_str());
|
||||
}
|
||||
|
||||
std::unique_ptr<const MemoryChunk> entry = zip->Uncompress("AndroidManifest.xml");
|
||||
if (!entry) {
|
||||
return Error("failed to uncompress AndroidManifest.xml from %s", path.c_str());
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<const XmlParser>> xml = XmlParser::Create(entry->buf, entry->size);
|
||||
if (!xml) {
|
||||
return Error("failed to parse AndroidManifest.xml from %s", path.c_str());
|
||||
}
|
||||
|
||||
auto manifest_it = (*xml)->tree_iterator();
|
||||
if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") {
|
||||
return Error("root element tag is not <manifest> in AndroidManifest.xml of %s", path.c_str());
|
||||
}
|
||||
|
||||
for (auto&& it : manifest_it) {
|
||||
if (it.event() != XmlParser::Event::START_TAG || it.name() != "overlay") {
|
||||
continue;
|
||||
}
|
||||
|
||||
OverlayManifestInfo info{};
|
||||
if (auto result_str = it.GetAttributeStringValue(kAttrName, "android:name")) {
|
||||
if (*result_str != name) {
|
||||
// A value for android:name was found, but either a the name does not match the requested
|
||||
// name, or an <overlay> tag with no name was requested.
|
||||
continue;
|
||||
}
|
||||
info.name = *result_str;
|
||||
} else if (!name.empty()) {
|
||||
// This tag does not have a value for android:name, but an <overlay> tag with a specific name
|
||||
// has been requested.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto result_str = it.GetAttributeStringValue(kAttrTargetPackage, "android:targetPackage")) {
|
||||
info.target_package = *result_str;
|
||||
} else {
|
||||
return Error("android:targetPackage missing from <overlay> of %s: %s", path.c_str(),
|
||||
result_str.GetErrorMessage().c_str());
|
||||
}
|
||||
|
||||
if (auto result_str = it.GetAttributeStringValue(kAttrTargetName, "android:targetName")) {
|
||||
info.target_name = *result_str;
|
||||
}
|
||||
|
||||
if (auto result_value = it.GetAttributeValue(kAttrResourcesMap, "android:resourcesMap")) {
|
||||
if (IsReference((*result_value).dataType)) {
|
||||
info.resource_mapping = (*result_value).data;
|
||||
} else {
|
||||
return Error("android:resourcesMap is not a reference in AndroidManifest.xml of %s",
|
||||
path.c_str());
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
return Error("<overlay> with android:name \"%s\" missing from AndroidManifest.xml of %s",
|
||||
name.c_str(), path.c_str());
|
||||
}
|
||||
|
||||
} // namespace android::idmap2::utils
|
||||
|
||||
@@ -151,16 +151,18 @@ Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string&
|
||||
return value ? GetStringValue(parser_, *value, name) : value.GetError();
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, size_t size,
|
||||
bool copy_data) {
|
||||
auto parser = std::unique_ptr<const XmlParser>(new XmlParser());
|
||||
if (parser->tree_.setTo(data, size, copy_data) != NO_ERROR) {
|
||||
XmlParser::XmlParser(std::unique_ptr<ResXMLTree> tree) : tree_(std::move(tree)) {
|
||||
}
|
||||
|
||||
Result<XmlParser> XmlParser::Create(const void* data, size_t size, bool copy_data) {
|
||||
auto tree = std::make_unique<ResXMLTree>();
|
||||
if (tree->setTo(data, size, copy_data) != NO_ERROR) {
|
||||
return Error("Malformed xml block");
|
||||
}
|
||||
|
||||
// Find the beginning of the first tag.
|
||||
XmlParser::Event event;
|
||||
while ((event = parser->tree_.next()) != XmlParser::Event::BAD_DOCUMENT &&
|
||||
while ((event = tree->next()) != XmlParser::Event::BAD_DOCUMENT &&
|
||||
event != XmlParser::Event::END_DOCUMENT && event != XmlParser::Event::START_TAG) {
|
||||
}
|
||||
|
||||
@@ -172,11 +174,7 @@ Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, siz
|
||||
return Error("Bad xml document");
|
||||
}
|
||||
|
||||
return parser;
|
||||
}
|
||||
|
||||
XmlParser::~XmlParser() {
|
||||
tree_.uninit();
|
||||
return XmlParser{std::move(tree)};
|
||||
}
|
||||
|
||||
} // namespace android::idmap2
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "idmap2/ZipFile.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "idmap2/Result.h"
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
std::unique_ptr<MemoryChunk> MemoryChunk::Allocate(size_t size) {
|
||||
void* ptr = ::operator new(sizeof(MemoryChunk) + size);
|
||||
std::unique_ptr<MemoryChunk> chunk(reinterpret_cast<MemoryChunk*>(ptr));
|
||||
chunk->size = size;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
std::unique_ptr<const ZipFile> ZipFile::Open(const std::string& path) {
|
||||
::ZipArchiveHandle handle;
|
||||
int32_t status = ::OpenArchive(path.c_str(), &handle);
|
||||
if (status != 0) {
|
||||
::CloseArchive(handle);
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<ZipFile>(new ZipFile(handle));
|
||||
}
|
||||
|
||||
ZipFile::~ZipFile() {
|
||||
::CloseArchive(handle_);
|
||||
}
|
||||
|
||||
std::unique_ptr<const MemoryChunk> ZipFile::Uncompress(const std::string& entryPath) const {
|
||||
::ZipEntry entry;
|
||||
int32_t status = ::FindEntry(handle_, entryPath, &entry);
|
||||
if (status != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<MemoryChunk> chunk = MemoryChunk::Allocate(entry.uncompressed_length);
|
||||
status = ::ExtractToMemory(handle_, &entry, chunk->buf, chunk->size);
|
||||
if (status != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
Result<uint32_t> ZipFile::Crc(const std::string& entryPath) const {
|
||||
::ZipEntry entry;
|
||||
int32_t status = ::FindEntry(handle_, entryPath, &entry);
|
||||
if (status != 0) {
|
||||
return Error("failed to find zip entry %s", entryPath.c_str());
|
||||
}
|
||||
return entry.crc32;
|
||||
}
|
||||
|
||||
} // namespace android::idmap2
|
||||
56
cmds/idmap2/libidmap2/proto/fabricated_v1.proto
Normal file
56
cmds/idmap2/libidmap2/proto/fabricated_v1.proto
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package android.idmap2.pb;
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
// All changes to the proto messages in this file MUST be backwards compatible. Backwards
|
||||
// incompatible changes will cause previously fabricated overlays to be considered corrupt by the
|
||||
// new proto message specification.
|
||||
message FabricatedOverlay {
|
||||
repeated ResourcePackage packages = 1;
|
||||
string name = 2;
|
||||
string package_name = 3;
|
||||
string target_package_name = 4;
|
||||
string target_overlayable = 5;
|
||||
}
|
||||
|
||||
message ResourcePackage {
|
||||
string name = 1;
|
||||
repeated ResourceType types = 2;
|
||||
}
|
||||
|
||||
message ResourceType {
|
||||
string name = 1;
|
||||
repeated ResourceEntry entries = 2;
|
||||
}
|
||||
|
||||
message ResourceEntry {
|
||||
string name = 1;
|
||||
oneof value {
|
||||
ResourceValue res_value = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message ResourceValue {
|
||||
// Corresponds with android::Res_value::dataType
|
||||
uint32 data_type = 1;
|
||||
// Corresponds with android::Res_value::data
|
||||
uint32 data_value = 2;
|
||||
}
|
||||
@@ -33,7 +33,7 @@ using ::testing::NotNull;
|
||||
namespace android::idmap2 {
|
||||
|
||||
TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) {
|
||||
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
|
||||
std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
|
||||
std::istringstream raw_stream(raw);
|
||||
|
||||
auto result1 = Idmap::FromBinaryStream(raw_stream);
|
||||
|
||||
138
cmds/idmap2/tests/FabricatedOverlayTests.cpp
Normal file
138
cmds/idmap2/tests/FabricatedOverlayTests.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <idmap2/FabricatedOverlay.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
TEST(FabricatedOverlayTests, OverlayInfo) {
|
||||
auto overlay =
|
||||
FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
|
||||
.SetOverlayable("TestResources")
|
||||
.Build();
|
||||
|
||||
ASSERT_TRUE(overlay);
|
||||
auto container = FabricatedOverlayContainer::FromOverlay(std::move(*overlay));
|
||||
auto info = container->FindOverlayInfo("SandTheme");
|
||||
ASSERT_TRUE(info);
|
||||
EXPECT_EQ("SandTheme", (*info).name);
|
||||
EXPECT_EQ("TestResources", (*info).target_name);
|
||||
|
||||
info = container->FindOverlayInfo("OceanTheme");
|
||||
ASSERT_FALSE(info);
|
||||
}
|
||||
|
||||
TEST(FabricatedOverlayTests, SetResourceValue) {
|
||||
auto overlay =
|
||||
FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
|
||||
.SetResourceValue("com.example.target:integer/int1", Res_value::TYPE_INT_DEC, 1U)
|
||||
.SetResourceValue("com.example.target.split:integer/int2", Res_value::TYPE_INT_DEC, 2U)
|
||||
.SetResourceValue("string/int3", Res_value::TYPE_REFERENCE, 0x7f010000)
|
||||
.Build();
|
||||
ASSERT_TRUE(overlay);
|
||||
auto container = FabricatedOverlayContainer::FromOverlay(std::move(*overlay));
|
||||
auto info = container->FindOverlayInfo("SandTheme");
|
||||
ASSERT_TRUE(info);
|
||||
ASSERT_TRUE((*info).target_name.empty());
|
||||
|
||||
auto crc = (*container).GetCrc();
|
||||
ASSERT_TRUE(crc) << crc.GetErrorMessage();
|
||||
EXPECT_NE(0U, *crc);
|
||||
|
||||
auto pairs = container->GetOverlayData(*info);
|
||||
ASSERT_TRUE(pairs);
|
||||
EXPECT_FALSE(pairs->string_pool_data.has_value());
|
||||
ASSERT_EQ(3U, pairs->pairs.size());
|
||||
|
||||
auto& it = pairs->pairs[0];
|
||||
ASSERT_EQ("com.example.target:integer/int1", it.resource_name);
|
||||
auto entry = std::get_if<TargetValue>(&it.value);
|
||||
ASSERT_NE(nullptr, entry);
|
||||
ASSERT_EQ(1U, entry->data_value);
|
||||
ASSERT_EQ(Res_value::TYPE_INT_DEC, entry->data_type);
|
||||
|
||||
it = pairs->pairs[1];
|
||||
ASSERT_EQ("com.example.target:string/int3", it.resource_name);
|
||||
entry = std::get_if<TargetValue>(&it.value);
|
||||
ASSERT_NE(nullptr, entry);
|
||||
ASSERT_EQ(0x7f010000, entry->data_value);
|
||||
ASSERT_EQ(Res_value::TYPE_REFERENCE, entry->data_type);
|
||||
|
||||
it = pairs->pairs[2];
|
||||
ASSERT_EQ("com.example.target.split:integer/int2", it.resource_name);
|
||||
entry = std::get_if<TargetValue>(&it.value);
|
||||
ASSERT_NE(nullptr, entry);
|
||||
ASSERT_EQ(2U, entry->data_value);
|
||||
ASSERT_EQ(Res_value::TYPE_INT_DEC, entry->data_type);
|
||||
}
|
||||
|
||||
TEST(FabricatedOverlayTests, SetResourceValueBadArgs) {
|
||||
{
|
||||
auto builder =
|
||||
FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
|
||||
.SetResourceValue("int1", Res_value::TYPE_INT_DEC, 1U);
|
||||
ASSERT_FALSE(builder.Build());
|
||||
}
|
||||
{
|
||||
auto builder =
|
||||
FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
|
||||
.SetResourceValue("com.example.target:int2", Res_value::TYPE_INT_DEC, 1U);
|
||||
ASSERT_FALSE(builder.Build());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FabricatedOverlayTests, SerializeAndDeserialize) {
|
||||
auto overlay =
|
||||
FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
|
||||
.SetOverlayable("TestResources")
|
||||
.SetResourceValue("com.example.target:integer/int1", Res_value::TYPE_INT_DEC, 1U)
|
||||
.Build();
|
||||
ASSERT_TRUE(overlay);
|
||||
TemporaryFile tf;
|
||||
std::ofstream out(tf.path);
|
||||
ASSERT_TRUE((*overlay).ToBinaryStream(out));
|
||||
out.close();
|
||||
|
||||
auto container = OverlayResourceContainer::FromPath(tf.path);
|
||||
ASSERT_TRUE(container) << container.GetErrorMessage();
|
||||
EXPECT_EQ(tf.path, (*container)->GetPath());
|
||||
|
||||
auto crc = (*container)->GetCrc();
|
||||
ASSERT_TRUE(crc) << crc.GetErrorMessage();
|
||||
EXPECT_NE(0U, *crc);
|
||||
|
||||
auto info = (*container)->FindOverlayInfo("SandTheme");
|
||||
ASSERT_TRUE(info) << info.GetErrorMessage();
|
||||
EXPECT_EQ("SandTheme", (*info).name);
|
||||
EXPECT_EQ("TestResources", (*info).target_name);
|
||||
|
||||
auto pairs = (*container)->GetOverlayData(*info);
|
||||
ASSERT_TRUE(pairs) << pairs.GetErrorMessage();
|
||||
EXPECT_EQ(1U, pairs->pairs.size());
|
||||
|
||||
auto& it = pairs->pairs[0];
|
||||
ASSERT_EQ("com.example.target:integer/int1", it.resource_name);
|
||||
auto entry = std::get_if<TargetValue>(&it.value);
|
||||
ASSERT_NE(nullptr, entry);
|
||||
EXPECT_EQ(1U, entry->data_value);
|
||||
EXPECT_EQ(Res_value::TYPE_INT_DEC, entry->data_type);
|
||||
}
|
||||
|
||||
} // namespace android::idmap2
|
||||
@@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <android-base/file.h>
|
||||
|
||||
#include <cstdio> // fclose
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
@@ -61,12 +63,12 @@ TEST(IdmapTests, TestCanonicalIdmapPathFor) {
|
||||
}
|
||||
|
||||
TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) {
|
||||
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
|
||||
std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
|
||||
std::istringstream stream(raw);
|
||||
std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
|
||||
ASSERT_THAT(header, NotNull());
|
||||
ASSERT_EQ(header->GetMagic(), 0x504d4449U);
|
||||
ASSERT_EQ(header->GetVersion(), 0x07U);
|
||||
ASSERT_EQ(header->GetVersion(), 0x08U);
|
||||
ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
|
||||
ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
|
||||
ASSERT_EQ(header->GetFulfilledPolicies(), 0x11);
|
||||
@@ -81,7 +83,7 @@ TEST(IdmapTests, IdmapFailParsingDifferentVersion) {
|
||||
std::stringstream stream;
|
||||
stream << android::kIdmapMagic;
|
||||
stream << 0xffffffffU;
|
||||
stream << std::string(kJunkSize, (char) 0xffU);
|
||||
stream << std::string(kJunkSize, (char)0xffU);
|
||||
ASSERT_FALSE(Idmap::FromBinaryStream(stream));
|
||||
}
|
||||
|
||||
@@ -90,14 +92,13 @@ TEST(IdmapTests, IdmapFailParsingDifferentMagic) {
|
||||
std::stringstream stream;
|
||||
stream << 0xffffffffU;
|
||||
stream << android::kIdmapCurrentVersion;
|
||||
stream << std::string(kJunkSize, (char) 0xffU);
|
||||
stream << std::string(kJunkSize, (char)0xffU);
|
||||
ASSERT_FALSE(Idmap::FromBinaryStream(stream));
|
||||
}
|
||||
|
||||
TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
|
||||
const size_t offset = kIdmapRawDataOffset;
|
||||
std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
|
||||
kIdmapRawDataLen - offset);
|
||||
std::string raw(reinterpret_cast<const char*>(kIdmapRawData + offset), kIdmapRawDataLen - offset);
|
||||
std::istringstream stream(raw);
|
||||
|
||||
std::unique_ptr<const IdmapData::Header> header = IdmapData::Header::FromBinaryStream(stream);
|
||||
@@ -108,8 +109,7 @@ TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
|
||||
|
||||
TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
|
||||
const size_t offset = kIdmapRawDataOffset;
|
||||
std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
|
||||
kIdmapRawDataLen - offset);
|
||||
std::string raw(reinterpret_cast<const char*>(kIdmapRawData + offset), kIdmapRawDataLen - offset);
|
||||
std::istringstream stream(raw);
|
||||
|
||||
std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream);
|
||||
@@ -134,7 +134,7 @@ TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
|
||||
}
|
||||
|
||||
TEST(IdmapTests, CreateIdmapFromBinaryStream) {
|
||||
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
|
||||
std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
|
||||
std::istringstream stream(raw);
|
||||
|
||||
auto result = Idmap::FromBinaryStream(stream);
|
||||
@@ -143,7 +143,7 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) {
|
||||
|
||||
ASSERT_THAT(idmap->GetHeader(), NotNull());
|
||||
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
|
||||
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x07U);
|
||||
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x08U);
|
||||
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
|
||||
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
|
||||
ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), kIdmapRawDataPolicies);
|
||||
@@ -177,7 +177,7 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) {
|
||||
}
|
||||
|
||||
TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) {
|
||||
std::string raw(reinterpret_cast<const char*>(idmap_raw_data),
|
||||
std::string raw(reinterpret_cast<const char*>(kIdmapRawData),
|
||||
10); // data too small
|
||||
std::istringstream stream(raw);
|
||||
|
||||
@@ -189,14 +189,14 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) {
|
||||
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
|
||||
std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
|
||||
|
||||
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
|
||||
ASSERT_THAT(target_apk, NotNull());
|
||||
auto target = TargetResourceContainer::FromPath(target_apk_path);
|
||||
ASSERT_TRUE(target);
|
||||
|
||||
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
|
||||
ASSERT_THAT(overlay_apk, NotNull());
|
||||
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
|
||||
ASSERT_TRUE(overlay);
|
||||
|
||||
auto idmap_result = Idmap::FromApkAssets(
|
||||
*target_apk, *overlay_apk, TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::PUBLIC,
|
||||
auto idmap_result = Idmap::FromContainers(
|
||||
**target, **overlay, TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ true);
|
||||
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
|
||||
auto& idmap = *idmap_result;
|
||||
@@ -204,7 +204,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) {
|
||||
|
||||
ASSERT_THAT(idmap->GetHeader(), NotNull());
|
||||
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
|
||||
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x07U);
|
||||
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x08U);
|
||||
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC);
|
||||
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC);
|
||||
ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC);
|
||||
@@ -218,15 +218,15 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssets) {
|
||||
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
|
||||
std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
|
||||
|
||||
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
|
||||
ASSERT_THAT(target_apk, NotNull());
|
||||
auto target = TargetResourceContainer::FromPath(target_apk_path);
|
||||
ASSERT_TRUE(target);
|
||||
|
||||
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
|
||||
ASSERT_THAT(overlay_apk, NotNull());
|
||||
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
|
||||
ASSERT_TRUE(overlay);
|
||||
|
||||
auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk,
|
||||
TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ true);
|
||||
auto idmap_result = Idmap::FromContainers(
|
||||
**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ true);
|
||||
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
|
||||
auto& idmap = *idmap_result;
|
||||
ASSERT_THAT(idmap, NotNull());
|
||||
@@ -255,25 +255,66 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssets) {
|
||||
ASSERT_OVERLAY_ENTRY(overlay_entries[3], R::overlay::string::str4, R::target::string::str4);
|
||||
}
|
||||
|
||||
TEST(IdmapTests, FabricatedOverlay) {
|
||||
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
|
||||
auto target = TargetResourceContainer::FromPath(target_apk_path);
|
||||
ASSERT_TRUE(target);
|
||||
|
||||
auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target")
|
||||
.SetOverlayable("TestResources")
|
||||
.SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U)
|
||||
.SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000)
|
||||
.Build();
|
||||
|
||||
ASSERT_TRUE(frro);
|
||||
TemporaryFile tf;
|
||||
std::ofstream out(tf.path);
|
||||
ASSERT_TRUE((*frro).ToBinaryStream(out));
|
||||
out.close();
|
||||
|
||||
auto overlay = OverlayResourceContainer::FromPath(tf.path);
|
||||
ASSERT_TRUE(overlay);
|
||||
|
||||
auto idmap_result = Idmap::FromContainers(**target, **overlay, "SandTheme", PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ true);
|
||||
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
|
||||
auto& idmap = *idmap_result;
|
||||
ASSERT_THAT(idmap, NotNull());
|
||||
|
||||
const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
|
||||
ASSERT_EQ(dataBlocks.size(), 1U);
|
||||
|
||||
const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
|
||||
ASSERT_THAT(data, NotNull());
|
||||
ASSERT_EQ(data->GetTargetEntries().size(), 0U);
|
||||
ASSERT_EQ(data->GetOverlayEntries().size(), 0U);
|
||||
|
||||
const auto& target_inline_entries = data->GetTargetInlineEntries();
|
||||
ASSERT_EQ(target_inline_entries.size(), 2U);
|
||||
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1,
|
||||
Res_value::TYPE_INT_DEC, 2U);
|
||||
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1,
|
||||
Res_value::TYPE_REFERENCE, 0x7f010000);
|
||||
}
|
||||
|
||||
TEST(IdmapTests, FailCreateIdmapInvalidName) {
|
||||
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
|
||||
std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
|
||||
|
||||
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
|
||||
ASSERT_THAT(target_apk, NotNull());
|
||||
auto target = TargetResourceContainer::FromPath(target_apk_path);
|
||||
ASSERT_TRUE(target);
|
||||
|
||||
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
|
||||
ASSERT_THAT(overlay_apk, NotNull());
|
||||
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
|
||||
ASSERT_TRUE(overlay);
|
||||
|
||||
{
|
||||
auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, "", PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ true);
|
||||
auto idmap_result = Idmap::FromContainers(**target, **overlay, "", PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ true);
|
||||
ASSERT_FALSE(idmap_result);
|
||||
}
|
||||
{
|
||||
auto idmap_result =
|
||||
Idmap::FromApkAssets(*target_apk, *overlay_apk, "unknown", PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ true);
|
||||
auto idmap_result = Idmap::FromContainers(**target, **overlay, "unknown", PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ true);
|
||||
ASSERT_FALSE(idmap_result);
|
||||
}
|
||||
}
|
||||
@@ -282,15 +323,15 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) {
|
||||
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
|
||||
std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-shared.apk";
|
||||
|
||||
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
|
||||
ASSERT_THAT(target_apk, NotNull());
|
||||
auto target = TargetResourceContainer::FromPath(target_apk_path);
|
||||
ASSERT_TRUE(target);
|
||||
|
||||
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
|
||||
ASSERT_THAT(overlay_apk, NotNull());
|
||||
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
|
||||
ASSERT_TRUE(overlay);
|
||||
|
||||
auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk,
|
||||
TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ true);
|
||||
auto idmap_result = Idmap::FromContainers(
|
||||
**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ true);
|
||||
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
|
||||
auto& idmap = *idmap_result;
|
||||
ASSERT_THAT(idmap, NotNull());
|
||||
@@ -328,30 +369,29 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) {
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<const IdmapData>> TestIdmapDataFromApkAssets(
|
||||
const std::string& local_target_apk_path, const std::string& local_overlay_apk_path,
|
||||
const std::string& local_target_path, const std::string& local_overlay_path,
|
||||
const std::string& overlay_name, const PolicyBitmask& fulfilled_policies,
|
||||
bool enforce_overlayable) {
|
||||
auto overlay_info =
|
||||
utils::ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path, overlay_name);
|
||||
const std::string target_path(GetTestDataPath() + local_target_path);
|
||||
auto target = TargetResourceContainer::FromPath(target_path);
|
||||
if (!target) {
|
||||
return Error(R"(Failed to load target "%s")", target_path.c_str());
|
||||
}
|
||||
|
||||
const std::string overlay_path(GetTestDataPath() + local_overlay_path);
|
||||
auto overlay = OverlayResourceContainer::FromPath(overlay_path);
|
||||
if (!overlay) {
|
||||
return Error(R"(Failed to load overlay "%s")", overlay_path.c_str());
|
||||
}
|
||||
|
||||
auto overlay_info = (*overlay)->FindOverlayInfo(overlay_name);
|
||||
if (!overlay_info) {
|
||||
return overlay_info.GetError();
|
||||
}
|
||||
|
||||
const std::string target_apk_path(GetTestDataPath() + local_target_apk_path);
|
||||
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
|
||||
if (!target_apk) {
|
||||
return Error(R"(Failed to load target apk "%s")", target_apk_path.data());
|
||||
}
|
||||
|
||||
const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path);
|
||||
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
|
||||
if (!overlay_apk) {
|
||||
return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data());
|
||||
return Error(R"(Failed to find overlay name "%s")", overlay_name.c_str());
|
||||
}
|
||||
|
||||
LogInfo log_info;
|
||||
auto mapping = ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, *overlay_info,
|
||||
fulfilled_policies, enforce_overlayable, log_info);
|
||||
auto mapping = ResourceMapping::FromContainers(**target, **overlay, *overlay_info,
|
||||
fulfilled_policies, enforce_overlayable, log_info);
|
||||
if (!mapping) {
|
||||
return mapping.GetError();
|
||||
}
|
||||
@@ -360,11 +400,9 @@ Result<std::unique_ptr<const IdmapData>> TestIdmapDataFromApkAssets(
|
||||
}
|
||||
|
||||
TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) {
|
||||
auto idmap_data =
|
||||
TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", "DifferentPackages",
|
||||
|
||||
PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ false);
|
||||
auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk",
|
||||
"DifferentPackages", PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ false);
|
||||
|
||||
ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
|
||||
auto& data = *idmap_data;
|
||||
@@ -417,7 +455,7 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
|
||||
const uint32_t target_crc = kIdmapRawDataTargetCrc;
|
||||
const uint32_t overlay_crc = kIdmapRawOverlayCrc;
|
||||
|
||||
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
|
||||
std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
|
||||
std::istringstream raw_stream(raw);
|
||||
|
||||
auto result = Idmap::FromBinaryStream(raw_stream);
|
||||
@@ -468,8 +506,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
|
||||
ASSERT_THAT(bad_target_crc_header, NotNull());
|
||||
ASSERT_NE(header->GetTargetCrc(), bad_target_crc_header->GetTargetCrc());
|
||||
ASSERT_FALSE(bad_target_crc_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
|
||||
target_crc, overlay_crc, policies,
|
||||
/* enforce_overlayable */ true));
|
||||
target_crc, overlay_crc, policies,
|
||||
/* enforce_overlayable */ true));
|
||||
|
||||
// overlay crc: bytes (0xc, 0xf)
|
||||
std::string bad_overlay_crc_string(stream.str());
|
||||
@@ -483,8 +521,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
|
||||
ASSERT_THAT(bad_overlay_crc_header, NotNull());
|
||||
ASSERT_NE(header->GetOverlayCrc(), bad_overlay_crc_header->GetOverlayCrc());
|
||||
ASSERT_FALSE(bad_overlay_crc_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
|
||||
target_crc, overlay_crc, policies,
|
||||
/* enforce_overlayable */ true));
|
||||
target_crc, overlay_crc, policies,
|
||||
/* enforce_overlayable */ true));
|
||||
|
||||
// fulfilled policy: bytes (0x10, 0x13)
|
||||
std::string bad_policy_string(stream.str());
|
||||
@@ -522,8 +560,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
|
||||
ASSERT_THAT(bad_target_path_header, NotNull());
|
||||
ASSERT_NE(header->GetTargetPath(), bad_target_path_header->GetTargetPath());
|
||||
ASSERT_FALSE(bad_target_path_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
|
||||
target_crc, overlay_crc, policies,
|
||||
/* enforce_overlayable */ true));
|
||||
target_crc, overlay_crc, policies,
|
||||
/* enforce_overlayable */ true));
|
||||
|
||||
// overlay path: bytes (0x2c, 0x37)
|
||||
std::string bad_overlay_path_string(stream.str());
|
||||
@@ -576,7 +614,7 @@ class TestVisitor : public Visitor {
|
||||
};
|
||||
|
||||
TEST(IdmapTests, TestVisitor) {
|
||||
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
|
||||
std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
|
||||
std::istringstream stream(raw);
|
||||
|
||||
const auto idmap = Idmap::FromBinaryStream(stream);
|
||||
|
||||
@@ -27,35 +27,31 @@
|
||||
#include "idmap2/Idmap.h"
|
||||
#include "idmap2/PrettyPrintVisitor.h"
|
||||
|
||||
using android::ApkAssets;
|
||||
using android::base::StringPrintf;
|
||||
using ::testing::NotNull;
|
||||
|
||||
using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
|
||||
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
|
||||
const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
|
||||
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
|
||||
ASSERT_THAT(target_apk, NotNull());
|
||||
auto target = TargetResourceContainer::FromPath(target_apk_path);
|
||||
ASSERT_TRUE(target);
|
||||
|
||||
const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
|
||||
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
|
||||
ASSERT_THAT(overlay_apk, NotNull());
|
||||
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
|
||||
ASSERT_TRUE(overlay);
|
||||
|
||||
const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk,
|
||||
TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ true);
|
||||
const auto idmap = Idmap::FromContainers(**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT,
|
||||
PolicyFlags::PUBLIC, /* enforce_overlayable */ true);
|
||||
ASSERT_TRUE(idmap);
|
||||
|
||||
std::stringstream stream;
|
||||
PrettyPrintVisitor visitor(stream);
|
||||
(*idmap)->accept(&visitor);
|
||||
|
||||
ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
|
||||
ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
|
||||
ASSERT_NE(stream.str().find("target path : "), std::string::npos);
|
||||
ASSERT_NE(stream.str().find("overlay path : "), std::string::npos);
|
||||
ASSERT_NE(stream.str().find(StringPrintf("0x%08x -> 0x%08x (integer/int1 -> integer/int1)\n",
|
||||
R::target::integer::int1, R::overlay::integer::int1)),
|
||||
std::string::npos);
|
||||
@@ -64,7 +60,7 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
|
||||
TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) {
|
||||
fclose(stderr); // silence expected warnings from libandroidfw
|
||||
|
||||
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
|
||||
std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
|
||||
std::istringstream raw_stream(raw);
|
||||
|
||||
const auto idmap = Idmap::FromBinaryStream(raw_stream);
|
||||
@@ -74,8 +70,8 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) {
|
||||
PrettyPrintVisitor visitor(stream);
|
||||
(*idmap)->accept(&visitor);
|
||||
|
||||
ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
|
||||
ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
|
||||
ASSERT_NE(stream.str().find("target path : "), std::string::npos);
|
||||
ASSERT_NE(stream.str().find("overlay path : "), std::string::npos);
|
||||
ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000 (\?\?\? -> \?\?\?)\n"), std::string::npos);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,17 +30,16 @@
|
||||
#include "idmap2/RawPrintVisitor.h"
|
||||
|
||||
using android::base::StringPrintf;
|
||||
using ::testing::NotNull;
|
||||
|
||||
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
#define ASSERT_CONTAINS_REGEX(pattern, str) \
|
||||
do { \
|
||||
ASSERT_TRUE(std::regex_search(str, std::regex(pattern))) \
|
||||
<< "pattern '" << pattern << "' not found in\n--------\n" \
|
||||
<< str << "--------"; \
|
||||
#define ASSERT_CONTAINS_REGEX(pattern, str) \
|
||||
do { \
|
||||
ASSERT_TRUE(std::regex_search(str, std::regex(pattern))) \
|
||||
<< "pattern '" << (pattern) << "' not found in\n--------\n" \
|
||||
<< (str) << "--------"; \
|
||||
} while (0)
|
||||
|
||||
#define ADDRESS "[0-9a-f]{8}: "
|
||||
@@ -49,16 +48,15 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
|
||||
fclose(stderr); // silence expected warnings
|
||||
|
||||
const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
|
||||
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
|
||||
ASSERT_THAT(target_apk, NotNull());
|
||||
auto target = TargetResourceContainer::FromPath(target_apk_path);
|
||||
ASSERT_TRUE(target);
|
||||
|
||||
const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
|
||||
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
|
||||
ASSERT_THAT(overlay_apk, NotNull());
|
||||
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
|
||||
ASSERT_TRUE(overlay);
|
||||
|
||||
const auto idmap =
|
||||
Idmap::FromApkAssets(*target_apk, *overlay_apk, TestConstants::OVERLAY_NAME_DEFAULT,
|
||||
PolicyFlags::PUBLIC, /* enforce_overlayable */ true);
|
||||
const auto idmap = Idmap::FromContainers(**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT,
|
||||
PolicyFlags::PUBLIC, /* enforce_overlayable */ true);
|
||||
ASSERT_TRUE(idmap);
|
||||
|
||||
std::stringstream stream;
|
||||
@@ -66,7 +64,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
|
||||
(*idmap)->accept(&visitor);
|
||||
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "00000007 version\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "00000008 version\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(
|
||||
StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING),
|
||||
stream.str());
|
||||
@@ -75,8 +73,6 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
|
||||
stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 fulfilled policies: public\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 enforce overlayable\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "00000000 target inline entry count", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count", stream.str());
|
||||
@@ -104,7 +100,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
|
||||
TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
|
||||
fclose(stderr); // silence expected warnings from libandroidfw
|
||||
|
||||
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
|
||||
std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
|
||||
std::istringstream raw_stream(raw);
|
||||
|
||||
const auto idmap = Idmap::FromBinaryStream(raw_stream);
|
||||
@@ -115,7 +111,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
|
||||
(*idmap)->accept(&visitor);
|
||||
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "00000007 version\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "00000008 version\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "00001234 target crc\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "00005678 overlay crc\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "00000011 fulfilled policies: public|signature\n", stream.str());
|
||||
@@ -126,8 +122,6 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "........ overlay path: overlayX.apk\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "0000000b overlay name size\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "........ overlay name: OverlayName\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "00000003 target entry count\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 target inline entry count\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "00000003 overlay entry count\n", stream.str());
|
||||
@@ -140,7 +134,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "7f030002 target id\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 string pool size\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX("000000a8: ........ string pool\n", stream.str());
|
||||
ASSERT_CONTAINS_REGEX("000000a4: ........ string pool\n", stream.str());
|
||||
}
|
||||
|
||||
} // namespace android::idmap2
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <androidfw/ResourceTypes.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstdio> // fclose
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
@@ -22,14 +26,10 @@
|
||||
#include "R.h"
|
||||
#include "TestConstants.h"
|
||||
#include "TestHelpers.h"
|
||||
#include "androidfw/ResourceTypes.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "idmap2/LogInfo.h"
|
||||
#include "idmap2/ResourceMapping.h"
|
||||
|
||||
using android::Res_value;
|
||||
using android::idmap2::utils::ExtractOverlayManifestInfo;
|
||||
|
||||
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
|
||||
|
||||
@@ -41,32 +41,36 @@ namespace android::idmap2 {
|
||||
ASSERT_TRUE(result) << result.GetErrorMessage(); \
|
||||
} while (0)
|
||||
|
||||
Result<ResourceMapping> TestGetResourceMapping(const std::string& local_target_apk_path,
|
||||
const std::string& local_overlay_apk_path,
|
||||
Result<ResourceMapping> TestGetResourceMapping(const std::string& local_target_path,
|
||||
const std::string& local_overlay_path,
|
||||
const std::string& overlay_name,
|
||||
const PolicyBitmask& fulfilled_policies,
|
||||
bool enforce_overlayable) {
|
||||
auto overlay_info =
|
||||
ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path, overlay_name);
|
||||
const std::string target_path = (local_target_path[0] == '/')
|
||||
? local_target_path
|
||||
: (GetTestDataPath() + "/" + local_target_path);
|
||||
auto target = TargetResourceContainer::FromPath(target_path);
|
||||
if (!target) {
|
||||
return Error(target.GetError(), R"(Failed to load target "%s")", target_path.c_str());
|
||||
}
|
||||
|
||||
const std::string overlay_path = (local_overlay_path[0] == '/')
|
||||
? local_overlay_path
|
||||
: (GetTestDataPath() + "/" + local_overlay_path);
|
||||
auto overlay = OverlayResourceContainer::FromPath(overlay_path);
|
||||
if (!overlay) {
|
||||
return Error(overlay.GetError(), R"(Failed to load overlay "%s")", overlay_path.c_str());
|
||||
}
|
||||
|
||||
auto overlay_info = (*overlay)->FindOverlayInfo(overlay_name);
|
||||
if (!overlay_info) {
|
||||
return overlay_info.GetError();
|
||||
}
|
||||
|
||||
const std::string target_apk_path(GetTestDataPath() + local_target_apk_path);
|
||||
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
|
||||
if (!target_apk) {
|
||||
return Error(R"(Failed to load target apk "%s")", target_apk_path.data());
|
||||
}
|
||||
|
||||
const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path);
|
||||
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
|
||||
if (!overlay_apk) {
|
||||
return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data());
|
||||
return Error(overlay_info.GetError(), R"(Failed to find overlay name "%s")",
|
||||
overlay_name.c_str());
|
||||
}
|
||||
|
||||
LogInfo log_info;
|
||||
return ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, *overlay_info,
|
||||
fulfilled_policies, enforce_overlayable, log_info);
|
||||
return ResourceMapping::FromContainers(**target, **overlay, *overlay_info, fulfilled_policies,
|
||||
enforce_overlayable, log_info);
|
||||
}
|
||||
|
||||
Result<Unit> MappingExists(const ResourceMapping& mapping, ResourceId target_resource,
|
||||
@@ -128,7 +132,7 @@ Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& tar
|
||||
}
|
||||
|
||||
TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) {
|
||||
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-legacy.apk", "",
|
||||
auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay-legacy.apk", "",
|
||||
PolicyFlags::PUBLIC, /* enforce_overlayable */ false);
|
||||
|
||||
ASSERT_TRUE(resources) << resources.GetErrorMessage();
|
||||
@@ -145,7 +149,7 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) {
|
||||
}
|
||||
|
||||
TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
|
||||
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", "SwapNames",
|
||||
auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", "SwapNames",
|
||||
PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ false);
|
||||
|
||||
@@ -161,7 +165,7 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
|
||||
}
|
||||
|
||||
TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
|
||||
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk",
|
||||
auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk",
|
||||
"DifferentPackages", PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ false);
|
||||
|
||||
@@ -176,7 +180,7 @@ TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
|
||||
}
|
||||
|
||||
TEST(ResourceMappingTests, InlineResources) {
|
||||
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", "Inline",
|
||||
auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", "Inline",
|
||||
PolicyFlags::PUBLIC, /* enforce_overlayable */ false);
|
||||
|
||||
constexpr size_t overlay_string_pool_size = 10U;
|
||||
@@ -189,8 +193,32 @@ TEST(ResourceMappingTests, InlineResources) {
|
||||
ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U));
|
||||
}
|
||||
|
||||
TEST(ResourceMappingTests, FabricatedOverlay) {
|
||||
auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target")
|
||||
.SetOverlayable("TestResources")
|
||||
.SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U)
|
||||
.SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000)
|
||||
.Build();
|
||||
|
||||
ASSERT_TRUE(frro);
|
||||
TemporaryFile tf;
|
||||
std::ofstream out(tf.path);
|
||||
ASSERT_TRUE((*frro).ToBinaryStream(out));
|
||||
out.close();
|
||||
|
||||
auto resources = TestGetResourceMapping("target/target.apk", tf.path, "SandTheme",
|
||||
PolicyFlags::PUBLIC, /* enforce_overlayable */ false);
|
||||
|
||||
ASSERT_TRUE(resources) << resources.GetErrorMessage();
|
||||
auto& res = *resources;
|
||||
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
|
||||
ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U);
|
||||
ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, 0x7f010000));
|
||||
ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 2U));
|
||||
}
|
||||
|
||||
TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
|
||||
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk",
|
||||
auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk",
|
||||
TestConstants::OVERLAY_NAME_ALL_POLICIES,
|
||||
PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ true);
|
||||
@@ -209,7 +237,7 @@ TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
|
||||
// Resources that are not declared as overlayable and resources that a protected by policies the
|
||||
// overlay does not fulfill must not map to overlay resources.
|
||||
TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
|
||||
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk",
|
||||
auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk",
|
||||
TestConstants::OVERLAY_NAME_ALL_POLICIES,
|
||||
PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ true);
|
||||
@@ -229,7 +257,7 @@ TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
|
||||
// overlay does not fulfilled can map to overlay resources when overlayable enforcement is turned
|
||||
// off.
|
||||
TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
|
||||
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk",
|
||||
auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk",
|
||||
TestConstants::OVERLAY_NAME_ALL_POLICIES,
|
||||
PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ false);
|
||||
@@ -264,7 +292,7 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnore
|
||||
// Overlays that do not target an <overlayable> tag can overlay any resource if overlayable
|
||||
// enforcement is disabled.
|
||||
TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTargetName) {
|
||||
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-legacy.apk", "",
|
||||
auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay-legacy.apk", "",
|
||||
PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ false);
|
||||
|
||||
@@ -284,10 +312,9 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTarget
|
||||
// Overlays that are neither pre-installed nor signed with the same signature as the target cannot
|
||||
// overlay packages that have not defined overlayable resources.
|
||||
TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
|
||||
auto resources =
|
||||
TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay.apk",
|
||||
"NoTargetName", PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ true);
|
||||
auto resources = TestGetResourceMapping("target/target-no-overlayable.apk", "overlay/overlay.apk",
|
||||
"NoTargetName", PolicyFlags::PUBLIC,
|
||||
/* enforce_overlayable */ true);
|
||||
|
||||
ASSERT_TRUE(resources) << resources.GetErrorMessage();
|
||||
ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
|
||||
@@ -297,9 +324,9 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
|
||||
// signed with the same signature as the reference package can overlay packages that have not
|
||||
// defined overlayable resources.
|
||||
TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
|
||||
auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void {
|
||||
auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) {
|
||||
auto resources =
|
||||
TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay.apk",
|
||||
TestGetResourceMapping("target/target-no-overlayable.apk", "overlay/overlay.apk",
|
||||
TestConstants::OVERLAY_NAME_ALL_POLICIES, fulfilled_policies,
|
||||
/* enforce_overlayable */ true);
|
||||
|
||||
|
||||
@@ -17,10 +17,12 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "R.h"
|
||||
#include "TestHelpers.h"
|
||||
#include "androidfw/ApkAssets.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "idmap2/ResourceContainer.h"
|
||||
#include "idmap2/ResourceUtils.h"
|
||||
#include "idmap2/Result.h"
|
||||
|
||||
@@ -49,8 +51,8 @@ class ResourceUtilsTests : public Idmap2Tests {
|
||||
};
|
||||
|
||||
TEST_F(ResourceUtilsTests, ResToTypeEntryName) {
|
||||
Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000U);
|
||||
ASSERT_TRUE(name);
|
||||
Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), R::target::integer::int1);
|
||||
ASSERT_TRUE(name) << name.GetErrorMessage();
|
||||
ASSERT_EQ(*name, "integer/int1");
|
||||
}
|
||||
|
||||
@@ -60,25 +62,34 @@ TEST_F(ResourceUtilsTests, ResToTypeEntryNameNoSuchResourceId) {
|
||||
}
|
||||
|
||||
TEST_F(ResourceUtilsTests, InvalidValidOverlayNameInvalidAttributes) {
|
||||
auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk",
|
||||
"InvalidName");
|
||||
auto overlay =
|
||||
OverlayResourceContainer::FromPath(GetTestDataPath() + "/overlay/overlay-invalid.apk");
|
||||
ASSERT_TRUE(overlay);
|
||||
|
||||
auto info = (*overlay)->FindOverlayInfo("InvalidName");
|
||||
ASSERT_FALSE(info);
|
||||
}
|
||||
|
||||
TEST_F(ResourceUtilsTests, ValidOverlayNameInvalidAttributes) {
|
||||
auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk",
|
||||
"ValidName");
|
||||
auto overlay =
|
||||
OverlayResourceContainer::FromPath(GetTestDataPath() + "/overlay/overlay-invalid.apk");
|
||||
ASSERT_TRUE(overlay);
|
||||
|
||||
auto info = (*overlay)->FindOverlayInfo("ValidName");
|
||||
ASSERT_FALSE(info);
|
||||
}
|
||||
|
||||
TEST_F(ResourceUtilsTests, ValidOverlayNameAndTargetPackageInvalidAttributes) {
|
||||
auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk",
|
||||
"ValidNameAndTargetPackage");
|
||||
auto overlay =
|
||||
OverlayResourceContainer::FromPath(GetTestDataPath() + "/overlay/overlay-invalid.apk");
|
||||
ASSERT_TRUE(overlay);
|
||||
|
||||
auto info = (*overlay)->FindOverlayInfo("ValidNameAndTargetPackage");
|
||||
ASSERT_TRUE(info);
|
||||
ASSERT_EQ("ValidNameAndTargetPackage", info->name);
|
||||
ASSERT_EQ("Valid", info->target_package);
|
||||
ASSERT_EQ("", info->target_name); // Attribute resource id could not be found
|
||||
ASSERT_EQ(0, info->resource_mapping); // Attribute resource id could not be found
|
||||
ASSERT_EQ("", info->target_name); // Attribute resource id could not be found
|
||||
ASSERT_EQ(0, info->resource_mapping); // Attribute resource id could not be found
|
||||
}
|
||||
|
||||
}// namespace android::idmap2
|
||||
} // namespace android::idmap2
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
const unsigned char idmap_raw_data[] = {
|
||||
const unsigned char kIdmapRawData[] = {
|
||||
// IDMAP HEADER
|
||||
// 0x0: magic
|
||||
0x49, 0x44, 0x4d, 0x50,
|
||||
|
||||
// 0x4: version
|
||||
0x07, 0x00, 0x00, 0x00,
|
||||
0x08, 0x00, 0x00, 0x00,
|
||||
|
||||
// 0x8: target crc
|
||||
0x34, 0x12, 0x00, 0x00,
|
||||
@@ -70,81 +70,72 @@ const unsigned char idmap_raw_data[] = {
|
||||
0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00,
|
||||
|
||||
// DATA HEADER
|
||||
// 0x54: target_package_id
|
||||
0x7f,
|
||||
|
||||
// 0x55: overlay_package_id
|
||||
0x7f,
|
||||
|
||||
// 0x56: padding
|
||||
0x00, 0x00,
|
||||
|
||||
// 0x58: target_entry_count
|
||||
// 0x54: target_entry_count
|
||||
0x03, 0x00, 0x00, 0x00,
|
||||
|
||||
// 0x5c: target_inline_entry_count
|
||||
// 0x58: target_inline_entry_count
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
|
||||
// 0x60: overlay_entry_count
|
||||
// 0x5c: overlay_entry_count
|
||||
0x03, 0x00, 0x00, 0x00,
|
||||
|
||||
// 0x64: string_pool_offset
|
||||
// 0x60: string_pool_offset
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
// TARGET ENTRIES
|
||||
// 0x68: target id (0x7f020000)
|
||||
// 0x64: target id (0x7f020000)
|
||||
0x00, 0x00, 0x02, 0x7f,
|
||||
|
||||
// 0x6c: overlay_id (0x7f020000)
|
||||
// 0x68: overlay_id (0x7f020000)
|
||||
0x00, 0x00, 0x02, 0x7f,
|
||||
|
||||
// 0x70: target id (0x7f030000)
|
||||
// 0x6c: target id (0x7f030000)
|
||||
0x00, 0x00, 0x03, 0x7f,
|
||||
|
||||
// 0x74: overlay_id (0x7f030000)
|
||||
// 0x70: overlay_id (0x7f030000)
|
||||
0x00, 0x00, 0x03, 0x7f,
|
||||
|
||||
// 0x78: target id (0x7f030002)
|
||||
// 0x74: target id (0x7f030002)
|
||||
0x02, 0x00, 0x03, 0x7f,
|
||||
|
||||
// 0x7c: overlay_id (0x7f030001)
|
||||
// 0x78: overlay_id (0x7f030001)
|
||||
0x01, 0x00, 0x03, 0x7f,
|
||||
|
||||
// INLINE TARGET ENTRIES
|
||||
|
||||
// 0x80: target_id
|
||||
// 0x7c: target_id
|
||||
0x00, 0x00, 0x04, 0x7f,
|
||||
|
||||
// 0x84: Res_value::size (value ignored by idmap)
|
||||
// 0x80: Res_value::size (value ignored by idmap)
|
||||
0x08, 0x00,
|
||||
|
||||
// 0x87: Res_value::res0 (value ignored by idmap)
|
||||
// 0x82: Res_value::res0 (value ignored by idmap)
|
||||
0x00,
|
||||
|
||||
// 0x88: Res_value::dataType (TYPE_INT_HEX)
|
||||
// 0x83: Res_value::dataType (TYPE_INT_HEX)
|
||||
0x11,
|
||||
|
||||
// 0x8c: Res_value::data
|
||||
// 0x84: Res_value::data
|
||||
0x78, 0x56, 0x34, 0x12,
|
||||
|
||||
// OVERLAY ENTRIES
|
||||
// 0x90: 0x7f020000 -> 0x7f020000
|
||||
// 0x88: 0x7f020000 -> 0x7f020000
|
||||
0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
|
||||
|
||||
// 0x98: 0x7f030000 -> 0x7f030000
|
||||
// 0x90: 0x7f030000 -> 0x7f030000
|
||||
0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
|
||||
|
||||
// 0xa0: 0x7f030001 -> 0x7f030002
|
||||
// 0x98: 0x7f030001 -> 0x7f030002
|
||||
0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f,
|
||||
|
||||
// 0xa4: string pool
|
||||
// 0xa0: string pool
|
||||
// string length,
|
||||
0x04, 0x00, 0x00, 0x00,
|
||||
|
||||
// 0xa8 string contents "test"
|
||||
// 0xa4 string contents "test"
|
||||
0x74, 0x65, 0x73, 0x74};
|
||||
|
||||
const unsigned int kIdmapRawDataLen = 0xac;
|
||||
const unsigned int kIdmapRawDataLen = 0xa8;
|
||||
const unsigned int kIdmapRawDataOffset = 0x54;
|
||||
const unsigned int kIdmapRawDataTargetCrc = 0x1234;
|
||||
const unsigned int kIdmapRawOverlayCrc = 0x5678;
|
||||
|
||||
@@ -19,25 +19,25 @@
|
||||
#include <string>
|
||||
|
||||
#include "TestHelpers.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "androidfw/AssetsProvider.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "idmap2/XmlParser.h"
|
||||
#include "idmap2/ZipFile.h"
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
Result<std::unique_ptr<const XmlParser>> CreateTestParser(const std::string& test_file) {
|
||||
auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
|
||||
Result<XmlParser> CreateTestParser(const std::string& test_file) {
|
||||
auto zip = ZipAssetsProvider::Create(GetTestDataPath() + "/target/target.apk");
|
||||
if (zip == nullptr) {
|
||||
return Error("Failed to open zip file");
|
||||
}
|
||||
|
||||
auto data = zip->Uncompress(test_file);
|
||||
auto data = zip->Open(test_file);
|
||||
if (data == nullptr) {
|
||||
return Error("Failed to open xml file");
|
||||
}
|
||||
|
||||
return XmlParser::Create(data->buf, data->size, /* copy_data */ true);
|
||||
return XmlParser::Create(data->getBuffer(true /* aligned*/), data->getLength(),
|
||||
/* copy_data */ true);
|
||||
}
|
||||
|
||||
TEST(XmlParserTests, Create) {
|
||||
@@ -54,7 +54,7 @@ TEST(XmlParserTests, NextChild) {
|
||||
auto xml = CreateTestParser("res/xml/test.xml");
|
||||
ASSERT_TRUE(xml) << xml.GetErrorMessage();
|
||||
|
||||
auto root_iter = (*xml)->tree_iterator();
|
||||
auto root_iter = xml->tree_iterator();
|
||||
ASSERT_EQ(root_iter->event(), XmlParser::Event::START_TAG);
|
||||
ASSERT_EQ(root_iter->name(), "a");
|
||||
|
||||
@@ -85,7 +85,7 @@ TEST(XmlParserTests, AttributeValues) {
|
||||
ASSERT_TRUE(xml) << xml.GetErrorMessage();
|
||||
|
||||
// Start at the <a> tag.
|
||||
auto root_iter = (*xml)->tree_iterator();
|
||||
auto root_iter = xml->tree_iterator();
|
||||
|
||||
// Start at the <b> tag.
|
||||
auto a_iter = root_iter.begin();
|
||||
@@ -111,8 +111,8 @@ TEST(XmlParserTests, IteratorEquality) {
|
||||
ASSERT_TRUE(xml) << xml.GetErrorMessage();
|
||||
|
||||
// Start at the <a> tag.
|
||||
auto root_iter_1 = (*xml)->tree_iterator();
|
||||
auto root_iter_2 = (*xml)->tree_iterator();
|
||||
auto root_iter_1 = xml->tree_iterator();
|
||||
auto root_iter_2 = xml->tree_iterator();
|
||||
ASSERT_EQ(root_iter_1, root_iter_2);
|
||||
ASSERT_EQ(*root_iter_1, *root_iter_2);
|
||||
|
||||
@@ -146,7 +146,7 @@ TEST(XmlParserTests, Backtracking) {
|
||||
ASSERT_TRUE(xml) << xml.GetErrorMessage();
|
||||
|
||||
// Start at the <a> tag.
|
||||
auto root_iter_1 = (*xml)->tree_iterator();
|
||||
auto root_iter_1 = xml->tree_iterator();
|
||||
|
||||
// Start at the <b> tag.
|
||||
auto a_iter_1 = root_iter_1.begin();
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <cstdio> // fclose
|
||||
#include <string>
|
||||
|
||||
#include "TestHelpers.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "idmap2/Result.h"
|
||||
#include "idmap2/ZipFile.h"
|
||||
|
||||
using ::testing::IsNull;
|
||||
using ::testing::NotNull;
|
||||
|
||||
namespace android::idmap2 {
|
||||
|
||||
TEST(ZipFileTests, BasicOpen) {
|
||||
auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
|
||||
ASSERT_THAT(zip, NotNull());
|
||||
|
||||
fclose(stderr); // silence expected warnings from libziparchive
|
||||
auto fail = ZipFile::Open(GetTestDataPath() + "/does-not-exist");
|
||||
ASSERT_THAT(fail, IsNull());
|
||||
}
|
||||
|
||||
TEST(ZipFileTests, Crc) {
|
||||
auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
|
||||
ASSERT_THAT(zip, NotNull());
|
||||
|
||||
Result<uint32_t> crc = zip->Crc("AndroidManifest.xml");
|
||||
ASSERT_TRUE(crc);
|
||||
ASSERT_EQ(*crc, 0x762f3d24);
|
||||
|
||||
Result<uint32_t> crc2 = zip->Crc("does-not-exist");
|
||||
ASSERT_FALSE(crc2);
|
||||
}
|
||||
|
||||
TEST(ZipFileTests, Uncompress) {
|
||||
auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
|
||||
ASSERT_THAT(zip, NotNull());
|
||||
|
||||
auto data = zip->Uncompress("assets/lorem-ipsum.txt");
|
||||
ASSERT_THAT(data, NotNull());
|
||||
const std::string lorem_ipsum("Lorem ipsum dolor sit amet.\n");
|
||||
ASSERT_THAT(data->size, lorem_ipsum.size());
|
||||
ASSERT_THAT(std::string(reinterpret_cast<const char*>(data->buf), data->size), lorem_ipsum);
|
||||
|
||||
auto fail = zip->Uncompress("does-not-exist");
|
||||
ASSERT_THAT(fail, IsNull());
|
||||
}
|
||||
|
||||
} // namespace android::idmap2
|
||||
65
core/java/android/content/om/CriticalOverlayInfo.java
Normal file
65
core/java/android/content/om/CriticalOverlayInfo.java
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* 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 android.content.om;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SystemApi;
|
||||
|
||||
/**
|
||||
* A subset of {@link OverlayInfo} fields that when changed cause the overlay's settings to be
|
||||
* completely reinitialized.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public interface CriticalOverlayInfo {
|
||||
|
||||
/**
|
||||
* @return the package name of the overlay.
|
||||
*/
|
||||
@NonNull
|
||||
String getPackageName();
|
||||
|
||||
/**
|
||||
* @return the unique name of the overlay within its containing package.
|
||||
*/
|
||||
@Nullable
|
||||
String getOverlayName();
|
||||
|
||||
/**
|
||||
* @return the target package name of the overlay.
|
||||
*/
|
||||
@NonNull
|
||||
String getTargetPackageName();
|
||||
|
||||
/**
|
||||
* @return the name of the target overlayable declaration.
|
||||
*/
|
||||
@Nullable
|
||||
String getTargetOverlayableName();
|
||||
|
||||
/**
|
||||
* @return an identifier representing the current overlay.
|
||||
*/
|
||||
@NonNull
|
||||
OverlayIdentifier getOverlayIdentifier();
|
||||
|
||||
/**
|
||||
* Returns whether or not the overlay is a {@link FabricatedOverlay}.
|
||||
*/
|
||||
boolean isFabricated();
|
||||
}
|
||||
126
core/java/android/content/om/FabricatedOverlay.java
Normal file
126
core/java/android/content/om/FabricatedOverlay.java
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* 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 android.content.om;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.FabricatedOverlayInternal;
|
||||
import android.os.FabricatedOverlayInternalEntry;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Fabricated Runtime Resource Overlays (FRROs) are overlays generated ar runtime.
|
||||
*
|
||||
* Fabricated overlays are enabled, disabled, and reordered just like normal overlays. The
|
||||
* overlayable policies a fabricated overlay fulfills are the same policies the creator of the
|
||||
* overlay fulfill. For example, a fabricated overlay created by a platform signed package on the
|
||||
* system partition would fulfil the {@code system} and {@code signature} policies.
|
||||
*
|
||||
* The owner of a fabricated overlay is the UID that created it. Overlays commit to the overlay
|
||||
* manager persist across reboots. When the UID is uninstalled, its fabricated overlays are wiped.
|
||||
*
|
||||
* Processes with {@link Android.Manifest.permission.CHANGE_OVERLAY_PACKAGES} can manage normal
|
||||
* overlays and fabricated overlays.
|
||||
* @hide
|
||||
*/
|
||||
public class FabricatedOverlay {
|
||||
|
||||
/** Retrieves the identifier for this fabricated overlay. */
|
||||
public OverlayIdentifier getIdentifier() {
|
||||
return new OverlayIdentifier(
|
||||
mOverlay.packageName, TextUtils.nullIfEmpty(mOverlay.overlayName));
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final String mOwningPackage;
|
||||
private final String mName;
|
||||
private final String mTargetPackage;
|
||||
private String mTargetOverlayable = "";
|
||||
private final ArrayList<FabricatedOverlayInternalEntry> mEntries = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Constructs a build for a fabricated overlay.
|
||||
*
|
||||
* @param owningPackage the name of the package that owns the fabricated overlay (must
|
||||
* be a package name of this UID).
|
||||
* @param name a name used to uniquely identify the fabricated overlay owned by
|
||||
* {@param owningPackageName}
|
||||
* @param targetPackage the name of the package to overlay
|
||||
*/
|
||||
public Builder(@NonNull String owningPackage, @NonNull String name,
|
||||
@NonNull String targetPackage) {
|
||||
Preconditions.checkStringNotEmpty(owningPackage,
|
||||
"'owningPackage' must not be empty nor null");
|
||||
Preconditions.checkStringNotEmpty(name,
|
||||
"'name'' must not be empty nor null");
|
||||
Preconditions.checkStringNotEmpty(targetPackage,
|
||||
"'targetPackage' must not be empty nor null");
|
||||
|
||||
mOwningPackage = owningPackage;
|
||||
mName = name;
|
||||
mTargetPackage = targetPackage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the overlayable resources to overlay (can be null).
|
||||
*/
|
||||
public Builder setTargetOverlayable(@Nullable String targetOverlayable) {
|
||||
mTargetOverlayable = TextUtils.emptyIfNull(targetOverlayable);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of
|
||||
*
|
||||
* @param resourceName name of the target resource to overlay (in the form
|
||||
* [package]:type/entry)
|
||||
* @param dataType the data type of the new value
|
||||
* @param value the unsigned 32 bit integer representing the new value
|
||||
*
|
||||
* @see android.util.TypedValue#type
|
||||
*/
|
||||
public Builder setResourceValue(@NonNull String resourceName, int dataType, int value) {
|
||||
final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
|
||||
entry.resourceName = resourceName;
|
||||
entry.dataType = dataType;
|
||||
entry.data = value;
|
||||
mEntries.add(entry);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Builds an immutable fabricated overlay. */
|
||||
public FabricatedOverlay build() {
|
||||
final FabricatedOverlayInternal overlay = new FabricatedOverlayInternal();
|
||||
overlay.packageName = mOwningPackage;
|
||||
overlay.overlayName = mName;
|
||||
overlay.targetPackageName = mTargetPackage;
|
||||
overlay.targetOverlayable = mTargetOverlayable;
|
||||
overlay.entries = new ArrayList<>();
|
||||
overlay.entries.addAll(mEntries);
|
||||
return new FabricatedOverlay(overlay);
|
||||
}
|
||||
}
|
||||
|
||||
final FabricatedOverlayInternal mOverlay;
|
||||
private FabricatedOverlay(FabricatedOverlayInternal overlay) {
|
||||
mOverlay = overlay;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.content.om;
|
||||
|
||||
import android.content.om.OverlayIdentifier;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.content.om.OverlayManagerTransaction;
|
||||
|
||||
@@ -65,6 +66,17 @@ interface IOverlayManager {
|
||||
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
|
||||
OverlayInfo getOverlayInfo(in String packageName, in int userId);
|
||||
|
||||
/**
|
||||
* Returns information about the overlay with the given package name for the
|
||||
* specified user.
|
||||
*
|
||||
* @param packageName The name of the overlay package.
|
||||
* @param userId The user to get the OverlayInfo for.
|
||||
* @return The OverlayInfo for the overlay package; or null if no such
|
||||
* overlay package exists.
|
||||
*/
|
||||
OverlayInfo getOverlayInfoByIdentifier(in OverlayIdentifier packageName, in int userId);
|
||||
|
||||
/**
|
||||
* Request that an overlay package be enabled or disabled when possible to
|
||||
* do so.
|
||||
@@ -163,7 +175,7 @@ interface IOverlayManager {
|
||||
* Invalidates and removes the idmap for an overlay,
|
||||
* @param packageName The name of the overlay package whose idmap should be deleted.
|
||||
*/
|
||||
void invalidateCachesForOverlay(in String packageName, in int userIs);
|
||||
void invalidateCachesForOverlay(in String packageName, in int userId);
|
||||
|
||||
/**
|
||||
* Perform a series of requests related to overlay packages. This is an
|
||||
|
||||
19
core/java/android/content/om/OverlayIdentifier.aidl
Normal file
19
core/java/android/content/om/OverlayIdentifier.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* 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 android.content.om;
|
||||
|
||||
parcelable OverlayIdentifier;
|
||||
208
core/java/android/content/om/OverlayIdentifier.java
Normal file
208
core/java/android/content/om/OverlayIdentifier.java
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* 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 android.content.om;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.DataClass;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A key used to uniquely identify a Runtime Resource Overlay (RRO).
|
||||
*
|
||||
* An overlay always belongs to a package and may optionally have a name associated with it.
|
||||
* The name helps uniquely identify a particular overlay within a package.
|
||||
* @hide
|
||||
*/
|
||||
/** @hide */
|
||||
@DataClass(genConstructor = false, genBuilder = false, genHiddenBuilder = false,
|
||||
genEqualsHashCode = true, genToString = false)
|
||||
public class OverlayIdentifier implements Parcelable {
|
||||
/**
|
||||
* The package name containing or owning the overlay.
|
||||
*/
|
||||
@Nullable
|
||||
private final String mPackageName;
|
||||
|
||||
/**
|
||||
* The unique name within the package of the overlay.
|
||||
*/
|
||||
@Nullable
|
||||
private final String mOverlayName;
|
||||
|
||||
/**
|
||||
* Creates an identifier from a package and unique name within the package.
|
||||
*
|
||||
* @param packageName the package containing or owning the overlay
|
||||
* @param overlayName the unique name of the overlay within the package
|
||||
*/
|
||||
public OverlayIdentifier(@NonNull String packageName, @Nullable String overlayName) {
|
||||
mPackageName = packageName;
|
||||
mOverlayName = overlayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an identifier for an overlay without a name.
|
||||
*
|
||||
* @param packageName the package containing or owning the overlay
|
||||
*/
|
||||
public OverlayIdentifier(@NonNull String packageName) {
|
||||
mPackageName = packageName;
|
||||
mOverlayName = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mOverlayName == null ? mPackageName : mPackageName + ":" + mOverlayName;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static OverlayIdentifier fromString(@NonNull String text) {
|
||||
final String[] parts = text.split(":", 2);
|
||||
if (parts.length == 2) {
|
||||
return new OverlayIdentifier(parts[0], parts[1]);
|
||||
} else {
|
||||
return new OverlayIdentifier(parts[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Code below generated by codegen v1.0.22.
|
||||
//
|
||||
// DO NOT MODIFY!
|
||||
// CHECKSTYLE:OFF Generated code
|
||||
//
|
||||
// To regenerate run:
|
||||
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/om/OverlayIdentifier.java
|
||||
//
|
||||
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
|
||||
// Settings > Editor > Code Style > Formatter Control
|
||||
//@formatter:off
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the package name containing or owning the overlay.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @Nullable String getPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the unique name within the package of the overlay.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @Nullable String getOverlayName() {
|
||||
return mOverlayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public boolean equals(@Nullable Object o) {
|
||||
// You can override field equality logic by defining either of the methods like:
|
||||
// boolean fieldNameEquals(OverlayIdentifier other) { ... }
|
||||
// boolean fieldNameEquals(FieldType otherValue) { ... }
|
||||
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
@SuppressWarnings("unchecked")
|
||||
OverlayIdentifier that = (OverlayIdentifier) o;
|
||||
//noinspection PointlessBooleanExpression
|
||||
return true
|
||||
&& Objects.equals(mPackageName, that.mPackageName)
|
||||
&& Objects.equals(mOverlayName, that.mOverlayName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public int hashCode() {
|
||||
// You can override field hashCode logic by defining methods like:
|
||||
// int fieldNameHashCode() { ... }
|
||||
|
||||
int _hash = 1;
|
||||
_hash = 31 * _hash + Objects.hashCode(mPackageName);
|
||||
_hash = 31 * _hash + Objects.hashCode(mOverlayName);
|
||||
return _hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
// You can override field parcelling by defining methods like:
|
||||
// void parcelFieldName(Parcel dest, int flags) { ... }
|
||||
|
||||
byte flg = 0;
|
||||
if (mPackageName != null) flg |= 0x1;
|
||||
if (mOverlayName != null) flg |= 0x2;
|
||||
dest.writeByte(flg);
|
||||
if (mPackageName != null) dest.writeString(mPackageName);
|
||||
if (mOverlayName != null) dest.writeString(mOverlayName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public int describeContents() { return 0; }
|
||||
|
||||
/** @hide */
|
||||
@SuppressWarnings({"unchecked", "RedundantCast"})
|
||||
@DataClass.Generated.Member
|
||||
protected OverlayIdentifier(@NonNull Parcel in) {
|
||||
// You can override field unparcelling by defining methods like:
|
||||
// static FieldType unparcelFieldName(Parcel in) { ... }
|
||||
|
||||
byte flg = in.readByte();
|
||||
String packageName = (flg & 0x1) == 0 ? null : in.readString();
|
||||
String overlayName = (flg & 0x2) == 0 ? null : in.readString();
|
||||
|
||||
this.mPackageName = packageName;
|
||||
this.mOverlayName = overlayName;
|
||||
|
||||
// onConstructed(); // You can define this method to get a callback
|
||||
}
|
||||
|
||||
@DataClass.Generated.Member
|
||||
public static final @NonNull Parcelable.Creator<OverlayIdentifier> CREATOR
|
||||
= new Parcelable.Creator<OverlayIdentifier>() {
|
||||
@Override
|
||||
public OverlayIdentifier[] newArray(int size) {
|
||||
return new OverlayIdentifier[size];
|
||||
}
|
||||
|
||||
@Override
|
||||
public OverlayIdentifier createFromParcel(@NonNull Parcel in) {
|
||||
return new OverlayIdentifier(in);
|
||||
}
|
||||
};
|
||||
|
||||
@DataClass.Generated(
|
||||
time = 1612482438728L,
|
||||
codegenVersion = "1.0.22",
|
||||
sourceFile = "frameworks/base/core/java/android/content/om/OverlayIdentifier.java",
|
||||
inputSignatures = "private final @android.annotation.Nullable java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mOverlayName\npublic @java.lang.Override java.lang.String toString()\npublic static android.content.om.OverlayIdentifier fromString(java.lang.String)\nclass OverlayIdentifier extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genHiddenBuilder=false, genEqualsHashCode=true, genToString=false)")
|
||||
@Deprecated
|
||||
private void __metadata() {}
|
||||
|
||||
|
||||
//@formatter:on
|
||||
// End of generated code
|
||||
|
||||
}
|
||||
@@ -26,6 +26,8 @@ import android.os.Build;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Objects;
|
||||
@@ -37,7 +39,7 @@ import java.util.Objects;
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class OverlayInfo implements Parcelable {
|
||||
public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
|
||||
|
||||
/** @hide */
|
||||
@IntDef(prefix = "STATE_", value = {
|
||||
@@ -142,6 +144,14 @@ public final class OverlayInfo implements Parcelable {
|
||||
@NonNull
|
||||
public final String packageName;
|
||||
|
||||
/**
|
||||
* The unique name within the package of the overlay.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public final String overlayName;
|
||||
|
||||
/**
|
||||
* Package name of the target package
|
||||
*
|
||||
@@ -201,6 +211,14 @@ public final class OverlayInfo implements Parcelable {
|
||||
*/
|
||||
public final boolean isMutable;
|
||||
|
||||
private OverlayIdentifier mIdentifierCached;
|
||||
|
||||
/**
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final boolean isFabricated;
|
||||
|
||||
/**
|
||||
* Create a new OverlayInfo based on source with an updated state.
|
||||
*
|
||||
@@ -210,17 +228,28 @@ public final class OverlayInfo implements Parcelable {
|
||||
* @hide
|
||||
*/
|
||||
public OverlayInfo(@NonNull OverlayInfo source, @State int state) {
|
||||
this(source.packageName, source.targetPackageName, source.targetOverlayableName,
|
||||
source.category, source.baseCodePath, state, source.userId, source.priority,
|
||||
source.isMutable);
|
||||
this(source.packageName, source.overlayName, source.targetPackageName,
|
||||
source.targetOverlayableName, source.category, source.baseCodePath, state,
|
||||
source.userId, source.priority, source.isMutable, source.isFabricated);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@VisibleForTesting
|
||||
public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName,
|
||||
@Nullable String targetOverlayableName, @Nullable String category,
|
||||
@NonNull String baseCodePath, int state, int userId,
|
||||
int priority, boolean isMutable) {
|
||||
@NonNull String baseCodePath, int state, int userId, int priority, boolean isMutable) {
|
||||
this(packageName, null /* overlayName */, targetPackageName, targetOverlayableName,
|
||||
category, baseCodePath, state, userId, priority, isMutable,
|
||||
false /* isFabricated */);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public OverlayInfo(@NonNull String packageName, @Nullable String overlayName,
|
||||
@NonNull String targetPackageName, @Nullable String targetOverlayableName,
|
||||
@Nullable String category, @NonNull String baseCodePath, int state, int userId,
|
||||
int priority, boolean isMutable, boolean isFabricated) {
|
||||
this.packageName = packageName;
|
||||
this.overlayName = overlayName;
|
||||
this.targetPackageName = targetPackageName;
|
||||
this.targetOverlayableName = targetOverlayableName;
|
||||
this.category = category;
|
||||
@@ -229,12 +258,14 @@ public final class OverlayInfo implements Parcelable {
|
||||
this.userId = userId;
|
||||
this.priority = priority;
|
||||
this.isMutable = isMutable;
|
||||
this.isFabricated = isFabricated;
|
||||
ensureValidState();
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public OverlayInfo(Parcel source) {
|
||||
packageName = source.readString();
|
||||
overlayName = source.readString();
|
||||
targetPackageName = source.readString();
|
||||
targetOverlayableName = source.readString();
|
||||
category = source.readString();
|
||||
@@ -243,13 +274,15 @@ public final class OverlayInfo implements Parcelable {
|
||||
userId = source.readInt();
|
||||
priority = source.readInt();
|
||||
isMutable = source.readBoolean();
|
||||
isFabricated = source.readBoolean();
|
||||
ensureValidState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns package name of the current overlay.
|
||||
* {@inheritDoc}
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public String getPackageName() {
|
||||
@@ -257,9 +290,20 @@ public final class OverlayInfo implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target package name of the current overlay.
|
||||
* {@inheritDoc}
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public String getOverlayName() {
|
||||
return overlayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public String getTargetPackageName() {
|
||||
@@ -268,7 +312,8 @@ public final class OverlayInfo implements Parcelable {
|
||||
|
||||
/**
|
||||
* Returns the category of the current overlay.
|
||||
* @hide\
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
@@ -278,6 +323,7 @@ public final class OverlayInfo implements Parcelable {
|
||||
|
||||
/**
|
||||
* Returns user handle for which this overlay applies to.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@@ -287,15 +333,47 @@ public final class OverlayInfo implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of the target overlayable declaration.
|
||||
* {@inheritDoc}
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public String getTargetOverlayableName() {
|
||||
return targetOverlayableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public boolean isFabricated() {
|
||||
return isFabricated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Full path to the base APK or fabricated overlay for this overlay package.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public String getBaseCodePath() {
|
||||
return baseCodePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
@NonNull
|
||||
public OverlayIdentifier getOverlayIdentifier() {
|
||||
if (mIdentifierCached == null) {
|
||||
mIdentifierCached = new OverlayIdentifier(packageName, overlayName);
|
||||
}
|
||||
return mIdentifierCached;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private void ensureValidState() {
|
||||
if (packageName == null) {
|
||||
@@ -330,6 +408,7 @@ public final class OverlayInfo implements Parcelable {
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(packageName);
|
||||
dest.writeString(overlayName);
|
||||
dest.writeString(targetPackageName);
|
||||
dest.writeString(targetOverlayableName);
|
||||
dest.writeString(category);
|
||||
@@ -338,6 +417,7 @@ public final class OverlayInfo implements Parcelable {
|
||||
dest.writeInt(userId);
|
||||
dest.writeInt(priority);
|
||||
dest.writeBoolean(isMutable);
|
||||
dest.writeBoolean(isFabricated);
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Parcelable.Creator<OverlayInfo> CREATOR =
|
||||
@@ -410,6 +490,7 @@ public final class OverlayInfo implements Parcelable {
|
||||
result = prime * result + userId;
|
||||
result = prime * result + state;
|
||||
result = prime * result + ((packageName == null) ? 0 : packageName.hashCode());
|
||||
result = prime * result + ((overlayName == null) ? 0 : overlayName.hashCode());
|
||||
result = prime * result + ((targetPackageName == null) ? 0 : targetPackageName.hashCode());
|
||||
result = prime * result + ((targetOverlayableName == null) ? 0
|
||||
: targetOverlayableName.hashCode());
|
||||
@@ -439,6 +520,9 @@ public final class OverlayInfo implements Parcelable {
|
||||
if (!packageName.equals(other.packageName)) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(overlayName, other.overlayName)) {
|
||||
return false;
|
||||
}
|
||||
if (!targetPackageName.equals(other.targetPackageName)) {
|
||||
return false;
|
||||
}
|
||||
@@ -457,9 +541,13 @@ public final class OverlayInfo implements Parcelable {
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OverlayInfo { overlay=" + packageName + ", targetPackage=" + targetPackageName
|
||||
+ ((targetOverlayableName == null) ? ""
|
||||
: ", targetOverlayable=" + targetOverlayableName)
|
||||
+ ", state=" + state + " (" + stateToString(state) + "), userId=" + userId + " }";
|
||||
return "OverlayInfo {"
|
||||
+ "packageName=" + packageName
|
||||
+ ", overlayName=" + overlayName
|
||||
+ ", targetPackage=" + targetPackageName
|
||||
+ ", targetOverlayable=" + targetOverlayableName
|
||||
+ ", state=" + state + " (" + stateToString(state) + "),"
|
||||
+ ", userId=" + userId
|
||||
+ " }";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +204,25 @@ public class OverlayManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about the overlay represented by the identifier for the specified user.
|
||||
*
|
||||
* @param overlay the identifier representing the overlay
|
||||
* @param userHandle the user of which to get overlay state info
|
||||
* @return the overlay info or null if the overlay cannot be found
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public OverlayInfo getOverlayInfo(@NonNull final OverlayIdentifier overlay,
|
||||
@NonNull final UserHandle userHandle) {
|
||||
try {
|
||||
return mService.getOverlayInfoByIdentifier(overlay, userHandle.getIdentifier());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about all overlays for the given target package for
|
||||
* the specified user. The returned list is ordered according to the
|
||||
|
||||
@@ -20,6 +20,9 @@ import static com.android.internal.util.Preconditions.checkNotNull;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.UserHandle;
|
||||
@@ -29,6 +32,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Container for a batch of requests to the OverlayManagerService.
|
||||
@@ -60,12 +64,13 @@ public class OverlayManagerTransaction
|
||||
|
||||
private OverlayManagerTransaction(@NonNull final Parcel source) {
|
||||
final int size = source.readInt();
|
||||
mRequests = new ArrayList<Request>(size);
|
||||
mRequests = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
final int request = source.readInt();
|
||||
final String packageName = source.readString();
|
||||
final OverlayIdentifier overlay = source.readParcelable(null);
|
||||
final int userId = source.readInt();
|
||||
mRequests.add(new Request(request, packageName, userId));
|
||||
final Bundle extras = source.readBundle(null);
|
||||
mRequests.add(new Request(request, overlay, userId, extras));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,22 +100,36 @@ public class OverlayManagerTransaction
|
||||
|
||||
public static final int TYPE_SET_ENABLED = 0;
|
||||
public static final int TYPE_SET_DISABLED = 1;
|
||||
public static final int TYPE_REGISTER_FABRICATED = 2;
|
||||
public static final int TYPE_UNREGISTER_FABRICATED = 3;
|
||||
|
||||
@RequestType public final int type;
|
||||
public final String packageName;
|
||||
public static final String BUNDLE_FABRICATED_OVERLAY = "fabricated_overlay";
|
||||
|
||||
@RequestType
|
||||
public final int type;
|
||||
@NonNull
|
||||
public final OverlayIdentifier overlay;
|
||||
public final int userId;
|
||||
@Nullable
|
||||
public final Bundle extras;
|
||||
|
||||
public Request(@RequestType final int type, @NonNull final String packageName,
|
||||
public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay,
|
||||
final int userId) {
|
||||
this(type, overlay, userId, null /* extras */);
|
||||
}
|
||||
|
||||
public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay,
|
||||
final int userId, @Nullable Bundle extras) {
|
||||
this.type = type;
|
||||
this.packageName = packageName;
|
||||
this.overlay = overlay;
|
||||
this.userId = userId;
|
||||
this.extras = extras;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Request{type=0x%02x (%s), packageName=%s, userId=%d}",
|
||||
type, typeToString(), packageName, userId);
|
||||
return String.format(Locale.US, "Request{type=0x%02x (%s), overlay=%s, userId=%d}",
|
||||
type, typeToString(), overlay, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,6 +142,8 @@ public class OverlayManagerTransaction
|
||||
switch (type) {
|
||||
case TYPE_SET_ENABLED: return "TYPE_SET_ENABLED";
|
||||
case TYPE_SET_DISABLED: return "TYPE_SET_DISABLED";
|
||||
case TYPE_REGISTER_FABRICATED: return "TYPE_REGISTER_FABRICATED";
|
||||
case TYPE_UNREGISTER_FABRICATED: return "TYPE_UNREGISTER_FABRICATED";
|
||||
default: return String.format("TYPE_UNKNOWN (0x%02x)", type);
|
||||
}
|
||||
}
|
||||
@@ -152,22 +173,56 @@ public class OverlayManagerTransaction
|
||||
* longer affect the resources of the target package. If the target is
|
||||
* currently running, its outdated resources will be replaced by new ones.
|
||||
*
|
||||
* @param packageName The name of the overlay package.
|
||||
* @param overlay The name of the overlay package.
|
||||
* @param enable true to enable the overlay, false to disable it.
|
||||
* @return this Builder object, so you can chain additional requests
|
||||
*/
|
||||
public Builder setEnabled(@NonNull String packageName, boolean enable) {
|
||||
return setEnabled(packageName, enable, UserHandle.myUserId());
|
||||
public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable) {
|
||||
return setEnabled(overlay, enable, UserHandle.myUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public Builder setEnabled(@NonNull String packageName, boolean enable, int userId) {
|
||||
checkNotNull(packageName);
|
||||
public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable, int userId) {
|
||||
checkNotNull(overlay);
|
||||
@Request.RequestType final int type =
|
||||
enable ? Request.TYPE_SET_ENABLED : Request.TYPE_SET_DISABLED;
|
||||
mRequests.add(new Request(type, packageName, userId));
|
||||
mRequests.add(new Request(type, overlay, userId));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the fabricated overlay with the overlay manager so it can be enabled and
|
||||
* disabled for any user.
|
||||
*
|
||||
* The fabricated overlay is initialized in a disabled state. If an overlay is re-registered
|
||||
* the existing overlay will be replaced by the newly registered overlay and the enabled
|
||||
* state of the overlay will be left unchanged if the target package and target overlayable
|
||||
* have not changed.
|
||||
*
|
||||
* @param overlay the overlay to register with the overlay manager
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public Builder registerFabricatedOverlay(@NonNull FabricatedOverlay overlay) {
|
||||
final Bundle extras = new Bundle();
|
||||
extras.putParcelable(Request.BUNDLE_FABRICATED_OVERLAY, overlay.mOverlay);
|
||||
mRequests.add(new Request(Request.TYPE_REGISTER_FABRICATED, overlay.getIdentifier(),
|
||||
UserHandle.USER_ALL, extras));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables and removes the overlay from the overlay manager for all users.
|
||||
*
|
||||
* @param overlay the overlay to disable and remove
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public Builder unregisterFabricatedOverlay(@NonNull OverlayIdentifier overlay) {
|
||||
mRequests.add(new Request(Request.TYPE_UNREGISTER_FABRICATED, overlay,
|
||||
UserHandle.USER_ALL));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -195,8 +250,9 @@ public class OverlayManagerTransaction
|
||||
for (int i = 0; i < size; i++) {
|
||||
final Request req = mRequests.get(i);
|
||||
dest.writeInt(req.type);
|
||||
dest.writeString(req.packageName);
|
||||
dest.writeParcelable(req.overlay, flags);
|
||||
dest.writeInt(req.userId);
|
||||
dest.writeBundle(req.extras);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2807,7 +2807,7 @@ public class ParsingPackageUtils {
|
||||
* limits length of the name to the {@link #MAX_FILE_NAME_SIZE}.
|
||||
* @return Success if it's valid.
|
||||
*/
|
||||
public static ParseResult validateName(ParseInput input, String name, boolean requireSeparator,
|
||||
public static String validateName(String name, boolean requireSeparator,
|
||||
boolean requireFilename) {
|
||||
final int N = name.length();
|
||||
boolean hasSep = false;
|
||||
@@ -2828,18 +2828,28 @@ public class ParsingPackageUtils {
|
||||
front = true;
|
||||
continue;
|
||||
}
|
||||
return input.error("bad character '" + c + "'");
|
||||
return "bad character '" + c + "'";
|
||||
}
|
||||
if (requireFilename) {
|
||||
if (!FileUtils.isValidExtFilename(name)) {
|
||||
return input.error("Invalid filename");
|
||||
return "Invalid filename";
|
||||
} else if (N > MAX_FILE_NAME_SIZE) {
|
||||
return input.error("the length of the name is greater than " + MAX_FILE_NAME_SIZE);
|
||||
return "the length of the name is greater than " + MAX_FILE_NAME_SIZE;
|
||||
}
|
||||
}
|
||||
return hasSep || !requireSeparator
|
||||
? input.success(null)
|
||||
: input.error("must have at least one '.' separator");
|
||||
return hasSep || !requireSeparator ? null : "must have at least one '.' separator";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #validateName(String, boolean, boolean)
|
||||
*/
|
||||
public static ParseResult validateName(ParseInput input, String name, boolean requireSeparator,
|
||||
boolean requireFilename) {
|
||||
final String errorMessage = validateName(name, requireSeparator, requireFilename);
|
||||
if (errorMessage != null) {
|
||||
return input.error(errorMessage);
|
||||
}
|
||||
return input.success(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -188,7 +188,7 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t forma
|
||||
ATRACE_NAME(base::StringPrintf("LoadApkAssets(%s)", path.c_str()).c_str());
|
||||
|
||||
auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
|
||||
std::unique_ptr<const ApkAssets> apk_assets;
|
||||
std::unique_ptr<ApkAssets> apk_assets;
|
||||
switch (format) {
|
||||
case FORMAT_APK: {
|
||||
auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<resources>
|
||||
<string name="str">none</string>
|
||||
<string name="str2">none</string>
|
||||
<integer name="overlaid">0</integer>
|
||||
<integer name="matrix_100000">100</integer>
|
||||
<integer name="matrix_100001">100</integer>
|
||||
<integer name="matrix_100010">100</integer>
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* 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 com.android.overlaytest;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertNull;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.om.FabricatedOverlay;
|
||||
import android.content.om.OverlayIdentifier;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.content.om.OverlayManager;
|
||||
import android.content.om.OverlayManagerTransaction;
|
||||
import android.content.res.Resources;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.MediumTest;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
@MediumTest
|
||||
public class FabricatedOverlaysTest {
|
||||
private static final String TAG = "FabricatedOverlaysTest";
|
||||
private final String TEST_RESOURCE = "integer/overlaid";
|
||||
private final String TEST_OVERLAY_NAME = "Test";
|
||||
|
||||
private Context mContext;
|
||||
private Resources mResources;
|
||||
private OverlayManager mOverlayManager;
|
||||
private int mUserId;
|
||||
private UserHandle mUserHandle;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mContext = InstrumentationRegistry.getTargetContext();
|
||||
mResources = mContext.getResources();
|
||||
mOverlayManager = mContext.getSystemService(OverlayManager.class);
|
||||
mUserId = UserHandle.myUserId();
|
||||
mUserHandle = UserHandle.of(mUserId);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
final OverlayManagerTransaction.Builder cleanUp = new OverlayManagerTransaction.Builder();
|
||||
mOverlayManager.getOverlayInfosForTarget(mContext.getPackageName(), mUserHandle).forEach(
|
||||
info -> {
|
||||
if (info.isFabricated()) {
|
||||
cleanUp.unregisterFabricatedOverlay(info.getOverlayIdentifier());
|
||||
}
|
||||
});
|
||||
mOverlayManager.commit(cleanUp.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFabricatedOverlay() throws Exception {
|
||||
final FabricatedOverlay overlay = new FabricatedOverlay.Builder(
|
||||
mContext.getPackageName(), TEST_OVERLAY_NAME, mContext.getPackageName())
|
||||
.setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
|
||||
.build();
|
||||
|
||||
waitForResourceValue(0);
|
||||
mOverlayManager.commit(new OverlayManagerTransaction.Builder()
|
||||
.registerFabricatedOverlay(overlay)
|
||||
.build());
|
||||
|
||||
OverlayInfo info = mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle);
|
||||
assertNotNull(info);
|
||||
assertFalse(info.isEnabled());
|
||||
|
||||
mOverlayManager.commit(new OverlayManagerTransaction.Builder()
|
||||
.setEnabled(overlay.getIdentifier(), true, mUserId)
|
||||
.build());
|
||||
|
||||
info = mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle);
|
||||
assertNotNull(info);
|
||||
assertTrue(info.isEnabled());
|
||||
|
||||
waitForResourceValue(1);
|
||||
mOverlayManager.commit(new OverlayManagerTransaction.Builder()
|
||||
.unregisterFabricatedOverlay(overlay.getIdentifier())
|
||||
.build());
|
||||
|
||||
waitForResourceValue(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterEnableAtomic() throws Exception {
|
||||
final FabricatedOverlay overlay = new FabricatedOverlay.Builder(
|
||||
mContext.getPackageName(), TEST_OVERLAY_NAME, mContext.getPackageName())
|
||||
.setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
|
||||
.build();
|
||||
|
||||
waitForResourceValue(0);
|
||||
mOverlayManager.commit(new OverlayManagerTransaction.Builder()
|
||||
.registerFabricatedOverlay(overlay)
|
||||
.setEnabled(overlay.getIdentifier(), true, mUserId)
|
||||
.build());
|
||||
|
||||
waitForResourceValue(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterTwice() throws Exception {
|
||||
FabricatedOverlay overlay = new FabricatedOverlay.Builder(
|
||||
mContext.getPackageName(), TEST_OVERLAY_NAME, mContext.getPackageName())
|
||||
.setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
|
||||
.build();
|
||||
|
||||
waitForResourceValue(0);
|
||||
mOverlayManager.commit(new OverlayManagerTransaction.Builder()
|
||||
.registerFabricatedOverlay(overlay)
|
||||
.setEnabled(overlay.getIdentifier(), true, mUserId)
|
||||
.build());
|
||||
|
||||
waitForResourceValue(1);
|
||||
overlay = new FabricatedOverlay.Builder(
|
||||
mContext.getPackageName(), TEST_OVERLAY_NAME, mContext.getPackageName())
|
||||
.setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 2)
|
||||
.build();
|
||||
|
||||
mOverlayManager.commit(new OverlayManagerTransaction.Builder()
|
||||
.registerFabricatedOverlay(overlay)
|
||||
.build());
|
||||
waitForResourceValue(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidOwningPackageName() throws Exception {
|
||||
final FabricatedOverlay overlay = new FabricatedOverlay.Builder(
|
||||
"android", TEST_OVERLAY_NAME, mContext.getPackageName())
|
||||
.setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
|
||||
.build();
|
||||
|
||||
waitForResourceValue(0);
|
||||
assertThrows(SecurityException.class, () ->
|
||||
mOverlayManager.commit(new OverlayManagerTransaction.Builder()
|
||||
.registerFabricatedOverlay(overlay)
|
||||
.setEnabled(overlay.getIdentifier(), true, mUserId)
|
||||
.build()));
|
||||
|
||||
assertNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidOverlayName() throws Exception {
|
||||
final FabricatedOverlay overlay = new FabricatedOverlay.Builder(
|
||||
mContext.getPackageName(), "invalid@name", mContext.getPackageName())
|
||||
.setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
|
||||
.build();
|
||||
|
||||
waitForResourceValue(0);
|
||||
assertThrows(SecurityException.class, () ->
|
||||
mOverlayManager.commit(new OverlayManagerTransaction.Builder()
|
||||
.registerFabricatedOverlay(overlay)
|
||||
.setEnabled(overlay.getIdentifier(), true, mUserId)
|
||||
.build()));
|
||||
|
||||
assertNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverlayIdentifierLongest() throws Exception {
|
||||
final int maxLength = 255 - 11; // 11 reserved characters
|
||||
final String longestName = String.join("",
|
||||
Collections.nCopies(maxLength - mContext.getPackageName().length(), "a"));
|
||||
{
|
||||
FabricatedOverlay overlay = new FabricatedOverlay.Builder(mContext.getPackageName(),
|
||||
longestName, mContext.getPackageName())
|
||||
.setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
|
||||
.build();
|
||||
|
||||
mOverlayManager.commit(new OverlayManagerTransaction.Builder()
|
||||
.registerFabricatedOverlay(overlay)
|
||||
.build());
|
||||
assertNotNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle));
|
||||
}
|
||||
{
|
||||
FabricatedOverlay overlay = new FabricatedOverlay.Builder(mContext.getPackageName(),
|
||||
longestName + "a", mContext.getPackageName())
|
||||
.setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
|
||||
.build();
|
||||
|
||||
assertThrows(SecurityException.class, () ->
|
||||
mOverlayManager.commit(new OverlayManagerTransaction.Builder()
|
||||
.registerFabricatedOverlay(overlay)
|
||||
.build()));
|
||||
|
||||
assertNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidResourceValues() throws Exception {
|
||||
final FabricatedOverlay overlay = new FabricatedOverlay.Builder(
|
||||
"android", TEST_OVERLAY_NAME, mContext.getPackageName())
|
||||
.setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
|
||||
.setResourceValue("something", TypedValue.TYPE_INT_DEC, 1)
|
||||
.build();
|
||||
|
||||
waitForResourceValue(0);
|
||||
assertThrows(SecurityException.class, () ->
|
||||
mOverlayManager.commit(new OverlayManagerTransaction.Builder()
|
||||
.registerFabricatedOverlay(overlay)
|
||||
.setEnabled(overlay.getIdentifier(), true, mUserId)
|
||||
.build()));
|
||||
|
||||
assertNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionFailRollback() throws Exception {
|
||||
final FabricatedOverlay overlay = new FabricatedOverlay.Builder(
|
||||
mContext.getPackageName(), TEST_OVERLAY_NAME, mContext.getPackageName())
|
||||
.setResourceValue(TEST_RESOURCE, TypedValue.TYPE_INT_DEC, 1)
|
||||
.build();
|
||||
|
||||
waitForResourceValue(0);
|
||||
assertThrows(SecurityException.class, () ->
|
||||
mOverlayManager.commit(new OverlayManagerTransaction.Builder()
|
||||
.registerFabricatedOverlay(overlay)
|
||||
.setEnabled(overlay.getIdentifier(), true, mUserId)
|
||||
.setEnabled(new OverlayIdentifier("not-valid"), true, mUserId)
|
||||
.build()));
|
||||
|
||||
assertNull(mOverlayManager.getOverlayInfo(overlay.getIdentifier(), mUserHandle));
|
||||
}
|
||||
|
||||
void waitForResourceValue(final int expectedValue) throws TimeoutException {
|
||||
final long timeOutDuration = 10000;
|
||||
final long endTime = System.currentTimeMillis() + timeOutDuration;
|
||||
final String resourceName = TEST_RESOURCE;
|
||||
final int resourceId = mResources.getIdentifier(resourceName, "",
|
||||
mContext.getPackageName());
|
||||
int resourceValue = 0;
|
||||
while (System.currentTimeMillis() < endTime) {
|
||||
resourceValue = mResources.getInteger(resourceId);
|
||||
if (resourceValue == expectedValue) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
final String paths = TextUtils.join(",", mResources.getAssets().getApkPaths());
|
||||
Log.w(TAG, "current paths: [" + paths + "]", new Throwable());
|
||||
throw new TimeoutException("Timed out waiting for '" + resourceName + "' value to equal '"
|
||||
+ expectedValue + "': current value is '" + resourceValue + "'");
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.content.om.OverlayIdentifier;
|
||||
import android.content.om.OverlayManager;
|
||||
import android.content.om.OverlayManagerTransaction;
|
||||
import android.os.UserHandle;
|
||||
@@ -32,14 +33,14 @@ import java.util.concurrent.FutureTask;
|
||||
class LocalOverlayManager {
|
||||
private static final long TIMEOUT = 30;
|
||||
|
||||
public static void toggleOverlaysAndWait(@NonNull final String[] overlaysToEnable,
|
||||
@NonNull final String[] overlaysToDisable) throws Exception {
|
||||
public static void toggleOverlaysAndWait(@NonNull final OverlayIdentifier[] overlaysToEnable,
|
||||
@NonNull final OverlayIdentifier[] overlaysToDisable) throws Exception {
|
||||
final int userId = UserHandle.myUserId();
|
||||
OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder();
|
||||
for (String pkg : overlaysToEnable) {
|
||||
for (OverlayIdentifier pkg : overlaysToEnable) {
|
||||
builder.setEnabled(pkg, true, userId);
|
||||
}
|
||||
for (String pkg : overlaysToDisable) {
|
||||
for (OverlayIdentifier pkg : overlaysToDisable) {
|
||||
builder.setEnabled(pkg, false, userId);
|
||||
}
|
||||
OverlayManagerTransaction transaction = builder.build();
|
||||
@@ -48,7 +49,7 @@ class LocalOverlayManager {
|
||||
FutureTask<Boolean> task = new FutureTask<>(() -> {
|
||||
while (true) {
|
||||
final String[] paths = ctx.getResources().getAssets().getApkPaths();
|
||||
if (arrayTailContains(paths, overlaysToEnable)
|
||||
if (arrayTailContainsOverlays(paths, overlaysToEnable)
|
||||
&& arrayDoesNotContain(paths, overlaysToDisable)) {
|
||||
return true;
|
||||
}
|
||||
@@ -64,15 +65,15 @@ class LocalOverlayManager {
|
||||
task.get(TIMEOUT, SECONDS);
|
||||
}
|
||||
|
||||
private static boolean arrayTailContains(@NonNull final String[] array,
|
||||
@NonNull final String[] substrings) {
|
||||
if (array.length < substrings.length) {
|
||||
private static boolean arrayTailContainsOverlays(@NonNull final String[] array,
|
||||
@NonNull final OverlayIdentifier[] overlays) {
|
||||
if (array.length < overlays.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < substrings.length; i++) {
|
||||
String a = array[array.length - substrings.length + i];
|
||||
String s = substrings[i];
|
||||
if (!a.contains(s)) {
|
||||
for (int i = 0; i < overlays.length; i++) {
|
||||
String a = array[array.length - overlays.length + i];
|
||||
OverlayIdentifier s = overlays[i];
|
||||
if (!a.contains(s.getPackageName())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -80,10 +81,10 @@ class LocalOverlayManager {
|
||||
}
|
||||
|
||||
private static boolean arrayDoesNotContain(@NonNull final String[] array,
|
||||
@NonNull final String[] substrings) {
|
||||
for (String s : substrings) {
|
||||
@NonNull final OverlayIdentifier[] overlays) {
|
||||
for (OverlayIdentifier s : overlays) {
|
||||
for (String a : array) {
|
||||
if (a.contains(s)) {
|
||||
if (a.contains(s.getPackageName())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.om.OverlayIdentifier;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
@@ -58,9 +59,12 @@ public abstract class OverlayBaseTest {
|
||||
static final int MODE_SINGLE_OVERLAY = 1;
|
||||
static final int MODE_MULTIPLE_OVERLAYS = 2;
|
||||
|
||||
static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one";
|
||||
static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two";
|
||||
static final String FRAMEWORK_OVERLAY_PKG = "com.android.overlaytest.framework";
|
||||
static final OverlayIdentifier APP_OVERLAY_ONE_PKG =
|
||||
new OverlayIdentifier("com.android.overlaytest.app_overlay_one");
|
||||
static final OverlayIdentifier APP_OVERLAY_TWO_PKG =
|
||||
new OverlayIdentifier("com.android.overlaytest.app_overlay_two");
|
||||
static final OverlayIdentifier FRAMEWORK_OVERLAY_PKG =
|
||||
new OverlayIdentifier("com.android.overlaytest.framework");
|
||||
|
||||
protected OverlayBaseTest(int mode) {
|
||||
mMode = mode;
|
||||
|
||||
@@ -16,14 +16,19 @@
|
||||
|
||||
package com.android.overlaytest;
|
||||
|
||||
import static com.android.overlaytest.OverlayBaseTest.APP_OVERLAY_ONE_PKG;
|
||||
import static com.android.overlaytest.OverlayBaseTest.APP_OVERLAY_TWO_PKG;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.om.OverlayIdentifier;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.content.om.OverlayManager;
|
||||
import android.content.om.OverlayManagerTransaction;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.os.UserHandle;
|
||||
|
||||
@@ -40,8 +45,6 @@ import java.util.List;
|
||||
@RunWith(JUnit4.class)
|
||||
@MediumTest
|
||||
public class TransactionTest {
|
||||
static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one";
|
||||
static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two";
|
||||
|
||||
private Context mContext;
|
||||
private Resources mResources;
|
||||
@@ -58,8 +61,8 @@ public class TransactionTest {
|
||||
mUserHandle = UserHandle.of(mUserId);
|
||||
|
||||
LocalOverlayManager.toggleOverlaysAndWait(
|
||||
new String[]{},
|
||||
new String[]{APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
|
||||
new OverlayIdentifier[]{},
|
||||
new OverlayIdentifier[]{APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -78,8 +81,8 @@ public class TransactionTest {
|
||||
List<OverlayInfo> ois =
|
||||
mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
|
||||
assertEquals(ois.size(), 2);
|
||||
assertEquals(ois.get(0).packageName, APP_OVERLAY_ONE_PKG);
|
||||
assertEquals(ois.get(1).packageName, APP_OVERLAY_TWO_PKG);
|
||||
assertEquals(ois.get(0).getOverlayIdentifier(), APP_OVERLAY_ONE_PKG);
|
||||
assertEquals(ois.get(1).getOverlayIdentifier(), APP_OVERLAY_TWO_PKG);
|
||||
|
||||
OverlayManagerTransaction t2 = new OverlayManagerTransaction.Builder()
|
||||
.setEnabled(APP_OVERLAY_TWO_PKG, true)
|
||||
@@ -92,8 +95,8 @@ public class TransactionTest {
|
||||
List<OverlayInfo> ois2 =
|
||||
mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
|
||||
assertEquals(ois2.size(), 2);
|
||||
assertEquals(ois2.get(0).packageName, APP_OVERLAY_TWO_PKG);
|
||||
assertEquals(ois2.get(1).packageName, APP_OVERLAY_ONE_PKG);
|
||||
assertEquals(ois2.get(0).getOverlayIdentifier(), APP_OVERLAY_TWO_PKG);
|
||||
assertEquals(ois2.get(1).getOverlayIdentifier(), APP_OVERLAY_ONE_PKG);
|
||||
|
||||
OverlayManagerTransaction t3 = new OverlayManagerTransaction.Builder()
|
||||
.setEnabled(APP_OVERLAY_TWO_PKG, false)
|
||||
@@ -105,8 +108,8 @@ public class TransactionTest {
|
||||
List<OverlayInfo> ois3 =
|
||||
mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
|
||||
assertEquals(ois3.size(), 2);
|
||||
assertEquals(ois3.get(0).packageName, APP_OVERLAY_TWO_PKG);
|
||||
assertEquals(ois3.get(1).packageName, APP_OVERLAY_ONE_PKG);
|
||||
assertEquals(ois3.get(0).getOverlayIdentifier(), APP_OVERLAY_TWO_PKG);
|
||||
assertEquals(ois3.get(1).getOverlayIdentifier(), APP_OVERLAY_ONE_PKG);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,7 +119,7 @@ public class TransactionTest {
|
||||
|
||||
OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
|
||||
.setEnabled(APP_OVERLAY_ONE_PKG, true)
|
||||
.setEnabled("does-not-exist", true)
|
||||
.setEnabled(new OverlayIdentifier("does-not-exist"), true)
|
||||
.setEnabled(APP_OVERLAY_TWO_PKG, true)
|
||||
.build();
|
||||
assertThrows(SecurityException.class, () -> mOverlayManager.commit(t));
|
||||
@@ -125,9 +128,10 @@ public class TransactionTest {
|
||||
assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
|
||||
}
|
||||
|
||||
private void assertOverlayIsEnabled(final String packageName, boolean enabled, int userId) {
|
||||
final OverlayInfo oi = mOverlayManager.getOverlayInfo(packageName, UserHandle.of(userId));
|
||||
private void assertOverlayIsEnabled(final OverlayIdentifier overlay, boolean enabled,
|
||||
int userId) {
|
||||
final OverlayInfo oi = mOverlayManager.getOverlayInfo(overlay, UserHandle.of(userId));
|
||||
assertNotNull(oi);
|
||||
assertEquals(oi.isEnabled(), enabled);
|
||||
assertEquals(enabled, oi.isEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.overlaytest;
|
||||
|
||||
import android.content.om.OverlayIdentifier;
|
||||
|
||||
import androidx.test.filters.MediumTest;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
@@ -32,7 +34,9 @@ public class WithMultipleOverlaysTest extends OverlayBaseTest {
|
||||
@BeforeClass
|
||||
public static void enableOverlay() throws Exception {
|
||||
LocalOverlayManager.toggleOverlaysAndWait(
|
||||
new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG},
|
||||
new String[]{});
|
||||
new OverlayIdentifier[]{
|
||||
FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG
|
||||
},
|
||||
new OverlayIdentifier[]{});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.overlaytest;
|
||||
|
||||
import android.content.om.OverlayIdentifier;
|
||||
|
||||
import androidx.test.filters.MediumTest;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
@@ -32,7 +34,7 @@ public class WithOverlayTest extends OverlayBaseTest {
|
||||
@BeforeClass
|
||||
public static void enableOverlays() throws Exception {
|
||||
LocalOverlayManager.toggleOverlaysAndWait(
|
||||
new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG},
|
||||
new String[]{APP_OVERLAY_TWO_PKG});
|
||||
new OverlayIdentifier[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG},
|
||||
new OverlayIdentifier[]{APP_OVERLAY_TWO_PKG});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.overlaytest;
|
||||
|
||||
import android.content.om.OverlayIdentifier;
|
||||
|
||||
import androidx.test.filters.MediumTest;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
@@ -32,7 +34,9 @@ public class WithoutOverlayTest extends OverlayBaseTest {
|
||||
@BeforeClass
|
||||
public static void disableOverlays() throws Exception {
|
||||
LocalOverlayManager.toggleOverlaysAndWait(
|
||||
new String[]{},
|
||||
new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
|
||||
new OverlayIdentifier[]{},
|
||||
new OverlayIdentifier[]{
|
||||
FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
<item target="integer/matrix_100100" value="@integer/matrix_100100"/>
|
||||
<item target="integer/matrix_100101" value="@integer/matrix_100101"/>
|
||||
<item target="integer/matrix_100110" value="@integer/matrix_100110"/>
|
||||
<item target="integer/matrix_100110" value="@integer/matrix_100110"/>
|
||||
<item target="integer/matrix_100111" value="@integer/matrix_100111"/>
|
||||
<item target="integer/matrix_101000" value="@integer/matrix_101000"/>
|
||||
<item target="integer/matrix_101001" value="@integer/matrix_101001"/>
|
||||
|
||||
@@ -83,8 +83,16 @@ std::unique_ptr<ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<AssetsProvider> overlay_assets;
|
||||
const std::string overlay_path(loaded_idmap->OverlayApkPath());
|
||||
auto overlay_assets = ZipAssetsProvider::Create(overlay_path);
|
||||
if (IsFabricatedOverlay(overlay_path)) {
|
||||
// Fabricated overlays do not contain resource definitions. All of the overlay resource values
|
||||
// are defined inline in the idmap.
|
||||
overlay_assets = EmptyAssetsProvider::Create();
|
||||
} else {
|
||||
// The overlay should be an APK.
|
||||
overlay_assets = ZipAssetsProvider::Create(overlay_path);
|
||||
}
|
||||
if (overlay_assets == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -102,9 +102,8 @@ AssetManager2::AssetManager2() {
|
||||
memset(&configuration_, 0, sizeof(configuration_));
|
||||
}
|
||||
|
||||
bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets,
|
||||
bool invalidate_caches) {
|
||||
apk_assets_ = apk_assets;
|
||||
bool AssetManager2::SetApkAssets(std::vector<const ApkAssets*> apk_assets, bool invalidate_caches) {
|
||||
apk_assets_ = std::move(apk_assets);
|
||||
BuildDynamicRefTable();
|
||||
RebuildFilterList();
|
||||
if (invalidate_caches) {
|
||||
@@ -137,6 +136,36 @@ void AssetManager2::BuildDynamicRefTable() {
|
||||
// 0x01 is reserved for the android package.
|
||||
int next_package_id = 0x02;
|
||||
for (const ApkAssets* apk_assets : sorted_apk_assets) {
|
||||
std::shared_ptr<OverlayDynamicRefTable> overlay_ref_table;
|
||||
if (auto loaded_idmap = apk_assets->GetLoadedIdmap(); loaded_idmap != nullptr) {
|
||||
// The target package must precede the overlay package in the apk assets paths in order
|
||||
// to take effect.
|
||||
auto iter = apk_assets_package_ids.find(std::string(loaded_idmap->TargetApkPath()));
|
||||
if (iter == apk_assets_package_ids.end()) {
|
||||
LOG(INFO) << "failed to find target package for overlay "
|
||||
<< loaded_idmap->OverlayApkPath();
|
||||
} else {
|
||||
uint8_t target_package_id = iter->second;
|
||||
|
||||
// Create a special dynamic reference table for the overlay to rewrite references to
|
||||
// overlay resources as references to the target resources they overlay.
|
||||
overlay_ref_table = std::make_shared<OverlayDynamicRefTable>(
|
||||
loaded_idmap->GetOverlayDynamicRefTable(target_package_id));
|
||||
|
||||
// Add the overlay resource map to the target package's set of overlays.
|
||||
const uint8_t target_idx = package_ids_[target_package_id];
|
||||
CHECK(target_idx != 0xff) << "overlay target '" << loaded_idmap->TargetApkPath()
|
||||
<< "'added to apk_assets_package_ids but does not have an"
|
||||
<< " assigned package group";
|
||||
|
||||
PackageGroup& target_package_group = package_groups_[target_idx];
|
||||
target_package_group.overlays_.push_back(
|
||||
ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id,
|
||||
overlay_ref_table.get()),
|
||||
apk_assets_cookies[apk_assets]});
|
||||
}
|
||||
}
|
||||
|
||||
const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc();
|
||||
for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) {
|
||||
// Get the package ID or assign one if a shared library.
|
||||
@@ -147,50 +176,25 @@ void AssetManager2::BuildDynamicRefTable() {
|
||||
package_id = package->GetPackageId();
|
||||
}
|
||||
|
||||
// Add the mapping for package ID to index if not present.
|
||||
uint8_t idx = package_ids_[package_id];
|
||||
if (idx == 0xff) {
|
||||
// Add the mapping for package ID to index if not present.
|
||||
package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size());
|
||||
package_groups_.push_back({});
|
||||
PackageGroup& new_group = package_groups_.emplace_back();
|
||||
|
||||
if (apk_assets->IsOverlay()) {
|
||||
// The target package must precede the overlay package in the apk assets paths in order
|
||||
// to take effect.
|
||||
const auto& loaded_idmap = apk_assets->GetLoadedIdmap();
|
||||
auto target_package_iter = apk_assets_package_ids.find(
|
||||
std::string(loaded_idmap->TargetApkPath()));
|
||||
if (target_package_iter == apk_assets_package_ids.end()) {
|
||||
LOG(INFO) << "failed to find target package for overlay "
|
||||
<< loaded_idmap->OverlayApkPath();
|
||||
} else {
|
||||
const uint8_t target_package_id = target_package_iter->second;
|
||||
const uint8_t target_idx = package_ids_[target_package_id];
|
||||
CHECK(target_idx != 0xff) << "overlay added to apk_assets_package_ids but does not"
|
||||
<< " have an assigned package group";
|
||||
|
||||
PackageGroup& target_package_group = package_groups_[target_idx];
|
||||
|
||||
// Create a special dynamic reference table for the overlay to rewrite references to
|
||||
// overlay resources as references to the target resources they overlay.
|
||||
auto overlay_table = std::make_shared<OverlayDynamicRefTable>(
|
||||
loaded_idmap->GetOverlayDynamicRefTable(target_package_id));
|
||||
package_groups_.back().dynamic_ref_table = overlay_table;
|
||||
|
||||
// Add the overlay resource map to the target package's set of overlays.
|
||||
target_package_group.overlays_.push_back(
|
||||
ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id,
|
||||
overlay_table.get()),
|
||||
apk_assets_cookies[apk_assets]});
|
||||
}
|
||||
if (overlay_ref_table != nullptr) {
|
||||
// If this package is from an overlay, use a dynamic reference table that can rewrite
|
||||
// overlay resource ids to their corresponding target resource ids.
|
||||
new_group.dynamic_ref_table = overlay_ref_table;
|
||||
}
|
||||
|
||||
DynamicRefTable* ref_table = package_groups_.back().dynamic_ref_table.get();
|
||||
DynamicRefTable* ref_table = new_group.dynamic_ref_table.get();
|
||||
ref_table->mAssignedPackageId = package_id;
|
||||
ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
|
||||
}
|
||||
PackageGroup* package_group = &package_groups_[idx];
|
||||
|
||||
// Add the package and to the set of packages with the same ID.
|
||||
PackageGroup* package_group = &package_groups_[idx];
|
||||
package_group->packages_.push_back(ConfiguredPackage{package.get(), {}});
|
||||
package_group->cookies_.push_back(apk_assets_cookies[apk_assets]);
|
||||
|
||||
@@ -578,7 +582,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
|
||||
|
||||
const PackageGroup& package_group = package_groups_[package_idx];
|
||||
auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
|
||||
stop_at_first_match, ignore_configuration);
|
||||
stop_at_first_match, ignore_configuration);
|
||||
if (UNLIKELY(!result.has_value())) {
|
||||
return base::unexpected(result.error());
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const {
|
||||
return value_;
|
||||
}
|
||||
|
||||
ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path,
|
||||
ZipAssetsProvider::ZipAssetsProvider(ZipArchiveHandle handle, PathOrDebugName&& path,
|
||||
time_t last_mod_time)
|
||||
: zip_handle_(handle, ::CloseArchive),
|
||||
name_(std::forward<PathOrDebugName>(path)),
|
||||
@@ -93,7 +93,7 @@ ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path,
|
||||
std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path) {
|
||||
ZipArchiveHandle handle;
|
||||
if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) {
|
||||
LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
|
||||
LOG(ERROR) << "Failed to open APK '" << path << "': " << ::ErrorCodeString(result);
|
||||
CloseArchive(handle);
|
||||
return {};
|
||||
}
|
||||
@@ -253,6 +253,14 @@ bool ZipAssetsProvider::ForEachFile(const std::string& root_path,
|
||||
return result == -1;
|
||||
}
|
||||
|
||||
std::optional<uint32_t> ZipAssetsProvider::GetCrc(std::string_view path) const {
|
||||
::ZipEntry entry;
|
||||
if (FindEntry(zip_handle_.get(), path, &entry) != 0) {
|
||||
return {};
|
||||
}
|
||||
return entry.crc32;
|
||||
}
|
||||
|
||||
const std::string& ZipAssetsProvider::GetDebugName() const {
|
||||
return name_.GetDebugName();
|
||||
}
|
||||
|
||||
@@ -54,12 +54,6 @@ struct Idmap_header {
|
||||
};
|
||||
|
||||
struct Idmap_data_header {
|
||||
uint8_t target_package_id;
|
||||
uint8_t overlay_package_id;
|
||||
|
||||
// Padding to ensure 4 byte alignment for target_entry_count
|
||||
uint16_t p0;
|
||||
|
||||
uint32_t target_entry_count;
|
||||
uint32_t target_inline_entry_count;
|
||||
uint32_t overlay_entry_count;
|
||||
@@ -158,19 +152,19 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
// The resource ids encoded within the idmap are build-time resource ids.
|
||||
target_res_id = (0x00FFFFFFU & target_res_id)
|
||||
| (((uint32_t) data_header_->target_package_id) << 24U);
|
||||
// The resource ids encoded within the idmap are build-time resource ids so do not consider the
|
||||
// package id when determining if the resource in the target package is overlaid.
|
||||
target_res_id &= 0x00FFFFFFU;
|
||||
|
||||
// Check if the target resource is mapped to an overlay resource.
|
||||
auto first_entry = entries_;
|
||||
auto end_entry = entries_ + dtohl(data_header_->target_entry_count);
|
||||
auto entry = std::lower_bound(first_entry, end_entry, target_res_id,
|
||||
[](const Idmap_target_entry &e, const uint32_t target_id) {
|
||||
return dtohl(e.target_id) < target_id;
|
||||
[](const Idmap_target_entry& e, const uint32_t target_id) {
|
||||
return (0x00FFFFFFU & dtohl(e.target_id)) < target_id;
|
||||
});
|
||||
|
||||
if (entry != end_entry && dtohl(entry->target_id) == target_res_id) {
|
||||
if (entry != end_entry && (0x00FFFFFFU & dtohl(entry->target_id)) == target_res_id) {
|
||||
uint32_t overlay_resource_id = dtohl(entry->overlay_id);
|
||||
// Lookup the resource without rewriting the overlay resource id back to the target resource id
|
||||
// being looked up.
|
||||
@@ -182,12 +176,13 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
|
||||
auto first_inline_entry = inline_entries_;
|
||||
auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count);
|
||||
auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id,
|
||||
[](const Idmap_target_entry_inline &e,
|
||||
[](const Idmap_target_entry_inline& e,
|
||||
const uint32_t target_id) {
|
||||
return dtohl(e.target_id) < target_id;
|
||||
return (0x00FFFFFFU & dtohl(e.target_id)) < target_id;
|
||||
});
|
||||
|
||||
if (inline_entry != end_inline_entry && dtohl(inline_entry->target_id) == target_res_id) {
|
||||
if (inline_entry != end_inline_entry &&
|
||||
(0x00FFFFFFU & dtohl(inline_entry->target_id)) == target_res_id) {
|
||||
return Result(inline_entry->value);
|
||||
}
|
||||
return {};
|
||||
@@ -235,7 +230,7 @@ std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size
|
||||
}
|
||||
return std::string_view(data, *len);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
|
||||
const Idmap_header* header,
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -44,6 +45,7 @@
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <binder/TextOutput.h>
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef INT32_MAX
|
||||
@@ -233,6 +235,15 @@ void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs
|
||||
fill9patchOffsets(reinterpret_cast<Res_png_9patch*>(outData));
|
||||
}
|
||||
|
||||
bool IsFabricatedOverlay(const std::string& path) {
|
||||
std::ifstream fin(path);
|
||||
uint32_t magic;
|
||||
if (fin.read(reinterpret_cast<char*>(&magic), sizeof(uint32_t))) {
|
||||
return magic == kFabricatedOverlayMagic;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool assertIdmapHeader(const void* idmap, size_t size) {
|
||||
if (reinterpret_cast<uintptr_t>(idmap) & 0x03) {
|
||||
ALOGE("idmap: header is not word aligned");
|
||||
|
||||
@@ -101,7 +101,7 @@ class AssetManager2 {
|
||||
// Only pass invalidate_caches=false when it is known that the structure
|
||||
// change in ApkAssets is due to a safe addition of resources with completely
|
||||
// new resource IDs.
|
||||
bool SetApkAssets(const std::vector<const ApkAssets*>& apk_assets, bool invalidate_caches = true);
|
||||
bool SetApkAssets(std::vector<const ApkAssets*> apk_assets, bool invalidate_caches = true);
|
||||
|
||||
inline const std::vector<const ApkAssets*> GetApkAssets() const {
|
||||
return apk_assets_;
|
||||
|
||||
@@ -88,6 +88,8 @@ struct ZipAssetsProvider : public AssetsProvider {
|
||||
WARN_UNUSED const std::string& GetDebugName() const override;
|
||||
WARN_UNUSED bool IsUpToDate() const override;
|
||||
|
||||
WARN_UNUSED std::optional<uint32_t> GetCrc(std::string_view path) const;
|
||||
|
||||
~ZipAssetsProvider() override = default;
|
||||
protected:
|
||||
std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode,
|
||||
|
||||
@@ -168,15 +168,14 @@ class LoadedIdmap {
|
||||
}
|
||||
|
||||
// Returns a mapping from target resource ids to overlay values.
|
||||
const IdmapResMap GetTargetResourcesMap(
|
||||
uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const {
|
||||
const IdmapResMap GetTargetResourcesMap(uint8_t target_assigned_package_id,
|
||||
const OverlayDynamicRefTable* overlay_ref_table) const {
|
||||
return IdmapResMap(data_header_, target_entries_, target_inline_entries_,
|
||||
target_assigned_package_id, overlay_ref_table);
|
||||
}
|
||||
|
||||
// Returns a dynamic reference table for a loaded overlay package.
|
||||
const OverlayDynamicRefTable GetOverlayDynamicRefTable(
|
||||
uint8_t target_assigned_package_id) const {
|
||||
const OverlayDynamicRefTable GetOverlayDynamicRefTable(uint8_t target_assigned_package_id) const {
|
||||
return OverlayDynamicRefTable(data_header_, overlay_entries_, target_assigned_package_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,8 +43,19 @@
|
||||
|
||||
namespace android {
|
||||
|
||||
constexpr const static uint32_t kIdmapMagic = 0x504D4449u;
|
||||
constexpr const static uint32_t kIdmapCurrentVersion = 0x00000007u;
|
||||
constexpr const uint32_t kIdmapMagic = 0x504D4449u;
|
||||
constexpr const uint32_t kIdmapCurrentVersion = 0x00000008u;
|
||||
|
||||
// This must never change.
|
||||
constexpr const uint32_t kFabricatedOverlayMagic = 0x4f525246; // FRRO (big endian)
|
||||
|
||||
// The version should only be changed when a backwards-incompatible change must be made to the
|
||||
// fabricated overlay file format. Old fabricated overlays must be migrated to the new file format
|
||||
// to prevent losing fabricated overlay data.
|
||||
constexpr const uint32_t kFabricatedOverlayCurrentVersion = 1;
|
||||
|
||||
// Returns whether or not the path represents a fabricated overlay.
|
||||
bool IsFabricatedOverlay(const std::string& path);
|
||||
|
||||
/**
|
||||
* In C++11, char16_t is defined as *at least* 16 bits. We do a lot of
|
||||
|
||||
Binary file not shown.
@@ -82,6 +82,7 @@ java_library_static {
|
||||
":framework_native_aidl",
|
||||
":gsiservice_aidl",
|
||||
":idmap2_aidl",
|
||||
":idmap2_core_aidl",
|
||||
":inputconstants_aidl",
|
||||
":installd_aidl",
|
||||
":storaged_aidl",
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.om;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.content.om.OverlayIdentifier;
|
||||
import android.os.UserHandle;
|
||||
|
||||
/**
|
||||
@@ -26,6 +27,7 @@ import android.os.UserHandle;
|
||||
public final class DumpState {
|
||||
@UserIdInt private int mUserId = UserHandle.USER_ALL;
|
||||
@Nullable private String mPackageName;
|
||||
@Nullable private String mOverlayName;
|
||||
@Nullable private String mField;
|
||||
private boolean mVerbose;
|
||||
|
||||
@@ -38,14 +40,19 @@ public final class DumpState {
|
||||
}
|
||||
|
||||
/** Sets the name of the package to dump the state for */
|
||||
public void setPackageName(String packageName) {
|
||||
mPackageName = packageName;
|
||||
public void setOverlyIdentifier(String overlayIdentifier) {
|
||||
final OverlayIdentifier overlay = OverlayIdentifier.fromString(overlayIdentifier);
|
||||
mPackageName = overlay.getPackageName();
|
||||
mOverlayName = overlay.getOverlayName();
|
||||
}
|
||||
@Nullable public String getPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
@Nullable public String getOverlayName() {
|
||||
return mOverlayName;
|
||||
}
|
||||
|
||||
/** Sets the name of the field to dump the state of */
|
||||
/** Sets the name of the field to dump the state for */
|
||||
public void setField(String field) {
|
||||
mField = field;
|
||||
}
|
||||
|
||||
@@ -20,17 +20,23 @@ import static android.content.Context.IDMAP_SERVICE;
|
||||
|
||||
import static com.android.server.om.OverlayManagerService.TAG;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.FabricatedOverlayInfo;
|
||||
import android.os.FabricatedOverlayInternal;
|
||||
import android.os.IBinder;
|
||||
import android.os.IIdmap2;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemService;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.FgThread;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@@ -104,10 +110,12 @@ class IdmapDaemon {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
String createIdmap(String targetPath, String overlayPath, int policies, boolean enforce,
|
||||
int userId) throws TimeoutException, RemoteException {
|
||||
String createIdmap(@NonNull String targetPath, @NonNull String overlayPath,
|
||||
@Nullable String overlayName, int policies, boolean enforce, int userId)
|
||||
throws TimeoutException, RemoteException {
|
||||
try (Connection c = connect()) {
|
||||
return mService.createIdmap(targetPath, overlayPath, policies, enforce, userId);
|
||||
return mService.createIdmap(targetPath, overlayPath, TextUtils.emptyIfNull(overlayName),
|
||||
policies, enforce, userId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,11 +125,12 @@ class IdmapDaemon {
|
||||
}
|
||||
}
|
||||
|
||||
boolean verifyIdmap(String targetPath, String overlayPath, int policies, boolean enforce,
|
||||
int userId)
|
||||
boolean verifyIdmap(@NonNull String targetPath, @NonNull String overlayPath,
|
||||
@Nullable String overlayName, int policies, boolean enforce, int userId)
|
||||
throws Exception {
|
||||
try (Connection c = connect()) {
|
||||
return mService.verifyIdmap(targetPath, overlayPath, policies, enforce, userId);
|
||||
return mService.verifyIdmap(targetPath, overlayPath, TextUtils.emptyIfNull(overlayName),
|
||||
policies, enforce, userId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,12 +138,38 @@ class IdmapDaemon {
|
||||
try (Connection c = connect()) {
|
||||
return new File(mService.getIdmapPath(overlayPath, userId)).isFile();
|
||||
} catch (Exception e) {
|
||||
Slog.wtf(TAG, "failed to check if idmap exists for " + overlayPath + ": "
|
||||
+ e.getMessage());
|
||||
Slog.wtf(TAG, "failed to check if idmap exists for " + overlayPath, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
FabricatedOverlayInfo createFabricatedOverlay(@NonNull FabricatedOverlayInternal overlay) {
|
||||
try (Connection c = connect()) {
|
||||
return mService.createFabricatedOverlay(overlay);
|
||||
} catch (Exception e) {
|
||||
Slog.wtf(TAG, "failed to fabricate overlay " + overlay, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
boolean deleteFabricatedOverlay(@NonNull String path) {
|
||||
try (Connection c = connect()) {
|
||||
return mService.deleteFabricatedOverlay(path);
|
||||
} catch (Exception e) {
|
||||
Slog.wtf(TAG, "failed to delete fabricated overlay '" + path + "'", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
List<FabricatedOverlayInfo> getFabricatedOverlayInfos() {
|
||||
try (Connection c = connect()) {
|
||||
return mService.getFabricatedOverlayInfos();
|
||||
} catch (Exception e) {
|
||||
Slog.wtf(TAG, "failed to get fabricated overlays", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private IBinder getIdmapService() throws TimeoutException, RemoteException {
|
||||
SystemService.start(IDMAP_DAEMON);
|
||||
|
||||
|
||||
@@ -22,15 +22,19 @@ import static com.android.server.om.OverlayManagerService.TAG;
|
||||
import android.annotation.NonNull;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.content.om.OverlayableInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageParser;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.FabricatedOverlayInfo;
|
||||
import android.os.FabricatedOverlayInternal;
|
||||
import android.os.OverlayablePolicy;
|
||||
import android.os.SystemProperties;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Handle the creation and deletion of idmap files.
|
||||
@@ -74,25 +78,26 @@ final class IdmapManager {
|
||||
* Creates the idmap for the target/overlay combination and returns whether the idmap file was
|
||||
* modified.
|
||||
*/
|
||||
boolean createIdmap(@NonNull final PackageInfo targetPackage,
|
||||
@NonNull final PackageInfo overlayPackage, int userId) {
|
||||
boolean createIdmap(@NonNull final AndroidPackage targetPackage,
|
||||
@NonNull final AndroidPackage overlayPackage, String overlayBasePath,
|
||||
String overlayName, int userId) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "create idmap for " + targetPackage.packageName + " and "
|
||||
+ overlayPackage.packageName);
|
||||
Slog.d(TAG, "create idmap for " + targetPackage.getPackageName() + " and "
|
||||
+ overlayPackage.getPackageName());
|
||||
}
|
||||
final String targetPath = targetPackage.applicationInfo.getBaseCodePath();
|
||||
final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath();
|
||||
final String targetPath = targetPackage.getBaseApkPath();
|
||||
try {
|
||||
int policies = calculateFulfilledPolicies(targetPackage, overlayPackage, userId);
|
||||
boolean enforce = enforceOverlayable(overlayPackage);
|
||||
if (mIdmapDaemon.verifyIdmap(targetPath, overlayPath, policies, enforce, userId)) {
|
||||
if (mIdmapDaemon.verifyIdmap(targetPath, overlayBasePath, overlayName, policies,
|
||||
enforce, userId)) {
|
||||
return false;
|
||||
}
|
||||
return mIdmapDaemon.createIdmap(targetPath, overlayPath, policies,
|
||||
return mIdmapDaemon.createIdmap(targetPath, overlayBasePath, overlayName, policies,
|
||||
enforce, userId) != null;
|
||||
} catch (Exception e) {
|
||||
Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
|
||||
+ overlayPath + ": " + e.getMessage());
|
||||
+ overlayBasePath, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -104,7 +109,7 @@ final class IdmapManager {
|
||||
try {
|
||||
return mIdmapDaemon.removeIdmap(oi.baseCodePath, userId);
|
||||
} catch (Exception e) {
|
||||
Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath + ": " + e.getMessage());
|
||||
Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -113,22 +118,40 @@ final class IdmapManager {
|
||||
return mIdmapDaemon.idmapExists(oi.baseCodePath, oi.userId);
|
||||
}
|
||||
|
||||
boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) {
|
||||
return mIdmapDaemon.idmapExists(overlayPackage.applicationInfo.getBaseCodePath(), userId);
|
||||
/**
|
||||
* @return the list of all fabricated overlays
|
||||
*/
|
||||
List<FabricatedOverlayInfo> getFabricatedOverlayInfos() {
|
||||
return mIdmapDaemon.getFabricatedOverlayInfos();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a fabricated overlay and persists it to disk.
|
||||
* @return the path to the fabricated overlay
|
||||
*/
|
||||
FabricatedOverlayInfo createFabricatedOverlay(@NonNull FabricatedOverlayInternal overlay) {
|
||||
return mIdmapDaemon.createFabricatedOverlay(overlay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the fabricated overlay file on disk.
|
||||
* @return whether the path was deleted
|
||||
*/
|
||||
boolean deleteFabricatedOverlay(@NonNull String path) {
|
||||
return mIdmapDaemon.deleteFabricatedOverlay(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if overlayable and policies should be enforced on the specified overlay for backwards
|
||||
* compatibility with pre-Q overlays.
|
||||
*/
|
||||
private boolean enforceOverlayable(@NonNull final PackageInfo overlayPackage) {
|
||||
final ApplicationInfo ai = overlayPackage.applicationInfo;
|
||||
if (ai.targetSdkVersion >= VERSION_CODES.Q) {
|
||||
private boolean enforceOverlayable(@NonNull final AndroidPackage overlayPackage) {
|
||||
if (overlayPackage.getTargetSdkVersion() >= VERSION_CODES.Q) {
|
||||
// Always enforce policies for overlays targeting Q+.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ai.isVendor()) {
|
||||
if (overlayPackage.isVendor()) {
|
||||
// If the overlay is on a pre-Q vendor partition, do not enforce overlayable
|
||||
// restrictions on this overlay because the pre-Q platform has no understanding of
|
||||
// overlayable.
|
||||
@@ -137,20 +160,19 @@ final class IdmapManager {
|
||||
|
||||
// Do not enforce overlayable restrictions on pre-Q overlays that are signed with the
|
||||
// platform signature or that are preinstalled.
|
||||
return !(ai.isSystemApp() || ai.isSignedWithPlatformKey());
|
||||
return !(overlayPackage.isSystem() || overlayPackage.isSignedWithPlatformKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a bitmask for idmap2 that represents the policies the overlay fulfills.
|
||||
*/
|
||||
private int calculateFulfilledPolicies(@NonNull final PackageInfo targetPackage,
|
||||
@NonNull final PackageInfo overlayPackage, int userId) {
|
||||
final ApplicationInfo ai = overlayPackage.applicationInfo;
|
||||
private int calculateFulfilledPolicies(@NonNull final AndroidPackage targetPackage,
|
||||
@NonNull final AndroidPackage overlayPackage, int userId) {
|
||||
int fulfilledPolicies = OverlayablePolicy.PUBLIC;
|
||||
|
||||
// Overlay matches target signature
|
||||
if (mPackageManager.signaturesMatching(targetPackage.packageName,
|
||||
overlayPackage.packageName, userId)) {
|
||||
if (mPackageManager.signaturesMatching(targetPackage.getPackageName(),
|
||||
overlayPackage.getPackageName(), userId)) {
|
||||
fulfilledPolicies |= OverlayablePolicy.SIGNATURE;
|
||||
}
|
||||
|
||||
@@ -164,52 +186,52 @@ final class IdmapManager {
|
||||
// preinstalled package, check if overlay matches its signature.
|
||||
if (!TextUtils.isEmpty(mConfigSignaturePackage)
|
||||
&& mPackageManager.signaturesMatching(mConfigSignaturePackage,
|
||||
overlayPackage.packageName,
|
||||
overlayPackage.getPackageName(),
|
||||
userId)) {
|
||||
fulfilledPolicies |= OverlayablePolicy.CONFIG_SIGNATURE;
|
||||
}
|
||||
|
||||
// Vendor partition (/vendor)
|
||||
if (ai.isVendor()) {
|
||||
if (overlayPackage.isVendor()) {
|
||||
return fulfilledPolicies | OverlayablePolicy.VENDOR_PARTITION;
|
||||
}
|
||||
|
||||
// Product partition (/product)
|
||||
if (ai.isProduct()) {
|
||||
if (overlayPackage.isProduct()) {
|
||||
return fulfilledPolicies | OverlayablePolicy.PRODUCT_PARTITION;
|
||||
}
|
||||
|
||||
// Odm partition (/odm)
|
||||
if (ai.isOdm()) {
|
||||
if (overlayPackage.isOdm()) {
|
||||
return fulfilledPolicies | OverlayablePolicy.ODM_PARTITION;
|
||||
}
|
||||
|
||||
// Oem partition (/oem)
|
||||
if (ai.isOem()) {
|
||||
if (overlayPackage.isOem()) {
|
||||
return fulfilledPolicies | OverlayablePolicy.OEM_PARTITION;
|
||||
}
|
||||
|
||||
// System_ext partition (/system_ext) is considered as system
|
||||
// Check this last since every partition except for data is scanned as system in the PMS.
|
||||
if (ai.isSystemApp() || ai.isSystemExt()) {
|
||||
if (overlayPackage.isSystem() || overlayPackage.isSystemExt()) {
|
||||
return fulfilledPolicies | OverlayablePolicy.SYSTEM_PARTITION;
|
||||
}
|
||||
|
||||
return fulfilledPolicies;
|
||||
}
|
||||
|
||||
private boolean matchesActorSignature(@NonNull PackageInfo targetPackage,
|
||||
@NonNull PackageInfo overlayPackage, int userId) {
|
||||
String targetOverlayableName = overlayPackage.targetOverlayableName;
|
||||
private boolean matchesActorSignature(@NonNull AndroidPackage targetPackage,
|
||||
@NonNull AndroidPackage overlayPackage, int userId) {
|
||||
String targetOverlayableName = overlayPackage.getOverlayTargetName();
|
||||
if (targetOverlayableName != null) {
|
||||
try {
|
||||
OverlayableInfo overlayableInfo = mPackageManager.getOverlayableForTarget(
|
||||
targetPackage.packageName, targetOverlayableName, userId);
|
||||
targetPackage.getPackageName(), targetOverlayableName, userId);
|
||||
if (overlayableInfo != null && overlayableInfo.actor != null) {
|
||||
String actorPackageName = OverlayActorEnforcer.getPackageNameForActor(
|
||||
overlayableInfo.actor, mPackageManager.getNamedActors()).first;
|
||||
if (mPackageManager.signaturesMatching(actorPackageName,
|
||||
overlayPackage.packageName, userId)) {
|
||||
overlayPackage.getPackageName(), userId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ package com.android.server.om;
|
||||
import android.annotation.NonNull;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.content.om.OverlayableInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Process;
|
||||
import android.text.TextUtils;
|
||||
@@ -29,6 +27,7 @@ import android.util.Pair;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.CollectionUtils;
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@@ -114,12 +113,13 @@ public class OverlayActorEnforcer {
|
||||
}
|
||||
|
||||
final String targetPackageName = overlayInfo.targetPackageName;
|
||||
final PackageInfo targetPkgInfo = mPackageManager.getPackageInfo(targetPackageName, userId);
|
||||
final AndroidPackage targetPkgInfo = mPackageManager.getPackageForUser(targetPackageName,
|
||||
userId);
|
||||
if (targetPkgInfo == null) {
|
||||
return ActorState.TARGET_NOT_FOUND;
|
||||
}
|
||||
|
||||
if ((targetPkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
|
||||
if (targetPkgInfo.isDebuggable()) {
|
||||
return ActorState.ALLOWED;
|
||||
}
|
||||
|
||||
@@ -189,23 +189,18 @@ public class OverlayActorEnforcer {
|
||||
return actorUriState;
|
||||
}
|
||||
|
||||
String packageName = actorUriPair.first;
|
||||
PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, userId);
|
||||
if (packageInfo == null) {
|
||||
return ActorState.MISSING_APP_INFO;
|
||||
}
|
||||
|
||||
ApplicationInfo appInfo = packageInfo.applicationInfo;
|
||||
if (appInfo == null) {
|
||||
return ActorState.MISSING_APP_INFO;
|
||||
String actorPackageName = actorUriPair.first;
|
||||
AndroidPackage actorPackage = mPackageManager.getPackageForUser(actorPackageName, userId);
|
||||
if (actorPackage == null) {
|
||||
return ActorState.ACTOR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Currently only pre-installed apps can be actors
|
||||
if (!appInfo.isSystemApp()) {
|
||||
if (!actorPackage.isSystem()) {
|
||||
return ActorState.ACTOR_NOT_PREINSTALLED;
|
||||
}
|
||||
|
||||
if (ArrayUtils.contains(callingPackageNames, packageName)) {
|
||||
if (ArrayUtils.contains(callingPackageNames, actorPackageName)) {
|
||||
return ActorState.ALLOWED;
|
||||
}
|
||||
|
||||
@@ -231,7 +226,7 @@ public class OverlayActorEnforcer {
|
||||
NO_NAMED_ACTORS,
|
||||
MISSING_NAMESPACE,
|
||||
MISSING_ACTOR_NAME,
|
||||
MISSING_APP_INFO,
|
||||
ACTOR_NOT_FOUND,
|
||||
ACTOR_NOT_PREINSTALLED,
|
||||
INVALID_ACTOR,
|
||||
ALLOWED
|
||||
|
||||
@@ -24,8 +24,10 @@ import static android.content.Intent.ACTION_PACKAGE_REMOVED;
|
||||
import static android.content.Intent.ACTION_USER_ADDED;
|
||||
import static android.content.Intent.ACTION_USER_REMOVED;
|
||||
import static android.content.Intent.EXTRA_REASON;
|
||||
import static android.content.om.OverlayManagerTransaction.Request.TYPE_REGISTER_FABRICATED;
|
||||
import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_DISABLED;
|
||||
import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED;
|
||||
import static android.content.om.OverlayManagerTransaction.Request.TYPE_UNREGISTER_FABRICATED;
|
||||
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
|
||||
import static android.os.Trace.TRACE_TAG_RRO;
|
||||
import static android.os.Trace.traceBegin;
|
||||
@@ -43,11 +45,11 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.om.IOverlayManager;
|
||||
import android.content.om.OverlayIdentifier;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.content.om.OverlayManagerTransaction;
|
||||
import android.content.om.OverlayableInfo;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.content.pm.overlay.OverlayPaths;
|
||||
@@ -55,8 +57,10 @@ import android.content.res.ApkAssets;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Environment;
|
||||
import android.os.FabricatedOverlayInternal;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ResultReceiver;
|
||||
import android.os.ShellCallback;
|
||||
@@ -71,12 +75,15 @@ import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.content.om.OverlayConfig;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.CollectionUtils;
|
||||
import com.android.server.FgThread;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.SystemConfig;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.SystemService.TargetUser;
|
||||
|
||||
import com.android.server.pm.UserManagerService;
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
|
||||
import libcore.util.EmptyArray;
|
||||
|
||||
@@ -92,13 +99,12 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Service to manage asset overlays.
|
||||
@@ -247,15 +253,6 @@ public final class OverlayManagerService extends SystemService {
|
||||
|
||||
private final OverlayActorEnforcer mActorEnforcer;
|
||||
|
||||
private final Consumer<PackageAndUser> mPropagateOverlayChange = (pair) -> {
|
||||
persistSettings();
|
||||
FgThread.getHandler().post(() -> {
|
||||
List<String> affectedTargets = updatePackageManager(pair.packageName, pair.userId);
|
||||
updateActivityManager(affectedTargets, pair.userId);
|
||||
broadcastActionOverlayChanged(pair.packageName, pair.userId);
|
||||
});
|
||||
};
|
||||
|
||||
public OverlayManagerService(@NonNull final Context context) {
|
||||
super(context);
|
||||
try {
|
||||
@@ -315,8 +312,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
// Initialize any users that can't be switched to, as their state would
|
||||
// never be setup in onSwitchUser(). We will switch to the system user right
|
||||
// after this, and its state will be setup there.
|
||||
final List<String> targets = mImpl.updateOverlaysForUser(users.get(i).id);
|
||||
updatePackageManager(targets, users.get(i).id);
|
||||
updatePackageManager(mImpl.updateOverlaysForUser(users.get(i).id));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -333,9 +329,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
// ensure overlays in the settings are up-to-date, and propagate
|
||||
// any asset changes to the rest of the system
|
||||
synchronized (mLock) {
|
||||
final List<String> targets = mImpl.updateOverlaysForUser(newUserId);
|
||||
final List<String> affectedTargets = updatePackageManager(targets, newUserId);
|
||||
updateActivityManager(affectedTargets, newUserId);
|
||||
updateActivityManager(updatePackageManager(mImpl.updateOverlaysForUser(newUserId)));
|
||||
}
|
||||
persistSettings();
|
||||
} finally {
|
||||
@@ -417,19 +411,11 @@ public final class OverlayManagerService extends SystemService {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName);
|
||||
for (final int userId : userIds) {
|
||||
synchronized (mLock) {
|
||||
final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
|
||||
false);
|
||||
if (pi != null && !pi.applicationInfo.isInstantApp()) {
|
||||
mPackageManager.cachePackageInfo(packageName, userId, pi);
|
||||
|
||||
final AndroidPackage pkg = mPackageManager.onPackageAdded(
|
||||
packageName, userId);
|
||||
if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) {
|
||||
try {
|
||||
if (pi.isOverlayPackage()) {
|
||||
mImpl.onOverlayPackageAdded(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
} else {
|
||||
mImpl.onTargetPackageAdded(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
}
|
||||
updateTargetPackages(mImpl.onPackageAdded(packageName, userId));
|
||||
} catch (OperationFailedException e) {
|
||||
Slog.e(TAG, "onPackageAdded internal error", e);
|
||||
}
|
||||
@@ -447,19 +433,11 @@ public final class OverlayManagerService extends SystemService {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName);
|
||||
for (int userId : userIds) {
|
||||
synchronized (mLock) {
|
||||
final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
|
||||
false);
|
||||
if (pi != null && pi.applicationInfo.isInstantApp()) {
|
||||
mPackageManager.cachePackageInfo(packageName, userId, pi);
|
||||
|
||||
final AndroidPackage pkg = mPackageManager.onPackageUpdated(
|
||||
packageName, userId);
|
||||
if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) {
|
||||
try {
|
||||
if (pi.isOverlayPackage()) {
|
||||
mImpl.onOverlayPackageChanged(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
} else {
|
||||
mImpl.onTargetPackageChanged(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
}
|
||||
updateTargetPackages(mImpl.onPackageChanged(packageName, userId));
|
||||
} catch (OperationFailedException e) {
|
||||
Slog.e(TAG, "onPackageChanged internal error", e);
|
||||
}
|
||||
@@ -477,12 +455,11 @@ public final class OverlayManagerService extends SystemService {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName);
|
||||
for (int userId : userIds) {
|
||||
synchronized (mLock) {
|
||||
mPackageManager.forgetPackageInfo(packageName, userId);
|
||||
final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
|
||||
if (oi != null) {
|
||||
final AndroidPackage pkg = mPackageManager.onPackageUpdated(
|
||||
packageName, userId);
|
||||
if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) {
|
||||
try {
|
||||
mImpl.onOverlayPackageReplacing(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
updateTargetPackages(mImpl.onPackageReplacing(packageName, userId));
|
||||
} catch (OperationFailedException e) {
|
||||
Slog.e(TAG, "onPackageReplacing internal error", e);
|
||||
}
|
||||
@@ -500,18 +477,11 @@ public final class OverlayManagerService extends SystemService {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName);
|
||||
for (int userId : userIds) {
|
||||
synchronized (mLock) {
|
||||
final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId,
|
||||
false);
|
||||
if (pi != null && !pi.applicationInfo.isInstantApp()) {
|
||||
mPackageManager.cachePackageInfo(packageName, userId, pi);
|
||||
final AndroidPackage pkg = mPackageManager.onPackageUpdated(
|
||||
packageName, userId);
|
||||
if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) {
|
||||
try {
|
||||
if (pi.isOverlayPackage()) {
|
||||
mImpl.onOverlayPackageReplaced(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
} else {
|
||||
mImpl.onTargetPackageReplaced(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
}
|
||||
updateTargetPackages(mImpl.onPackageReplaced(packageName, userId));
|
||||
} catch (OperationFailedException e) {
|
||||
Slog.e(TAG, "onPackageReplaced internal error", e);
|
||||
}
|
||||
@@ -529,20 +499,8 @@ public final class OverlayManagerService extends SystemService {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName);
|
||||
for (int userId : userIds) {
|
||||
synchronized (mLock) {
|
||||
mPackageManager.forgetPackageInfo(packageName, userId);
|
||||
final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
|
||||
|
||||
try {
|
||||
if (oi != null) {
|
||||
mImpl.onOverlayPackageRemoved(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
} else {
|
||||
mImpl.onTargetPackageRemoved(packageName, userId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
}
|
||||
} catch (OperationFailedException e) {
|
||||
Slog.e(TAG, "onPackageRemoved internal error", e);
|
||||
}
|
||||
mPackageManager.onPackageRemoved(packageName, userId);
|
||||
updateTargetPackages(mImpl.onPackageRemoved(packageName, userId));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -560,11 +518,9 @@ public final class OverlayManagerService extends SystemService {
|
||||
if (userId != UserHandle.USER_NULL) {
|
||||
try {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS ACTION_USER_ADDED");
|
||||
final ArrayList<String> targets;
|
||||
synchronized (mLock) {
|
||||
targets = mImpl.updateOverlaysForUser(userId);
|
||||
updatePackageManager(mImpl.updateOverlaysForUser(userId));
|
||||
}
|
||||
updatePackageManager(targets, userId);
|
||||
} finally {
|
||||
traceEnd(TRACE_TAG_RRO);
|
||||
}
|
||||
@@ -628,16 +584,22 @@ public final class OverlayManagerService extends SystemService {
|
||||
@Override
|
||||
public OverlayInfo getOverlayInfo(@Nullable final String packageName,
|
||||
final int userIdArg) {
|
||||
if (packageName == null) {
|
||||
return getOverlayInfoByIdentifier(new OverlayIdentifier(packageName), userIdArg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OverlayInfo getOverlayInfoByIdentifier(@Nullable final OverlayIdentifier overlay,
|
||||
final int userIdArg) {
|
||||
if (overlay == null || overlay.getPackageName() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#getOverlayInfo " + packageName);
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#getOverlayInfo " + overlay);
|
||||
final int realUserId = handleIncomingUser(userIdArg, "getOverlayInfo");
|
||||
|
||||
synchronized (mLock) {
|
||||
return mImpl.getOverlayInfo(packageName, realUserId);
|
||||
return mImpl.getOverlayInfo(overlay, realUserId);
|
||||
}
|
||||
} finally {
|
||||
traceEnd(TRACE_TAG_RRO);
|
||||
@@ -653,15 +615,16 @@ public final class OverlayManagerService extends SystemService {
|
||||
|
||||
try {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#setEnabled " + packageName + " " + enable);
|
||||
|
||||
final OverlayIdentifier overlay = new OverlayIdentifier(packageName);
|
||||
final int realUserId = handleIncomingUser(userIdArg, "setEnabled");
|
||||
enforceActor(packageName, "setEnabled", realUserId);
|
||||
enforceActor(overlay, "setEnabled", realUserId);
|
||||
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
mImpl.setEnabled(packageName, enable, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
updateTargetPackages(mImpl.setEnabled(overlay, enable, realUserId));
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
@@ -684,16 +647,18 @@ public final class OverlayManagerService extends SystemService {
|
||||
|
||||
try {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusive " + packageName + " " + enable);
|
||||
|
||||
final OverlayIdentifier overlay = new OverlayIdentifier(packageName);
|
||||
final int realUserId = handleIncomingUser(userIdArg, "setEnabledExclusive");
|
||||
enforceActor(packageName, "setEnabledExclusive", realUserId);
|
||||
enforceActor(overlay, "setEnabledExclusive", realUserId);
|
||||
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
mImpl.setEnabledExclusive(packageName,
|
||||
mImpl.setEnabledExclusive(overlay,
|
||||
false /* withinCategory */, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(OverlayManagerService.this::updateTargetPackages);
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
@@ -716,17 +681,19 @@ public final class OverlayManagerService extends SystemService {
|
||||
|
||||
try {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusiveInCategory " + packageName);
|
||||
|
||||
final OverlayIdentifier overlay = new OverlayIdentifier(packageName);
|
||||
final int realUserId = handleIncomingUser(userIdArg,
|
||||
"setEnabledExclusiveInCategory");
|
||||
enforceActor(packageName, "setEnabledExclusiveInCategory", realUserId);
|
||||
enforceActor(overlay, "setEnabledExclusiveInCategory", realUserId);
|
||||
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
mImpl.setEnabledExclusive(packageName,
|
||||
mImpl.setEnabledExclusive(overlay,
|
||||
true /* withinCategory */, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
.ifPresent(OverlayManagerService.this::updateTargetPackages);
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
@@ -750,15 +717,18 @@ public final class OverlayManagerService extends SystemService {
|
||||
try {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#setPriority " + packageName + " "
|
||||
+ parentPackageName);
|
||||
|
||||
final OverlayIdentifier overlay = new OverlayIdentifier(packageName);
|
||||
final OverlayIdentifier parentOverlay = new OverlayIdentifier(parentPackageName);
|
||||
final int realUserId = handleIncomingUser(userIdArg, "setPriority");
|
||||
enforceActor(packageName, "setPriority", realUserId);
|
||||
enforceActor(overlay, "setPriority", realUserId);
|
||||
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
mImpl.setPriority(packageName, parentPackageName, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
mImpl.setPriority(overlay, parentOverlay, realUserId)
|
||||
.ifPresent(OverlayManagerService.this::updateTargetPackages);
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
@@ -780,15 +750,16 @@ public final class OverlayManagerService extends SystemService {
|
||||
|
||||
try {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#setHighestPriority " + packageName);
|
||||
|
||||
final OverlayIdentifier overlay = new OverlayIdentifier(packageName);
|
||||
final int realUserId = handleIncomingUser(userIdArg, "setHighestPriority");
|
||||
enforceActor(packageName, "setHighestPriority", realUserId);
|
||||
enforceActor(overlay, "setHighestPriority", realUserId);
|
||||
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
mImpl.setHighestPriority(packageName, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
updateTargetPackages(mImpl.setHighestPriority(overlay, realUserId));
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
@@ -810,15 +781,17 @@ public final class OverlayManagerService extends SystemService {
|
||||
|
||||
try {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#setLowestPriority " + packageName);
|
||||
|
||||
final OverlayIdentifier overlay = new OverlayIdentifier(packageName);
|
||||
final int realUserId = handleIncomingUser(userIdArg, "setLowestPriority");
|
||||
enforceActor(packageName, "setLowestPriority", realUserId);
|
||||
enforceActor(overlay, "setLowestPriority", realUserId);
|
||||
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
mImpl.setLowestPriority(packageName, realUserId)
|
||||
.ifPresent(mPropagateOverlayChange);
|
||||
mImpl.setLowestPriority(overlay, realUserId)
|
||||
.ifPresent(OverlayManagerService.this::updateTargetPackages);
|
||||
return true;
|
||||
} catch (OperationFailedException e) {
|
||||
return false;
|
||||
@@ -858,12 +831,17 @@ public final class OverlayManagerService extends SystemService {
|
||||
return;
|
||||
}
|
||||
|
||||
final OverlayIdentifier overlay = new OverlayIdentifier(packageName);
|
||||
final int realUserId = handleIncomingUser(userIdArg, "invalidateCachesForOverlay");
|
||||
enforceActor(packageName, "invalidateCachesForOverlay", realUserId);
|
||||
enforceActor(overlay, "invalidateCachesForOverlay", realUserId);
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
mImpl.removeIdmapForOverlay(packageName, realUserId);
|
||||
try {
|
||||
mImpl.removeIdmapForOverlay(overlay, realUserId);
|
||||
} catch (OperationFailedException e) {
|
||||
Slog.w(TAG, "invalidate caches for overlay '" + overlay + "' failed", e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
@@ -893,25 +871,63 @@ public final class OverlayManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<PackageAndUser> executeRequest(
|
||||
@NonNull final OverlayManagerTransaction.Request request) throws Exception {
|
||||
final int realUserId = handleIncomingUser(request.userId, request.typeToString());
|
||||
enforceActor(request.packageName, request.typeToString(), realUserId);
|
||||
private Set<PackageAndUser> executeRequest(
|
||||
@NonNull final OverlayManagerTransaction.Request request)
|
||||
throws OperationFailedException {
|
||||
Objects.requireNonNull(request, "Transaction contains a null request");
|
||||
Objects.requireNonNull(request.overlay,
|
||||
"Transaction overlay identifier must be non-null");
|
||||
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int realUserId;
|
||||
if (request.type == TYPE_REGISTER_FABRICATED
|
||||
|| request.type == TYPE_UNREGISTER_FABRICATED) {
|
||||
if (request.userId != UserHandle.USER_ALL) {
|
||||
throw new IllegalArgumentException(request.typeToString()
|
||||
+ " unsupported for user " + request.userId);
|
||||
}
|
||||
realUserId = UserHandle.USER_ALL;
|
||||
|
||||
// Enforce that the calling process can only register and unregister fabricated
|
||||
// overlays using its package name.
|
||||
final String pkgName = request.overlay.getPackageName();
|
||||
if (callingUid != Process.ROOT_UID && !ArrayUtils.contains(
|
||||
mPackageManager.getPackagesForUid(callingUid), pkgName)) {
|
||||
throw new IllegalArgumentException("UID " + callingUid + " does own package"
|
||||
+ "name " + pkgName);
|
||||
}
|
||||
} else {
|
||||
// Enforce actor requirements for enabling, disabling, and reordering overlays.
|
||||
realUserId = handleIncomingUser(request.userId, request.typeToString());
|
||||
enforceActor(request.overlay, request.typeToString(), realUserId);
|
||||
}
|
||||
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
switch (request.type) {
|
||||
case TYPE_SET_ENABLED:
|
||||
Optional<PackageAndUser> opt1 =
|
||||
mImpl.setEnabled(request.packageName, true, request.userId);
|
||||
Optional<PackageAndUser> opt2 =
|
||||
mImpl.setHighestPriority(request.packageName, request.userId);
|
||||
// Both setEnabled and setHighestPriority affected the same
|
||||
// target package and user: if both return non-empty
|
||||
// Optionals, they are identical
|
||||
return opt1.isPresent() ? opt1 : opt2;
|
||||
Set<PackageAndUser> result = null;
|
||||
result = CollectionUtils.addAll(result,
|
||||
mImpl.setEnabled(request.overlay, true, realUserId));
|
||||
result = CollectionUtils.addAll(result,
|
||||
mImpl.setHighestPriority(request.overlay, realUserId));
|
||||
return CollectionUtils.emptyIfNull(result);
|
||||
|
||||
case TYPE_SET_DISABLED:
|
||||
return mImpl.setEnabled(request.packageName, false, request.userId);
|
||||
return mImpl.setEnabled(request.overlay, false, realUserId);
|
||||
|
||||
case TYPE_REGISTER_FABRICATED:
|
||||
final FabricatedOverlayInternal fabricated =
|
||||
request.extras.getParcelable(
|
||||
OverlayManagerTransaction.Request.BUNDLE_FABRICATED_OVERLAY
|
||||
);
|
||||
Objects.requireNonNull(fabricated,
|
||||
"no fabricated overlay attached to request");
|
||||
return mImpl.registerFabricatedOverlay(fabricated);
|
||||
|
||||
case TYPE_UNREGISTER_FABRICATED:
|
||||
return mImpl.unregisterFabricatedOverlay(request.overlay);
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("unsupported request: " + request);
|
||||
}
|
||||
@@ -921,7 +937,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
}
|
||||
|
||||
private void executeAllRequests(@NonNull final OverlayManagerTransaction transaction)
|
||||
throws Exception {
|
||||
throws OperationFailedException {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "commit " + transaction);
|
||||
}
|
||||
@@ -931,25 +947,25 @@ public final class OverlayManagerService extends SystemService {
|
||||
|
||||
// map: userId -> set<package-name>: target packages of overlays in
|
||||
// this transaction
|
||||
SparseArray<Set<String>> transactionTargets = new SparseArray<>();
|
||||
final SparseArray<Set<String>> transactionTargets = new SparseArray<>();
|
||||
|
||||
// map: userId -> set<package-name>: packages that need to reload
|
||||
// their resources due to changes to the overlays in this
|
||||
// transaction
|
||||
SparseArray<List<String>> affectedPackagesToUpdate = new SparseArray<>();
|
||||
final SparseArray<List<String>> affectedPackagesToUpdate = new SparseArray<>();
|
||||
|
||||
synchronized (mLock) {
|
||||
|
||||
// execute the requests (as calling user)
|
||||
for (final OverlayManagerTransaction.Request request : transaction) {
|
||||
executeRequest(request).ifPresent(target -> {
|
||||
Set<String> userTargets = transactionTargets.get(target.userId);
|
||||
if (userTargets == null) {
|
||||
userTargets = new ArraySet<String>();
|
||||
transactionTargets.put(target.userId, userTargets);
|
||||
}
|
||||
userTargets.add(target.packageName);
|
||||
});
|
||||
executeRequest(request).forEach(
|
||||
target -> {
|
||||
Set<String> userTargets = transactionTargets.get(target.userId);
|
||||
if (userTargets == null) {
|
||||
userTargets = new ArraySet<>();
|
||||
transactionTargets.put(target.userId, userTargets);
|
||||
}
|
||||
userTargets.add(target.packageName);
|
||||
});
|
||||
}
|
||||
|
||||
// past the point of no return: the entire transaction has been
|
||||
@@ -975,10 +991,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
// schedule apps to refresh
|
||||
for (int index = 0; index < affectedPackagesToUpdate.size(); index++) {
|
||||
final int userId = affectedPackagesToUpdate.keyAt(index);
|
||||
updateActivityManager(affectedPackagesToUpdate.valueAt(index), userId);
|
||||
}
|
||||
updateActivityManager(affectedPackagesToUpdate);
|
||||
|
||||
// broadcast the ACTION_OVERLAY_CHANGED intents
|
||||
for (int index = 0; index < transactionTargets.size(); index++) {
|
||||
@@ -1059,12 +1072,12 @@ public final class OverlayManagerService extends SystemService {
|
||||
dumpState.setField(arg);
|
||||
break;
|
||||
default:
|
||||
dumpState.setPackageName(arg);
|
||||
dumpState.setOverlyIdentifier(arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dumpState.getPackageName() == null && opti < args.length) {
|
||||
dumpState.setPackageName(args[opti]);
|
||||
dumpState.setOverlyIdentifier(args[opti]);
|
||||
opti++;
|
||||
}
|
||||
|
||||
@@ -1101,12 +1114,12 @@ public final class OverlayManagerService extends SystemService {
|
||||
getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, message);
|
||||
}
|
||||
|
||||
private void enforceActor(String packageName, String methodName, int realUserId)
|
||||
throws SecurityException {
|
||||
OverlayInfo overlayInfo = mImpl.getOverlayInfo(packageName, realUserId);
|
||||
private void enforceActor(@NonNull OverlayIdentifier overlay, @NonNull String methodName,
|
||||
int realUserId) throws SecurityException {
|
||||
OverlayInfo overlayInfo = mImpl.getOverlayInfo(overlay, realUserId);
|
||||
if (overlayInfo == null) {
|
||||
throw new IllegalArgumentException("Unable to retrieve overlay information for "
|
||||
+ packageName);
|
||||
+ overlay);
|
||||
}
|
||||
|
||||
int callingUid = Binder.getCallingUid();
|
||||
@@ -1115,7 +1128,13 @@ public final class OverlayManagerService extends SystemService {
|
||||
};
|
||||
|
||||
private static final class PackageManagerHelperImpl implements PackageManagerHelper {
|
||||
|
||||
private static class AndroidPackageUsers {
|
||||
private AndroidPackage mPackage;
|
||||
private final Set<Integer> mInstalledUsers = new ArraySet<>();
|
||||
private AndroidPackageUsers(@NonNull AndroidPackage pkg) {
|
||||
this.mPackage = pkg;
|
||||
}
|
||||
}
|
||||
private final Context mContext;
|
||||
private final IPackageManager mPackageManager;
|
||||
private final PackageManagerInternal mPackageManagerInternal;
|
||||
@@ -1125,7 +1144,8 @@ public final class OverlayManagerService extends SystemService {
|
||||
// intent, querying the PackageManagerService for the actual current
|
||||
// state may lead to contradictions within OMS. Better then to lag
|
||||
// behind until all pending intents have been processed.
|
||||
private final SparseArray<HashMap<String, PackageInfo>> mCache = new SparseArray<>();
|
||||
private final ArrayMap<String, AndroidPackageUsers> mCache = new ArrayMap<>();
|
||||
private final Set<Integer> mInitializedUsers = new ArraySet<>();
|
||||
|
||||
PackageManagerHelperImpl(Context context) {
|
||||
mContext = context;
|
||||
@@ -1133,29 +1153,112 @@ public final class OverlayManagerService extends SystemService {
|
||||
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
|
||||
}
|
||||
|
||||
public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId,
|
||||
final boolean useCache) {
|
||||
if (useCache) {
|
||||
final PackageInfo cachedPi = getCachedPackageInfo(packageName, userId);
|
||||
if (cachedPi != null) {
|
||||
return cachedPi;
|
||||
/**
|
||||
* Initializes the helper for the user. This only needs to be invoked one time before
|
||||
* packages of this user are queried.
|
||||
* @param userId the user id to initialize
|
||||
* @return a map of package name to all packages installed in the user
|
||||
*/
|
||||
@NonNull
|
||||
public ArrayMap<String, AndroidPackage> initializeForUser(final int userId) {
|
||||
if (!mInitializedUsers.contains(userId)) {
|
||||
mInitializedUsers.add(userId);
|
||||
mPackageManagerInternal.forEachInstalledPackage(
|
||||
(pkg) -> addPackageUser(pkg, userId), userId);
|
||||
}
|
||||
|
||||
final ArrayMap<String, AndroidPackage> userPackages = new ArrayMap<>();
|
||||
for (int i = 0, n = mCache.size(); i < n; i++) {
|
||||
final AndroidPackageUsers pkg = mCache.valueAt(i);
|
||||
if (pkg.mInstalledUsers.contains(userId)) {
|
||||
userPackages.put(mCache.keyAt(i), pkg.mPackage);
|
||||
}
|
||||
}
|
||||
try {
|
||||
final PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0, userId);
|
||||
if (useCache && pi != null) {
|
||||
cachePackageInfo(packageName, userId, pi);
|
||||
}
|
||||
return pi;
|
||||
} catch (RemoteException e) {
|
||||
// Intentionally left empty.
|
||||
}
|
||||
return null;
|
||||
return userPackages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId) {
|
||||
return getPackageInfo(packageName, userId, true);
|
||||
@Nullable
|
||||
public AndroidPackage getPackageForUser(@NonNull final String packageName,
|
||||
final int userId) {
|
||||
final AndroidPackageUsers pkg = mCache.get(packageName);
|
||||
if (pkg != null && pkg.mInstalledUsers.contains(userId)) {
|
||||
return pkg.mPackage;
|
||||
}
|
||||
try {
|
||||
if (!mPackageManager.isPackageAvailable(packageName, userId)) {
|
||||
return null;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Failed to check availability of package '" + packageName
|
||||
+ "' for user " + userId, e);
|
||||
return null;
|
||||
}
|
||||
return addPackageUser(packageName, userId);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private AndroidPackage addPackageUser(@NonNull final String packageName,
|
||||
final int user) {
|
||||
final AndroidPackage pkg = mPackageManagerInternal.getPackage(packageName);
|
||||
if (pkg == null) {
|
||||
Slog.w(TAG, "Android package for '" + packageName + "' could not be found;"
|
||||
+ " continuing as if package was never added", new Throwable());
|
||||
return null;
|
||||
}
|
||||
return addPackageUser(pkg, user);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private AndroidPackage addPackageUser(@NonNull final AndroidPackage pkg,
|
||||
final int user) {
|
||||
AndroidPackageUsers pkgUsers = mCache.get(pkg.getPackageName());
|
||||
if (pkgUsers == null) {
|
||||
pkgUsers = new AndroidPackageUsers(pkg);
|
||||
mCache.put(pkg.getPackageName(), pkgUsers);
|
||||
} else {
|
||||
pkgUsers.mPackage = pkg;
|
||||
}
|
||||
pkgUsers.mInstalledUsers.add(user);
|
||||
return pkgUsers.mPackage;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
private void removePackageUser(@NonNull final String packageName, final int user) {
|
||||
final AndroidPackageUsers pkgUsers = mCache.get(packageName);
|
||||
if (pkgUsers == null) {
|
||||
return;
|
||||
}
|
||||
removePackageUser(pkgUsers, user);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private void removePackageUser(@NonNull final AndroidPackageUsers pkg, final int user) {
|
||||
pkg.mInstalledUsers.remove(user);
|
||||
if (pkg.mInstalledUsers.isEmpty()) {
|
||||
mCache.remove(pkg.mPackage.getPackageName());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AndroidPackage onPackageAdded(@NonNull final String packageName, final int userId) {
|
||||
return addPackageUser(packageName, userId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AndroidPackage onPackageUpdated(@NonNull final String packageName,
|
||||
final int userId) {
|
||||
return addPackageUser(packageName, userId);
|
||||
}
|
||||
|
||||
public void onPackageRemoved(@NonNull final String packageName, final int userId) {
|
||||
removePackageUser(packageName, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstantApp(@NonNull final String packageName, final int userId) {
|
||||
return mPackageManagerInternal.isInstantApp(packageName, userId);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -1178,15 +1281,6 @@ public final class OverlayManagerService extends SystemService {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PackageInfo> getOverlayPackages(final int userId) {
|
||||
final List<PackageInfo> overlays = mPackageManagerInternal.getOverlayPackages(userId);
|
||||
for (final PackageInfo info : overlays) {
|
||||
cachePackageInfo(info.packageName, userId, info);
|
||||
}
|
||||
return overlays;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfigSignaturePackage() {
|
||||
final String[] pkgs = mPackageManagerInternal.getKnownPackageNames(
|
||||
@@ -1200,16 +1294,14 @@ public final class OverlayManagerService extends SystemService {
|
||||
public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
|
||||
@NonNull String targetOverlayableName, int userId)
|
||||
throws IOException {
|
||||
PackageInfo packageInfo = getPackageInfo(packageName, userId);
|
||||
final AndroidPackage packageInfo = getPackageForUser(packageName, userId);
|
||||
if (packageInfo == null) {
|
||||
throw new IOException("Unable to get target package");
|
||||
}
|
||||
|
||||
String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
|
||||
|
||||
ApkAssets apkAssets = null;
|
||||
try {
|
||||
apkAssets = ApkAssets.loadFromPath(baseCodePath);
|
||||
apkAssets = ApkAssets.loadFromPath(packageInfo.getBaseApkPath());
|
||||
return apkAssets.getOverlayableInfo(targetOverlayableName);
|
||||
} finally {
|
||||
if (apkAssets != null) {
|
||||
@@ -1224,16 +1316,14 @@ public final class OverlayManagerService extends SystemService {
|
||||
@Override
|
||||
public boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
|
||||
throws IOException {
|
||||
PackageInfo packageInfo = getPackageInfo(targetPackageName, userId);
|
||||
AndroidPackage packageInfo = getPackageForUser(targetPackageName, userId);
|
||||
if (packageInfo == null) {
|
||||
throw new IOException("Unable to get target package");
|
||||
}
|
||||
|
||||
String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
|
||||
|
||||
ApkAssets apkAssets = null;
|
||||
try {
|
||||
apkAssets = ApkAssets.loadFromPath(baseCodePath);
|
||||
apkAssets = ApkAssets.loadFromPath(packageInfo.getBaseApkPath());
|
||||
return apkAssets.definesOverlayable();
|
||||
} finally {
|
||||
if (apkAssets != null) {
|
||||
@@ -1250,35 +1340,10 @@ public final class OverlayManagerService extends SystemService {
|
||||
mContext.enforceCallingOrSelfPermission(permission, message);
|
||||
}
|
||||
|
||||
public PackageInfo getCachedPackageInfo(@NonNull final String packageName,
|
||||
final int userId) {
|
||||
final HashMap<String, PackageInfo> map = mCache.get(userId);
|
||||
return map == null ? null : map.get(packageName);
|
||||
}
|
||||
|
||||
public void cachePackageInfo(@NonNull final String packageName, final int userId,
|
||||
@NonNull final PackageInfo pi) {
|
||||
HashMap<String, PackageInfo> map = mCache.get(userId);
|
||||
if (map == null) {
|
||||
map = new HashMap<>();
|
||||
mCache.put(userId, map);
|
||||
}
|
||||
map.put(packageName, pi);
|
||||
}
|
||||
|
||||
public void forgetPackageInfo(@NonNull final String packageName, final int userId) {
|
||||
final HashMap<String, PackageInfo> map = mCache.get(userId);
|
||||
if (map == null) {
|
||||
return;
|
||||
}
|
||||
map.remove(packageName);
|
||||
if (map.isEmpty()) {
|
||||
mCache.delete(userId);
|
||||
}
|
||||
}
|
||||
|
||||
public void forgetAllPackageInfos(final int userId) {
|
||||
mCache.delete(userId);
|
||||
for (int i = 0, n = mCache.size(); i < n; i++) {
|
||||
removePackageUser(mCache.valueAt(i), userId);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -1292,19 +1357,12 @@ public final class OverlayManagerService extends SystemService {
|
||||
}
|
||||
|
||||
private static final String TAB1 = " ";
|
||||
private static final String TAB2 = TAB1 + TAB1;
|
||||
|
||||
public void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
|
||||
pw.println("PackageInfo cache");
|
||||
pw.println("AndroidPackage cache");
|
||||
|
||||
if (!dumpState.isVerbose()) {
|
||||
int count = 0;
|
||||
final int n = mCache.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
final int userId = mCache.keyAt(i);
|
||||
count += mCache.get(userId).size();
|
||||
}
|
||||
pw.println(TAB1 + count + " package(s)");
|
||||
pw.println(TAB1 + mCache.size() + " package(s)");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1313,25 +1371,70 @@ public final class OverlayManagerService extends SystemService {
|
||||
return;
|
||||
}
|
||||
|
||||
final int n = mCache.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
final int userId = mCache.keyAt(i);
|
||||
pw.println(TAB1 + "User " + userId);
|
||||
final HashMap<String, PackageInfo> map = mCache.get(userId);
|
||||
for (Map.Entry<String, PackageInfo> entry : map.entrySet()) {
|
||||
pw.println(TAB2 + entry.getKey() + ": " + entry.getValue());
|
||||
}
|
||||
for (int i = 0, n = mCache.size(); i < n; i++) {
|
||||
final String packageName = mCache.keyAt(i);
|
||||
final AndroidPackageUsers pkg = mCache.valueAt(i);
|
||||
pw.print(TAB1 + packageName + ": " + pkg.mPackage + " users=");
|
||||
pw.println(TextUtils.join(", ", pkg.mInstalledUsers));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTargetPackages(@Nullable PackageAndUser updatedTarget) {
|
||||
if (updatedTarget != null) {
|
||||
updateTargetPackages(Set.of(updatedTarget));
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTargetPackages(@Nullable Set<PackageAndUser> updatedTargets) {
|
||||
if (CollectionUtils.isEmpty(updatedTargets)) {
|
||||
return;
|
||||
}
|
||||
persistSettings();
|
||||
final SparseArray<ArraySet<String>> userTargets = groupTargetsByUserId(updatedTargets);
|
||||
FgThread.getHandler().post(() -> {
|
||||
for (int i = 0, n = userTargets.size(); i < n; i++) {
|
||||
final ArraySet<String> targets = userTargets.valueAt(i);
|
||||
final int userId = userTargets.keyAt(i);
|
||||
|
||||
// Update the overlay paths in package manager.
|
||||
final List<String> affectedPackages = updatePackageManager(targets, userId);
|
||||
updateActivityManager(affectedPackages, userId);
|
||||
|
||||
// Overlays targeting shared libraries may cause more packages to need to be
|
||||
// refreshed.
|
||||
broadcastActionOverlayChanged(targets, userId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static SparseArray<ArraySet<String>> groupTargetsByUserId(
|
||||
@Nullable final Set<PackageAndUser> targetsAndUsers) {
|
||||
final SparseArray<ArraySet<String>> userTargets = new SparseArray<>();
|
||||
CollectionUtils.forEach(targetsAndUsers, target -> {
|
||||
ArraySet<String> targets = userTargets.get(target.userId);
|
||||
if (targets == null) {
|
||||
targets = new ArraySet<>();
|
||||
userTargets.put(target.userId, targets);
|
||||
}
|
||||
targets.add(target.packageName);
|
||||
});
|
||||
return userTargets;
|
||||
}
|
||||
|
||||
// Helper methods to update other parts of the system or read/write
|
||||
// settings: these methods should never call into each other!
|
||||
|
||||
private void broadcastActionOverlayChanged(@NonNull final String targetPackageName,
|
||||
private static void broadcastActionOverlayChanged(@NonNull final Set<String> targetPackages,
|
||||
final int userId) {
|
||||
CollectionUtils.forEach(targetPackages,
|
||||
target -> broadcastActionOverlayChanged(target, userId));
|
||||
}
|
||||
|
||||
private static void broadcastActionOverlayChanged(String targetPackage, final int userId) {
|
||||
final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
|
||||
Uri.fromParts("package", targetPackageName, null));
|
||||
Uri.fromParts("package", targetPackage, null));
|
||||
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
|
||||
try {
|
||||
ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, null, null,
|
||||
@@ -1345,7 +1448,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
* Tell the activity manager to tell a set of packages to reload their
|
||||
* resources.
|
||||
*/
|
||||
private void updateActivityManager(List<String> targetPackageNames, final int userId) {
|
||||
private void updateActivityManager(@NonNull List<String> targetPackageNames, final int userId) {
|
||||
final IActivityManager am = ActivityManager.getService();
|
||||
try {
|
||||
am.scheduleApplicationInfoChanged(targetPackageNames, userId);
|
||||
@@ -1354,16 +1457,33 @@ public final class OverlayManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<String> updatePackageManager(String targetPackageNames, final int userId) {
|
||||
return updatePackageManager(Collections.singletonList(targetPackageNames), userId);
|
||||
private void updateActivityManager(@NonNull SparseArray<List<String>> targetPackageNames) {
|
||||
for (int i = 0, n = targetPackageNames.size(); i < n; i++) {
|
||||
updateActivityManager(targetPackageNames.valueAt(i), targetPackageNames.keyAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private SparseArray<List<String>> updatePackageManager(@Nullable Set<PackageAndUser> targets) {
|
||||
if (CollectionUtils.isEmpty(targets)) {
|
||||
return new SparseArray<>();
|
||||
}
|
||||
final SparseArray<List<String>> affectedTargets = new SparseArray<>();
|
||||
final SparseArray<ArraySet<String>> userTargets = groupTargetsByUserId(targets);
|
||||
for (int i = 0, n = userTargets.size(); i < n; i++) {
|
||||
final int userId = userTargets.keyAt(i);
|
||||
affectedTargets.put(userId, updatePackageManager(userTargets.valueAt(i), userId));
|
||||
}
|
||||
return affectedTargets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the target packages' set of enabled overlays in PackageManager.
|
||||
* @return the package names of affected targets (a superset of
|
||||
* targetPackageNames: the target themserlves and shared libraries)
|
||||
* targetPackageNames: the target themselves and shared libraries)
|
||||
*/
|
||||
private ArrayList<String> updatePackageManager(@NonNull Collection<String> targetPackageNames,
|
||||
@NonNull
|
||||
private List<String> updatePackageManager(@NonNull Collection<String> targetPackageNames,
|
||||
final int userId) {
|
||||
try {
|
||||
traceBegin(TRACE_TAG_RRO, "OMS#updatePackageManager " + targetPackageNames);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,31 +21,32 @@ import static com.android.server.om.OverlayManagerService.TAG;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.om.OverlayIdentifier;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
import android.util.TypedXmlPullParser;
|
||||
import android.util.TypedXmlSerializer;
|
||||
import android.util.Xml;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.FastXmlSerializer;
|
||||
import com.android.internal.util.CollectionUtils;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
@@ -68,34 +69,45 @@ final class OverlayManagerSettings {
|
||||
*/
|
||||
private final ArrayList<SettingsItem> mItems = new ArrayList<>();
|
||||
|
||||
void init(@NonNull final String packageName, final int userId,
|
||||
@NonNull
|
||||
OverlayInfo init(@NonNull final OverlayIdentifier overlay, final int userId,
|
||||
@NonNull final String targetPackageName, @Nullable final String targetOverlayableName,
|
||||
@NonNull final String baseCodePath, boolean isMutable, boolean isEnabled, int priority,
|
||||
@Nullable String overlayCategory) {
|
||||
remove(packageName, userId);
|
||||
insert(new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName,
|
||||
baseCodePath, OverlayInfo.STATE_UNKNOWN, isEnabled, isMutable, priority,
|
||||
overlayCategory));
|
||||
@Nullable String overlayCategory, boolean isFabricated) {
|
||||
remove(overlay, userId);
|
||||
final SettingsItem item = new SettingsItem(overlay, userId, targetPackageName,
|
||||
targetOverlayableName, baseCodePath, OverlayInfo.STATE_UNKNOWN, isEnabled,
|
||||
isMutable, priority, overlayCategory, isFabricated);
|
||||
insert(item);
|
||||
return item.getOverlayInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the settings were modified, false if they remain the same.
|
||||
*/
|
||||
boolean remove(@NonNull final String packageName, final int userId) {
|
||||
final int idx = select(packageName, userId);
|
||||
boolean remove(@NonNull final OverlayIdentifier overlay, final int userId) {
|
||||
final int idx = select(overlay, userId);
|
||||
if (idx < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mItems.remove(idx);
|
||||
return true;
|
||||
}
|
||||
|
||||
@NonNull OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId)
|
||||
@NonNull OverlayInfo getOverlayInfo(@NonNull final OverlayIdentifier overlay, final int userId)
|
||||
throws BadKeyException {
|
||||
final int idx = select(packageName, userId);
|
||||
final int idx = select(overlay, userId);
|
||||
if (idx < 0) {
|
||||
throw new BadKeyException(packageName, userId);
|
||||
throw new BadKeyException(overlay, userId);
|
||||
}
|
||||
return mItems.get(idx).getOverlayInfo();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
OverlayInfo getNullableOverlayInfo(@NonNull final OverlayIdentifier overlay, final int userId) {
|
||||
final int idx = select(overlay, userId);
|
||||
if (idx < 0) {
|
||||
return null;
|
||||
}
|
||||
return mItems.get(idx).getOverlayInfo();
|
||||
}
|
||||
@@ -103,28 +115,29 @@ final class OverlayManagerSettings {
|
||||
/**
|
||||
* Returns true if the settings were modified, false if they remain the same.
|
||||
*/
|
||||
boolean setBaseCodePath(@NonNull final String packageName, final int userId,
|
||||
boolean setBaseCodePath(@NonNull final OverlayIdentifier overlay, final int userId,
|
||||
@NonNull final String path) throws BadKeyException {
|
||||
final int idx = select(packageName, userId);
|
||||
final int idx = select(overlay, userId);
|
||||
if (idx < 0) {
|
||||
throw new BadKeyException(packageName, userId);
|
||||
throw new BadKeyException(overlay, userId);
|
||||
}
|
||||
return mItems.get(idx).setBaseCodePath(path);
|
||||
}
|
||||
|
||||
boolean setCategory(@NonNull final String packageName, final int userId,
|
||||
boolean setCategory(@NonNull final OverlayIdentifier overlay, final int userId,
|
||||
@Nullable String category) throws BadKeyException {
|
||||
final int idx = select(packageName, userId);
|
||||
final int idx = select(overlay, userId);
|
||||
if (idx < 0) {
|
||||
throw new BadKeyException(packageName, userId);
|
||||
throw new BadKeyException(overlay, userId);
|
||||
}
|
||||
return mItems.get(idx).setCategory(category);
|
||||
}
|
||||
|
||||
boolean getEnabled(@NonNull final String packageName, final int userId) throws BadKeyException {
|
||||
final int idx = select(packageName, userId);
|
||||
boolean getEnabled(@NonNull final OverlayIdentifier overlay, final int userId)
|
||||
throws BadKeyException {
|
||||
final int idx = select(overlay, userId);
|
||||
if (idx < 0) {
|
||||
throw new BadKeyException(packageName, userId);
|
||||
throw new BadKeyException(overlay, userId);
|
||||
}
|
||||
return mItems.get(idx).isEnabled();
|
||||
}
|
||||
@@ -132,20 +145,20 @@ final class OverlayManagerSettings {
|
||||
/**
|
||||
* Returns true if the settings were modified, false if they remain the same.
|
||||
*/
|
||||
boolean setEnabled(@NonNull final String packageName, final int userId, final boolean enable)
|
||||
throws BadKeyException {
|
||||
final int idx = select(packageName, userId);
|
||||
boolean setEnabled(@NonNull final OverlayIdentifier overlay, final int userId,
|
||||
final boolean enable) throws BadKeyException {
|
||||
final int idx = select(overlay, userId);
|
||||
if (idx < 0) {
|
||||
throw new BadKeyException(packageName, userId);
|
||||
throw new BadKeyException(overlay, userId);
|
||||
}
|
||||
return mItems.get(idx).setEnabled(enable);
|
||||
}
|
||||
|
||||
@OverlayInfo.State int getState(@NonNull final String packageName, final int userId)
|
||||
@OverlayInfo.State int getState(@NonNull final OverlayIdentifier overlay, final int userId)
|
||||
throws BadKeyException {
|
||||
final int idx = select(packageName, userId);
|
||||
final int idx = select(overlay, userId);
|
||||
if (idx < 0) {
|
||||
throw new BadKeyException(packageName, userId);
|
||||
throw new BadKeyException(overlay, userId);
|
||||
}
|
||||
return mItems.get(idx).getState();
|
||||
}
|
||||
@@ -153,11 +166,11 @@ final class OverlayManagerSettings {
|
||||
/**
|
||||
* Returns true if the settings were modified, false if they remain the same.
|
||||
*/
|
||||
boolean setState(@NonNull final String packageName, final int userId,
|
||||
boolean setState(@NonNull final OverlayIdentifier overlay, final int userId,
|
||||
final @OverlayInfo.State int state) throws BadKeyException {
|
||||
final int idx = select(packageName, userId);
|
||||
final int idx = select(overlay, userId);
|
||||
if (idx < 0) {
|
||||
throw new BadKeyException(packageName, userId);
|
||||
throw new BadKeyException(overlay, userId);
|
||||
}
|
||||
return mItems.get(idx).setState(state);
|
||||
}
|
||||
@@ -166,53 +179,82 @@ final class OverlayManagerSettings {
|
||||
final int userId) {
|
||||
// Immutable RROs targeting "android" are loaded from AssetManager, and so they should be
|
||||
// ignored in OverlayManagerService.
|
||||
return selectWhereTarget(targetPackageName, userId)
|
||||
.filter((i) -> i.isMutable() || !"android".equals(i.getTargetPackageName()))
|
||||
.map(SettingsItem::getOverlayInfo)
|
||||
.collect(Collectors.toList());
|
||||
final List<SettingsItem> items = selectWhereTarget(targetPackageName, userId);
|
||||
items.removeIf(OverlayManagerSettings::isImmutableFrameworkOverlay);
|
||||
return CollectionUtils.map(items, SettingsItem::getOverlayInfo);
|
||||
}
|
||||
|
||||
ArrayMap<String, List<OverlayInfo>> getOverlaysForUser(final int userId) {
|
||||
// Immutable RROs targeting "android" are loaded from AssetManager, and so they should be
|
||||
// ignored in OverlayManagerService.
|
||||
return selectWhereUser(userId)
|
||||
.filter((i) -> i.isMutable() || !"android".equals(i.getTargetPackageName()))
|
||||
.map(SettingsItem::getOverlayInfo)
|
||||
.collect(Collectors.groupingBy(info -> info.targetPackageName, ArrayMap::new,
|
||||
Collectors.toList()));
|
||||
final List<SettingsItem> items = selectWhereUser(userId);
|
||||
items.removeIf(OverlayManagerSettings::isImmutableFrameworkOverlay);
|
||||
|
||||
final ArrayMap<String, List<OverlayInfo>> targetInfos = new ArrayMap<>();
|
||||
for (int i = 0, n = items.size(); i < n; i++) {
|
||||
final SettingsItem item = items.get(i);
|
||||
targetInfos.computeIfAbsent(item.mTargetPackageName, (String) -> new ArrayList<>())
|
||||
.add(item.getOverlayInfo());
|
||||
}
|
||||
return targetInfos;
|
||||
}
|
||||
|
||||
Set<String> getAllBaseCodePaths() {
|
||||
final Set<String> paths = new ArraySet<>();
|
||||
mItems.forEach(item -> paths.add(item.mBaseCodePath));
|
||||
return paths;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
List<OverlayInfo> removeIf(@NonNull final Predicate<OverlayInfo> predicate, final int userId) {
|
||||
return removeIf(info -> (predicate.test(info) && info.userId == userId));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
List<OverlayInfo> removeIf(final @NonNull Predicate<OverlayInfo> predicate) {
|
||||
List<OverlayInfo> removed = null;
|
||||
for (int i = mItems.size() - 1; i >= 0; i--) {
|
||||
final OverlayInfo info = mItems.get(i).getOverlayInfo();
|
||||
if (predicate.test(info)) {
|
||||
mItems.remove(i);
|
||||
removed = CollectionUtils.add(removed, info);
|
||||
}
|
||||
}
|
||||
return CollectionUtils.emptyIfNull(removed);
|
||||
}
|
||||
|
||||
int[] getUsers() {
|
||||
return mItems.stream().mapToInt(SettingsItem::getUserId).distinct().toArray();
|
||||
}
|
||||
|
||||
private static boolean isImmutableFrameworkOverlay(@NonNull SettingsItem item) {
|
||||
return !item.isMutable() && "android".equals(item.getTargetPackageName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the settings were modified, false if they remain the same.
|
||||
*/
|
||||
boolean removeUser(final int userId) {
|
||||
boolean removed = false;
|
||||
for (int i = 0; i < mItems.size(); i++) {
|
||||
final SettingsItem item = mItems.get(i);
|
||||
return mItems.removeIf(item -> {
|
||||
if (item.getUserId() == userId) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Removing overlay " + item.mPackageName + " for user " + userId
|
||||
Slog.d(TAG, "Removing overlay " + item.mOverlay + " for user " + userId
|
||||
+ " from settings because user was removed");
|
||||
}
|
||||
mItems.remove(i);
|
||||
removed = true;
|
||||
i--;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reassigns the priority of an overlay maintaining the values of the overlays other settings.
|
||||
*/
|
||||
void setPriority(@NonNull final String packageName, final int userId, final int priority) {
|
||||
final int moveIdx = select(packageName, userId);
|
||||
void setPriority(@NonNull final OverlayIdentifier overlay, final int userId,
|
||||
final int priority) throws BadKeyException {
|
||||
final int moveIdx = select(overlay, userId);
|
||||
if (moveIdx < 0) {
|
||||
throw new BadKeyException(packageName, userId);
|
||||
throw new BadKeyException(overlay, userId);
|
||||
}
|
||||
|
||||
final SettingsItem itemToMove = mItems.get(moveIdx);
|
||||
@@ -224,17 +266,17 @@ final class OverlayManagerSettings {
|
||||
/**
|
||||
* Returns true if the settings were modified, false if they remain the same.
|
||||
*/
|
||||
boolean setPriority(@NonNull final String packageName,
|
||||
@NonNull final String newParentPackageName, final int userId) {
|
||||
if (packageName.equals(newParentPackageName)) {
|
||||
boolean setPriority(@NonNull final OverlayIdentifier overlay,
|
||||
@NonNull final OverlayIdentifier newOverlay, final int userId) {
|
||||
if (overlay.equals(newOverlay)) {
|
||||
return false;
|
||||
}
|
||||
final int moveIdx = select(packageName, userId);
|
||||
final int moveIdx = select(overlay, userId);
|
||||
if (moveIdx < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int parentIdx = select(newParentPackageName, userId);
|
||||
final int parentIdx = select(newOverlay, userId);
|
||||
if (parentIdx < 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -248,7 +290,7 @@ final class OverlayManagerSettings {
|
||||
}
|
||||
|
||||
mItems.remove(moveIdx);
|
||||
final int newParentIdx = select(newParentPackageName, userId) + 1;
|
||||
final int newParentIdx = select(newOverlay, userId) + 1;
|
||||
mItems.add(newParentIdx, itemToMove);
|
||||
return moveIdx != newParentIdx;
|
||||
}
|
||||
@@ -256,8 +298,8 @@ final class OverlayManagerSettings {
|
||||
/**
|
||||
* Returns true if the settings were modified, false if they remain the same.
|
||||
*/
|
||||
boolean setLowestPriority(@NonNull final String packageName, final int userId) {
|
||||
final int idx = select(packageName, userId);
|
||||
boolean setLowestPriority(@NonNull final OverlayIdentifier overlay, final int userId) {
|
||||
final int idx = select(overlay, userId);
|
||||
if (idx <= 0) {
|
||||
// If the item doesn't exist or is already the lowest, don't change anything.
|
||||
return false;
|
||||
@@ -272,8 +314,8 @@ final class OverlayManagerSettings {
|
||||
/**
|
||||
* Returns true if the settings were modified, false if they remain the same.
|
||||
*/
|
||||
boolean setHighestPriority(@NonNull final String packageName, final int userId) {
|
||||
final int idx = select(packageName, userId);
|
||||
boolean setHighestPriority(@NonNull final OverlayIdentifier overlay, final int userId) {
|
||||
final int idx = select(overlay, userId);
|
||||
|
||||
// If the item doesn't exist or is already the highest, don't change anything.
|
||||
if (idx < 0 || idx == mItems.size() - 1) {
|
||||
@@ -297,7 +339,6 @@ final class OverlayManagerSettings {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mItems.add(i + 1, item);
|
||||
}
|
||||
|
||||
@@ -308,7 +349,12 @@ final class OverlayManagerSettings {
|
||||
items = items.filter(item -> item.mUserId == dumpState.getUserId());
|
||||
}
|
||||
if (dumpState.getPackageName() != null) {
|
||||
items = items.filter(item -> item.mPackageName.equals(dumpState.getPackageName()));
|
||||
items = items.filter(item -> item.mOverlay.getPackageName()
|
||||
.equals(dumpState.getPackageName()));
|
||||
}
|
||||
if (dumpState.getOverlayName() != null) {
|
||||
items = items.filter(item -> item.mOverlay.getOverlayName()
|
||||
.equals(dumpState.getOverlayName()));
|
||||
}
|
||||
|
||||
// display items
|
||||
@@ -322,10 +368,11 @@ final class OverlayManagerSettings {
|
||||
|
||||
private void dumpSettingsItem(@NonNull final IndentingPrintWriter pw,
|
||||
@NonNull final SettingsItem item) {
|
||||
pw.println(item.mPackageName + ":" + item.getUserId() + " {");
|
||||
pw.println(item.mOverlay + ":" + item.getUserId() + " {");
|
||||
pw.increaseIndent();
|
||||
|
||||
pw.println("mPackageName...........: " + item.mPackageName);
|
||||
pw.println("mPackageName...........: " + item.mOverlay.getPackageName());
|
||||
pw.println("mOverlayName...........: " + item.mOverlay.getOverlayName());
|
||||
pw.println("mUserId................: " + item.getUserId());
|
||||
pw.println("mTargetPackageName.....: " + item.getTargetPackageName());
|
||||
pw.println("mTargetOverlayableName.: " + item.getTargetOverlayableName());
|
||||
@@ -335,6 +382,7 @@ final class OverlayManagerSettings {
|
||||
pw.println("mIsMutable.............: " + item.isMutable());
|
||||
pw.println("mPriority..............: " + item.mPriority);
|
||||
pw.println("mCategory..............: " + item.mCategory);
|
||||
pw.println("mIsFabricated..........: " + item.mIsFabricated);
|
||||
|
||||
pw.decreaseIndent();
|
||||
pw.println("}");
|
||||
@@ -344,7 +392,10 @@ final class OverlayManagerSettings {
|
||||
@NonNull final SettingsItem item, @NonNull final String field) {
|
||||
switch (field) {
|
||||
case "packagename":
|
||||
pw.println(item.mPackageName);
|
||||
pw.println(item.mOverlay.getPackageName());
|
||||
break;
|
||||
case "overlayname":
|
||||
pw.println(item.mOverlay.getOverlayName());
|
||||
break;
|
||||
case "userid":
|
||||
pw.println(item.mUserId);
|
||||
@@ -392,6 +443,7 @@ final class OverlayManagerSettings {
|
||||
private static final String ATTR_BASE_CODE_PATH = "baseCodePath";
|
||||
private static final String ATTR_IS_ENABLED = "isEnabled";
|
||||
private static final String ATTR_PACKAGE_NAME = "packageName";
|
||||
private static final String ATTR_OVERLAY_NAME = "overlayName";
|
||||
private static final String ATTR_STATE = "state";
|
||||
private static final String ATTR_TARGET_PACKAGE_NAME = "targetPackageName";
|
||||
private static final String ATTR_TARGET_OVERLAYABLE_NAME = "targetOverlayableName";
|
||||
@@ -400,30 +452,26 @@ final class OverlayManagerSettings {
|
||||
private static final String ATTR_CATEGORY = "category";
|
||||
private static final String ATTR_USER_ID = "userId";
|
||||
private static final String ATTR_VERSION = "version";
|
||||
private static final String ATTR_IS_FABRICATED = "fabricated";
|
||||
|
||||
@VisibleForTesting
|
||||
static final int CURRENT_VERSION = 4;
|
||||
|
||||
public static void restore(@NonNull final ArrayList<SettingsItem> table,
|
||||
@NonNull final InputStream is) throws IOException, XmlPullParserException {
|
||||
table.clear();
|
||||
final TypedXmlPullParser parser = Xml.resolvePullParser(is);
|
||||
XmlUtils.beginDocument(parser, TAG_OVERLAYS);
|
||||
final int version = parser.getAttributeInt(null, ATTR_VERSION);
|
||||
if (version != CURRENT_VERSION) {
|
||||
upgrade(version);
|
||||
}
|
||||
|
||||
{
|
||||
table.clear();
|
||||
final TypedXmlPullParser parser = Xml.resolvePullParser(is);
|
||||
XmlUtils.beginDocument(parser, TAG_OVERLAYS);
|
||||
int version = parser.getAttributeInt(null, ATTR_VERSION);
|
||||
if (version != CURRENT_VERSION) {
|
||||
upgrade(version);
|
||||
}
|
||||
int depth = parser.getDepth();
|
||||
|
||||
while (XmlUtils.nextElementWithin(parser, depth)) {
|
||||
switch (parser.getName()) {
|
||||
case TAG_ITEM:
|
||||
final SettingsItem item = restoreRow(parser, depth + 1);
|
||||
table.add(item);
|
||||
break;
|
||||
}
|
||||
final int depth = parser.getDepth();
|
||||
while (XmlUtils.nextElementWithin(parser, depth)) {
|
||||
if (TAG_ITEM.equals(parser.getName())) {
|
||||
final SettingsItem item = restoreRow(parser, depth + 1);
|
||||
table.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -447,7 +495,9 @@ final class OverlayManagerSettings {
|
||||
|
||||
private static SettingsItem restoreRow(@NonNull final TypedXmlPullParser parser,
|
||||
final int depth) throws IOException, XmlPullParserException {
|
||||
final String packageName = XmlUtils.readStringAttribute(parser, ATTR_PACKAGE_NAME);
|
||||
final OverlayIdentifier overlay = new OverlayIdentifier(
|
||||
XmlUtils.readStringAttribute(parser, ATTR_PACKAGE_NAME),
|
||||
XmlUtils.readStringAttribute(parser, ATTR_OVERLAY_NAME));
|
||||
final int userId = parser.getAttributeInt(null, ATTR_USER_ID);
|
||||
final String targetPackageName = XmlUtils.readStringAttribute(parser,
|
||||
ATTR_TARGET_PACKAGE_NAME);
|
||||
@@ -459,9 +509,11 @@ final class OverlayManagerSettings {
|
||||
final boolean isStatic = parser.getAttributeBoolean(null, ATTR_IS_STATIC, false);
|
||||
final int priority = parser.getAttributeInt(null, ATTR_PRIORITY);
|
||||
final String category = XmlUtils.readStringAttribute(parser, ATTR_CATEGORY);
|
||||
final boolean isFabricated = parser.getAttributeBoolean(null, ATTR_IS_FABRICATED,
|
||||
false);
|
||||
|
||||
return new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName,
|
||||
baseCodePath, state, isEnabled, !isStatic, priority, category);
|
||||
return new SettingsItem(overlay, userId, targetPackageName, targetOverlayableName,
|
||||
baseCodePath, state, isEnabled, !isStatic, priority, category, isFabricated);
|
||||
}
|
||||
|
||||
public static void persist(@NonNull final ArrayList<SettingsItem> table,
|
||||
@@ -484,7 +536,8 @@ final class OverlayManagerSettings {
|
||||
private static void persistRow(@NonNull final TypedXmlSerializer xml,
|
||||
@NonNull final SettingsItem item) throws IOException {
|
||||
xml.startTag(null, TAG_ITEM);
|
||||
XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mPackageName);
|
||||
XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mOverlay.getPackageName());
|
||||
XmlUtils.writeStringAttribute(xml, ATTR_OVERLAY_NAME, item.mOverlay.getOverlayName());
|
||||
xml.attributeInt(null, ATTR_USER_ID, item.mUserId);
|
||||
XmlUtils.writeStringAttribute(xml, ATTR_TARGET_PACKAGE_NAME, item.mTargetPackageName);
|
||||
XmlUtils.writeStringAttribute(xml, ATTR_TARGET_OVERLAYABLE_NAME,
|
||||
@@ -495,13 +548,14 @@ final class OverlayManagerSettings {
|
||||
XmlUtils.writeBooleanAttribute(xml, ATTR_IS_STATIC, !item.mIsMutable);
|
||||
xml.attributeInt(null, ATTR_PRIORITY, item.mPriority);
|
||||
XmlUtils.writeStringAttribute(xml, ATTR_CATEGORY, item.mCategory);
|
||||
XmlUtils.writeBooleanAttribute(xml, ATTR_IS_FABRICATED, item.mIsFabricated);
|
||||
xml.endTag(null, TAG_ITEM);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SettingsItem {
|
||||
private final int mUserId;
|
||||
private final String mPackageName;
|
||||
private final OverlayIdentifier mOverlay;
|
||||
private final String mTargetPackageName;
|
||||
private final String mTargetOverlayableName;
|
||||
private String mBaseCodePath;
|
||||
@@ -511,13 +565,15 @@ final class OverlayManagerSettings {
|
||||
private boolean mIsMutable;
|
||||
private int mPriority;
|
||||
private String mCategory;
|
||||
private boolean mIsFabricated;
|
||||
|
||||
SettingsItem(@NonNull final String packageName, final int userId,
|
||||
SettingsItem(@NonNull final OverlayIdentifier overlay, final int userId,
|
||||
@NonNull final String targetPackageName,
|
||||
@Nullable final String targetOverlayableName, @NonNull final String baseCodePath,
|
||||
final @OverlayInfo.State int state, final boolean isEnabled,
|
||||
final boolean isMutable, final int priority, @Nullable String category) {
|
||||
mPackageName = packageName;
|
||||
final boolean isMutable, final int priority, @Nullable String category,
|
||||
final boolean isFabricated) {
|
||||
mOverlay = overlay;
|
||||
mUserId = userId;
|
||||
mTargetPackageName = targetPackageName;
|
||||
mTargetOverlayableName = targetOverlayableName;
|
||||
@@ -528,6 +584,7 @@ final class OverlayManagerSettings {
|
||||
mCache = null;
|
||||
mIsMutable = isMutable;
|
||||
mPriority = priority;
|
||||
mIsFabricated = isFabricated;
|
||||
}
|
||||
|
||||
private String getTargetPackageName() {
|
||||
@@ -596,8 +653,9 @@ final class OverlayManagerSettings {
|
||||
|
||||
private OverlayInfo getOverlayInfo() {
|
||||
if (mCache == null) {
|
||||
mCache = new OverlayInfo(mPackageName, mTargetPackageName, mTargetOverlayableName,
|
||||
mCategory, mBaseCodePath, mState, mUserId, mPriority, mIsMutable);
|
||||
mCache = new OverlayInfo(mOverlay.getPackageName(), mOverlay.getOverlayName(),
|
||||
mTargetPackageName, mTargetOverlayableName, mCategory, mBaseCodePath,
|
||||
mState, mUserId, mPriority, mIsMutable, mIsFabricated);
|
||||
}
|
||||
return mCache;
|
||||
}
|
||||
@@ -620,30 +678,40 @@ final class OverlayManagerSettings {
|
||||
}
|
||||
}
|
||||
|
||||
private int select(@NonNull final String packageName, final int userId) {
|
||||
private int select(@NonNull final OverlayIdentifier overlay, final int userId) {
|
||||
final int n = mItems.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
final SettingsItem item = mItems.get(i);
|
||||
if (item.mUserId == userId && item.mPackageName.equals(packageName)) {
|
||||
if (item.mUserId == userId && item.mOverlay.equals(overlay)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private Stream<SettingsItem> selectWhereUser(final int userId) {
|
||||
return mItems.stream().filter(item -> item.mUserId == userId);
|
||||
private List<SettingsItem> selectWhereUser(final int userId) {
|
||||
final List<SettingsItem> selectedItems = new ArrayList<>();
|
||||
CollectionUtils.addIf(mItems, selectedItems, i -> i.mUserId == userId);
|
||||
return selectedItems;
|
||||
}
|
||||
|
||||
private Stream<SettingsItem> selectWhereTarget(@NonNull final String targetPackageName,
|
||||
private List<SettingsItem> selectWhereOverlay(@NonNull final String packageName,
|
||||
final int userId) {
|
||||
return selectWhereUser(userId)
|
||||
.filter(item -> item.getTargetPackageName().equals(targetPackageName));
|
||||
final List<SettingsItem> items = selectWhereUser(userId);
|
||||
items.removeIf(i -> !i.mOverlay.getPackageName().equals(packageName));
|
||||
return items;
|
||||
}
|
||||
|
||||
static final class BadKeyException extends RuntimeException {
|
||||
BadKeyException(@NonNull final String packageName, final int userId) {
|
||||
super("Bad key mPackageName=" + packageName + " mUserId=" + userId);
|
||||
private List<SettingsItem> selectWhereTarget(@NonNull final String targetPackageName,
|
||||
final int userId) {
|
||||
final List<SettingsItem> items = selectWhereUser(userId);
|
||||
items.removeIf(i -> !i.getTargetPackageName().equals(targetPackageName));
|
||||
return items;
|
||||
}
|
||||
|
||||
static final class BadKeyException extends Exception {
|
||||
BadKeyException(@NonNull final OverlayIdentifier overlay, final int userId) {
|
||||
super("Bad key '" + overlay + "' for user " + userId );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,20 +19,28 @@ package com.android.server.om;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.om.FabricatedOverlay;
|
||||
import android.content.om.IOverlayManager;
|
||||
import android.content.om.OverlayIdentifier;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.content.om.OverlayManagerTransaction;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Binder;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ShellCommand;
|
||||
import android.os.UserHandle;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -72,6 +80,8 @@ final class OverlayManagerShellCommand extends ShellCommand {
|
||||
return runSetPriority();
|
||||
case "lookup":
|
||||
return runLookup();
|
||||
case "fabricate":
|
||||
return runFabricate();
|
||||
default:
|
||||
return handleDefaultCommands(cmd);
|
||||
}
|
||||
@@ -89,35 +99,36 @@ final class OverlayManagerShellCommand extends ShellCommand {
|
||||
out.println("Overlay manager (overlay) commands:");
|
||||
out.println(" help");
|
||||
out.println(" Print this help text.");
|
||||
out.println(" dump [--verbose] [--user USER_ID] [[FIELD] PACKAGE]");
|
||||
out.println(" dump [--verbose] [--user USER_ID] [[FIELD] PACKAGE[:NAME]]");
|
||||
out.println(" Print debugging information about the overlay manager.");
|
||||
out.println(" With optional parameter PACKAGE, limit output to the specified");
|
||||
out.println(" package. With optional parameter FIELD, limit output to");
|
||||
out.println(" With optional parameters PACKAGE and NAME, limit output to the specified");
|
||||
out.println(" overlay or target. With optional parameter FIELD, limit output to");
|
||||
out.println(" the value of that SettingsItem field. Field names are");
|
||||
out.println(" case insensitive and out.println the m prefix can be omitted,");
|
||||
out.println(" so the following are equivalent: mState, mstate, State, state.");
|
||||
out.println(" list [--user USER_ID] [PACKAGE]");
|
||||
out.println(" list [--user USER_ID] [PACKAGE[:NAME]]");
|
||||
out.println(" Print information about target and overlay packages.");
|
||||
out.println(" Overlay packages are printed in priority order. With optional");
|
||||
out.println(" parameter PACKAGE, limit output to the specified package.");
|
||||
out.println(" enable [--user USER_ID] PACKAGE");
|
||||
out.println(" Enable overlay package PACKAGE.");
|
||||
out.println(" disable [--user USER_ID] PACKAGE");
|
||||
out.println(" Disable overlay package PACKAGE.");
|
||||
out.println(" enable-exclusive [--user USER_ID] [--category] PACKAGE");
|
||||
out.println(" Enable overlay package PACKAGE and disable all other overlays for");
|
||||
out.println(" its target package. If the --category option is given, only disables");
|
||||
out.println(" other overlays in the same category.");
|
||||
out.println(" parameters PACKAGE and NAME, limit output to the specified overlay or");
|
||||
out.println(" target.");
|
||||
out.println(" enable [--user USER_ID] PACKAGE[:NAME]");
|
||||
out.println(" Enable overlay within or owned by PACKAGE with optional unique NAME.");
|
||||
out.println(" disable [--user USER_ID] PACKAGE[:NAME]");
|
||||
out.println(" Disable overlay within or owned by PACKAGE with optional unique NAME.");
|
||||
out.println(" enable-exclusive [--user USER_ID] [--category] PACKAGE[:NAME]");
|
||||
out.println(" Enable overlay within or owned by PACKAGE with optional unique NAME and");
|
||||
out.println(" disable all other overlays for its target package. If the --category");
|
||||
out.println(" option is given, only disables other overlays in the same category.");
|
||||
out.println(" set-priority [--user USER_ID] PACKAGE PARENT|lowest|highest");
|
||||
out.println(" Change the priority of the overlay PACKAGE to be just higher than");
|
||||
out.println(" the priority of PACKAGE_PARENT If PARENT is the special keyword");
|
||||
out.println(" Change the priority of the overlay to be just higher than");
|
||||
out.println(" the priority of PARENT If PARENT is the special keyword");
|
||||
out.println(" 'lowest', change priority of PACKAGE to the lowest priority.");
|
||||
out.println(" If PARENT is the special keyword 'highest', change priority of");
|
||||
out.println(" PACKAGE to the highest priority.");
|
||||
out.println(" lookup [--verbose] PACKAGE-TO-LOAD PACKAGE:TYPE/NAME");
|
||||
out.println(" Load a package and print the value of a given resource");
|
||||
out.println(" applying the current configuration and enabled overlays.");
|
||||
out.println(" For a more fine-grained alernative, use 'idmap2 lookup'.");
|
||||
out.println(" For a more fine-grained alternative, use 'idmap2 lookup'.");
|
||||
}
|
||||
|
||||
private int runList() throws RemoteException {
|
||||
@@ -192,7 +203,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
|
||||
status = "---";
|
||||
break;
|
||||
}
|
||||
out.println(String.format("%s %s", status, oi.packageName));
|
||||
out.println(String.format("%s %s", status, oi.getOverlayIdentifier()));
|
||||
}
|
||||
|
||||
private int runEnableDisable(final boolean enable) throws RemoteException {
|
||||
@@ -211,8 +222,88 @@ final class OverlayManagerShellCommand extends ShellCommand {
|
||||
}
|
||||
}
|
||||
|
||||
final String packageName = getNextArgRequired();
|
||||
return mInterface.setEnabled(packageName, enable, userId) ? 0 : 1;
|
||||
final OverlayIdentifier overlay = OverlayIdentifier.fromString(getNextArgRequired());
|
||||
mInterface.commit(new OverlayManagerTransaction.Builder()
|
||||
.setEnabled(overlay, enable, userId)
|
||||
.build());
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runFabricate() throws RemoteException {
|
||||
final PrintWriter err = getErrPrintWriter();
|
||||
if (Binder.getCallingUid() != Process.ROOT_UID) {
|
||||
err.println("Error: must be root to fabricate overlays through the shell");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int userId = UserHandle.USER_SYSTEM;
|
||||
String targetPackage = "";
|
||||
String targetOverlayable = "";
|
||||
String name = "";
|
||||
String opt;
|
||||
while ((opt = getNextOption()) != null) {
|
||||
switch (opt) {
|
||||
case "--user":
|
||||
userId = UserHandle.parseUserArg(getNextArgRequired());
|
||||
break;
|
||||
case "--target":
|
||||
targetPackage = getNextArgRequired();
|
||||
break;
|
||||
case "--target-name":
|
||||
targetOverlayable = getNextArgRequired();
|
||||
break;
|
||||
case "--name":
|
||||
name = getNextArgRequired();
|
||||
break;
|
||||
default:
|
||||
err.println("Error: Unknown option: " + opt);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (name.isEmpty()) {
|
||||
err.println("Error: Missing required arg '--name'");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (targetPackage.isEmpty()) {
|
||||
err.println("Error: Missing required arg '--target'");
|
||||
return 1;
|
||||
}
|
||||
|
||||
final String resourceName = getNextArgRequired();
|
||||
final String typeStr = getNextArgRequired();
|
||||
final int type;
|
||||
if (typeStr.startsWith("0x")) {
|
||||
type = Integer.parseUnsignedInt(typeStr.substring(2), 16);
|
||||
} else {
|
||||
type = Integer.parseUnsignedInt(typeStr);
|
||||
}
|
||||
final String dataStr = getNextArgRequired();
|
||||
final int data;
|
||||
if (dataStr.startsWith("0x")) {
|
||||
data = Integer.parseUnsignedInt(dataStr.substring(2), 16);
|
||||
} else {
|
||||
data = Integer.parseUnsignedInt(dataStr);
|
||||
}
|
||||
|
||||
final PackageManager pm = mContext.getPackageManager();
|
||||
if (pm == null) {
|
||||
err.println("Error: failed to get package manager");
|
||||
return 1;
|
||||
}
|
||||
|
||||
final String overlayPackageName = "com.android.shell";
|
||||
final FabricatedOverlay overlay = new FabricatedOverlay.Builder(
|
||||
overlayPackageName, name, targetPackage)
|
||||
.setTargetOverlayable(targetOverlayable)
|
||||
.setResourceValue(resourceName, type, data)
|
||||
.build();
|
||||
|
||||
mInterface.commit(new OverlayManagerTransaction.Builder()
|
||||
.registerFabricatedOverlay(overlay)
|
||||
.build());
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runEnableExclusive() throws RemoteException {
|
||||
@@ -234,12 +325,27 @@ final class OverlayManagerShellCommand extends ShellCommand {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
final String overlay = getNextArgRequired();
|
||||
if (inCategory) {
|
||||
return mInterface.setEnabledExclusiveInCategory(overlay, userId) ? 0 : 1;
|
||||
} else {
|
||||
return mInterface.setEnabledExclusive(overlay, true, userId) ? 0 : 1;
|
||||
|
||||
final OverlayIdentifier overlay = OverlayIdentifier.fromString(getNextArgRequired());
|
||||
final OverlayInfo overlayInfo = mInterface.getOverlayInfoByIdentifier(overlay, userId);
|
||||
if (overlayInfo == null) {
|
||||
err.println("Error: Unable to get overlay info of: " + overlay);
|
||||
return 1;
|
||||
}
|
||||
|
||||
final List<OverlayInfo> overlaysForTarget =
|
||||
mInterface.getOverlayInfosForTarget(overlayInfo.targetPackageName, userId);
|
||||
final OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder();
|
||||
for (final OverlayInfo disableOverlay : overlaysForTarget) {
|
||||
if ((inCategory && !Objects.equals(disableOverlay.category,overlayInfo.category))
|
||||
|| !disableOverlay.isMutable) {
|
||||
continue;
|
||||
}
|
||||
builder.setEnabled(disableOverlay.getOverlayIdentifier(), false, userId);
|
||||
}
|
||||
builder.setEnabled(overlayInfo.getOverlayIdentifier(), true, userId);
|
||||
mInterface.commit(builder.build());
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runSetPriority() throws RemoteException {
|
||||
|
||||
@@ -22,10 +22,15 @@ import android.content.om.OverlayableInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.pm.PackageManagerServiceUtils;
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -36,6 +41,33 @@ import java.util.Map;
|
||||
* @hide
|
||||
*/
|
||||
interface PackageManagerHelper {
|
||||
|
||||
/**
|
||||
* Initializes the helper for the user. This only needs to be invoked one time before
|
||||
* packages of this user are queried.
|
||||
* @param userId the user id to initialize
|
||||
* @return a map of package name to all packages installed in the user
|
||||
*/
|
||||
@NonNull
|
||||
ArrayMap<String, AndroidPackage> initializeForUser(final int userId);
|
||||
|
||||
/**
|
||||
* Retrieves the package information if it is installed for the user.
|
||||
*/
|
||||
@Nullable
|
||||
AndroidPackage getPackageForUser(@NonNull final String packageName, final int userId);
|
||||
|
||||
/**
|
||||
* Returns whether the package is an instant app.
|
||||
*/
|
||||
boolean isInstantApp(@NonNull final String packageName, final int userId);
|
||||
|
||||
/**
|
||||
* @see PackageManager#getPackagesForUid(int)
|
||||
*/
|
||||
@Nullable
|
||||
String[] getPackagesForUid(int uid);
|
||||
|
||||
/**
|
||||
* @return true if the target package has declared an overlayable
|
||||
*/
|
||||
@@ -63,11 +95,6 @@ interface PackageManagerHelper {
|
||||
@NonNull
|
||||
Map<String, Map<String, String>> getNamedActors();
|
||||
|
||||
/**
|
||||
* @see PackageManagerInternal#getOverlayPackages(int)
|
||||
*/
|
||||
List<PackageInfo> getOverlayPackages(int userId);
|
||||
|
||||
/**
|
||||
* Read from the APK and AndroidManifest of a package to return the overlayable defined for
|
||||
* a given name.
|
||||
@@ -79,19 +106,6 @@ interface PackageManagerHelper {
|
||||
@NonNull String targetOverlayableName, int userId)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* @see PackageManager#getPackagesForUid(int)
|
||||
*/
|
||||
@Nullable
|
||||
String[] getPackagesForUid(int uid);
|
||||
|
||||
/**
|
||||
* @param userId user to filter package visibility by
|
||||
* @see PackageManager#getPackageInfo(String, int)
|
||||
*/
|
||||
@Nullable
|
||||
PackageInfo getPackageInfo(@NonNull String packageName, int userId);
|
||||
|
||||
/**
|
||||
* @return true if {@link PackageManagerServiceUtils#compareSignatures} run on both packages
|
||||
* in the system returns {@link PackageManager#SIGNATURE_MATCH}
|
||||
|
||||
@@ -21,7 +21,9 @@ import android.content.om.OverlayableInfo
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import android.os.Process
|
||||
import android.util.ArrayMap
|
||||
import com.android.server.om.OverlayActorEnforcer.ActorState
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage
|
||||
import com.android.server.testutils.mockThrowOnUnmocked
|
||||
import com.android.server.testutils.whenever
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
@@ -29,6 +31,7 @@ import org.junit.BeforeClass
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.spy
|
||||
import java.io.IOException
|
||||
|
||||
@@ -125,11 +128,11 @@ class OverlayActorEnforcerTests {
|
||||
ActorState.TARGET_NOT_FOUND withCases {
|
||||
failure("nullPkgInfo") { targetPkgInfo = null }
|
||||
allowed("debuggable") {
|
||||
targetPkgInfo = pkgInfo(TARGET_PKG).apply {
|
||||
applicationInfo.flags = ApplicationInfo.FLAG_DEBUGGABLE
|
||||
targetPkgInfo = androidPackage(TARGET_PKG).apply {
|
||||
whenever(this.isDebuggable).thenReturn(true)
|
||||
}
|
||||
}
|
||||
skip { targetPkgInfo = pkgInfo(TARGET_PKG) }
|
||||
skip { targetPkgInfo = androidPackage(TARGET_PKG) }
|
||||
},
|
||||
ActorState.NO_PACKAGES_FOR_UID withCases {
|
||||
failure("empty") { callingUid = EMPTY_UID }
|
||||
@@ -236,22 +239,20 @@ class OverlayActorEnforcerTests {
|
||||
mapOf(VALID_ACTOR_NAME to VALID_ACTOR_PKG))
|
||||
}
|
||||
},
|
||||
ActorState.MISSING_APP_INFO withCases {
|
||||
ActorState.ACTOR_NOT_FOUND withCases {
|
||||
failure("nullActorPkgInfo") { actorPkgInfo = null }
|
||||
failure("nullActorAppInfo") {
|
||||
actorPkgInfo = PackageInfo().apply { applicationInfo = null }
|
||||
actorPkgInfo = null
|
||||
}
|
||||
skip { actorPkgInfo = pkgInfo(VALID_ACTOR_PKG) }
|
||||
skip { actorPkgInfo = androidPackage(VALID_ACTOR_PKG) }
|
||||
},
|
||||
ActorState.ACTOR_NOT_PREINSTALLED withCases {
|
||||
failure("notSystem") {
|
||||
actorPkgInfo = pkgInfo(VALID_ACTOR_PKG).apply {
|
||||
applicationInfo.flags = 0
|
||||
}
|
||||
actorPkgInfo = androidPackage(VALID_ACTOR_PKG)
|
||||
}
|
||||
skip {
|
||||
actorPkgInfo = pkgInfo(VALID_ACTOR_PKG).apply {
|
||||
applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM
|
||||
actorPkgInfo = androidPackage(VALID_ACTOR_PKG).apply {
|
||||
whenever(this.isSystem).thenReturn(true)
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -272,22 +273,22 @@ class OverlayActorEnforcerTests {
|
||||
) {
|
||||
fun toOverlayInfo() = OverlayInfo(
|
||||
OVERLAY_PKG,
|
||||
"",
|
||||
targetPackageName,
|
||||
targetOverlayableName,
|
||||
null,
|
||||
"/path",
|
||||
OverlayInfo.STATE_UNKNOWN, 0,
|
||||
0, false)
|
||||
0, false, false)
|
||||
}
|
||||
|
||||
private infix fun ActorState.withCases(block: TestCase.() -> Unit) =
|
||||
TestCase(this).apply(block)
|
||||
|
||||
private fun pkgInfo(pkgName: String): PackageInfo = mockThrowOnUnmocked {
|
||||
this.packageName = pkgName
|
||||
this.applicationInfo = ApplicationInfo().apply {
|
||||
this.packageName = pkgName
|
||||
}
|
||||
private fun androidPackage(pkgName: String): AndroidPackage = mockThrowOnUnmocked {
|
||||
whenever(this.packageName).thenReturn(pkgName)
|
||||
whenever(this.isDebuggable).thenReturn(false)
|
||||
whenever(this.isSystem).thenReturn(false)
|
||||
}
|
||||
|
||||
private fun makeTestName(testCase: TestCase, caseName: String, type: Params.Type): String {
|
||||
@@ -363,8 +364,8 @@ class OverlayActorEnforcerTests {
|
||||
var namedActorsMap: Map<String, Map<String, String>> = emptyMap(),
|
||||
var hasPermission: Boolean = false,
|
||||
var targetOverlayableInfo: OverlayableInfo? = null,
|
||||
var targetPkgInfo: PackageInfo? = null,
|
||||
var actorPkgInfo: PackageInfo? = null,
|
||||
var targetPkgInfo: AndroidPackage? = null,
|
||||
var actorPkgInfo: AndroidPackage? = null,
|
||||
vararg val packageNames: String = arrayOf("com.test.actor.one")
|
||||
) : PackageManagerHelper {
|
||||
|
||||
@@ -375,6 +376,14 @@ class OverlayActorEnforcerTests {
|
||||
|
||||
override fun getNamedActors() = namedActorsMap
|
||||
|
||||
override fun isInstantApp(packageName: String, userId: Int): Boolean {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun initializeForUser(userId: Int): ArrayMap<String, AndroidPackage> {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun getOverlayableForTarget(
|
||||
packageName: String,
|
||||
@@ -394,9 +403,6 @@ class OverlayActorEnforcerTests {
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun getPackageInfo(packageName: String, userId: Int) =
|
||||
listOfNotNull(targetPkgInfo, actorPkgInfo).find { it.packageName == packageName }
|
||||
|
||||
@Throws(IOException::class) // Mockito requires this checked exception to be declared
|
||||
override fun doesTargetDefineOverlayable(targetPackageName: String?, userId: Int): Boolean {
|
||||
return targetOverlayableInfo?.takeIf {
|
||||
@@ -411,11 +417,10 @@ class OverlayActorEnforcerTests {
|
||||
}
|
||||
}
|
||||
|
||||
override fun getConfigSignaturePackage(): String {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
override fun getPackageForUser(packageName: String, userId: Int) =
|
||||
listOfNotNull(targetPkgInfo, actorPkgInfo).find { it.packageName == packageName }
|
||||
|
||||
override fun getOverlayPackages(userId: Int): MutableList<PackageInfo> {
|
||||
override fun getConfigSignaturePackage(): String {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,9 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.om.OverlayIdentifier;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
@@ -29,77 +31,66 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceImplTestsBase {
|
||||
|
||||
private static final String OVERLAY = "com.test.overlay";
|
||||
private static final OverlayIdentifier IDENTIFIER = new OverlayIdentifier(OVERLAY);
|
||||
private static final String TARGET = "com.test.target";
|
||||
private static final int USER = 0;
|
||||
|
||||
private static final String OVERLAY2 = OVERLAY + "2";
|
||||
private static final OverlayIdentifier IDENTIFIER2 = new OverlayIdentifier(OVERLAY2);
|
||||
|
||||
@Test
|
||||
public void testUpdateOverlaysForUser() {
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
final String otherTarget = "some.other.target";
|
||||
addPackage(target(TARGET), USER);
|
||||
addPackage(target("some.other.target"), USER);
|
||||
addPackage(target(otherTarget), USER);
|
||||
addPackage(overlay(OVERLAY, TARGET), USER);
|
||||
|
||||
// do nothing, expect no change
|
||||
final List<String> a = impl.updateOverlaysForUser(USER);
|
||||
assertEquals(1, a.size());
|
||||
assertTrue(a.contains(TARGET));
|
||||
final ArraySet<PackageAndUser> a = impl.updateOverlaysForUser(USER);
|
||||
assertEquals(3, a.size());
|
||||
assertTrue(a.containsAll(Arrays.asList(
|
||||
new PackageAndUser(TARGET, USER),
|
||||
new PackageAndUser(otherTarget, USER),
|
||||
new PackageAndUser(OVERLAY, USER))));
|
||||
|
||||
// upgrade overlay, keep target
|
||||
addPackage(overlay(OVERLAY, TARGET), USER);
|
||||
|
||||
final List<String> b = impl.updateOverlaysForUser(USER);
|
||||
assertEquals(1, b.size());
|
||||
assertTrue(b.contains(TARGET));
|
||||
|
||||
// do nothing, expect no change
|
||||
final List<String> c = impl.updateOverlaysForUser(USER);
|
||||
assertEquals(1, c.size());
|
||||
assertTrue(c.contains(TARGET));
|
||||
|
||||
// upgrade overlay, switch to new target
|
||||
addPackage(overlay(OVERLAY, "some.other.target"), USER);
|
||||
final List<String> d = impl.updateOverlaysForUser(USER);
|
||||
assertEquals(2, d.size());
|
||||
assertTrue(d.containsAll(Arrays.asList(TARGET, "some.other.target")));
|
||||
|
||||
// do nothing, expect no change
|
||||
final List<String> f = impl.updateOverlaysForUser(USER);
|
||||
assertEquals(1, f.size());
|
||||
assertTrue(f.contains("some.other.target"));
|
||||
final ArraySet<PackageAndUser> b = impl.updateOverlaysForUser(USER);
|
||||
assertEquals(3, b.size());
|
||||
assertTrue(b.containsAll(Arrays.asList(
|
||||
new PackageAndUser(TARGET, USER),
|
||||
new PackageAndUser(otherTarget, USER),
|
||||
new PackageAndUser(OVERLAY, USER))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImmutableEnabledChange() throws Exception {
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installPackage(target(TARGET), USER);
|
||||
installPackage(overlay(OVERLAY, TARGET), USER);
|
||||
|
||||
configureSystemOverlay(OVERLAY, false /* mutable */, false /* enabled */, 0 /* priority */);
|
||||
impl.updateOverlaysForUser(USER);
|
||||
final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
|
||||
final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER);
|
||||
assertNotNull(o1);
|
||||
assertFalse(o1.isEnabled());
|
||||
assertFalse(o1.isMutable);
|
||||
|
||||
configureSystemOverlay(OVERLAY, false /* mutable */, true /* enabled */, 0 /* priority */);
|
||||
impl.updateOverlaysForUser(USER);
|
||||
final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER);
|
||||
final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER, USER);
|
||||
assertNotNull(o2);
|
||||
assertTrue(o2.isEnabled());
|
||||
assertFalse(o2.isMutable);
|
||||
|
||||
configureSystemOverlay(OVERLAY, false /* mutable */, false /* enabled */, 0 /* priority */);
|
||||
impl.updateOverlaysForUser(USER);
|
||||
final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
|
||||
final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER);
|
||||
assertNotNull(o3);
|
||||
assertFalse(o3.isEnabled());
|
||||
assertFalse(o3.isMutable);
|
||||
@@ -108,26 +99,26 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
|
||||
@Test
|
||||
public void testMutableEnabledChangeHasNoEffect() throws Exception {
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installPackage(target(TARGET), USER);
|
||||
installPackage(overlay(OVERLAY, TARGET), USER);
|
||||
configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */);
|
||||
|
||||
impl.updateOverlaysForUser(USER);
|
||||
final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
|
||||
final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER);
|
||||
assertNotNull(o1);
|
||||
assertFalse(o1.isEnabled());
|
||||
assertTrue(o1.isMutable);
|
||||
|
||||
configureSystemOverlay(OVERLAY, true /* mutable */, true /* enabled */, 0 /* priority */);
|
||||
impl.updateOverlaysForUser(USER);
|
||||
final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY, USER);
|
||||
final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER, USER);
|
||||
assertNotNull(o2);
|
||||
assertFalse(o2.isEnabled());
|
||||
assertTrue(o2.isMutable);
|
||||
|
||||
configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */);
|
||||
impl.updateOverlaysForUser(USER);
|
||||
final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
|
||||
final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER);
|
||||
assertNotNull(o3);
|
||||
assertFalse(o3.isEnabled());
|
||||
assertTrue(o3.isMutable);
|
||||
@@ -136,13 +127,13 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
|
||||
@Test
|
||||
public void testMutableEnabledToImmutableEnabled() throws Exception {
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installPackage(target(TARGET), USER);
|
||||
installPackage(overlay(OVERLAY, TARGET), USER);
|
||||
|
||||
final BiConsumer<Boolean, Boolean> setOverlay = (mutable, enabled) -> {
|
||||
configureSystemOverlay(OVERLAY, mutable, enabled, 0 /* priority */);
|
||||
impl.updateOverlaysForUser(USER);
|
||||
final OverlayInfo o = impl.getOverlayInfo(OVERLAY, USER);
|
||||
final OverlayInfo o = impl.getOverlayInfo(IDENTIFIER, USER);
|
||||
assertNotNull(o);
|
||||
assertEquals(enabled, o.isEnabled());
|
||||
assertEquals(mutable, o.isMutable);
|
||||
@@ -180,38 +171,38 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
|
||||
@Test
|
||||
public void testMutablePriorityChange() throws Exception {
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY2, TARGET), USER);
|
||||
installPackage(target(TARGET), USER);
|
||||
installPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installPackage(overlay(OVERLAY2, TARGET), USER);
|
||||
configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 0 /* priority */);
|
||||
configureSystemOverlay(OVERLAY2, true /* mutable */, false /* enabled */, 1 /* priority */);
|
||||
impl.updateOverlaysForUser(USER);
|
||||
|
||||
final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
|
||||
final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER);
|
||||
assertNotNull(o1);
|
||||
assertEquals(0, o1.priority);
|
||||
assertFalse(o1.isEnabled());
|
||||
|
||||
final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER);
|
||||
final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER2, USER);
|
||||
assertNotNull(o2);
|
||||
assertEquals(1, o2.priority);
|
||||
assertFalse(o2.isEnabled());
|
||||
|
||||
// Overlay priority changing between reboots should not affect enable state of mutable
|
||||
// overlays.
|
||||
impl.setEnabled(OVERLAY, true, USER);
|
||||
impl.setEnabled(IDENTIFIER, true, USER);
|
||||
|
||||
// Reorder the overlays
|
||||
configureSystemOverlay(OVERLAY, true /* mutable */, false /* enabled */, 1 /* priority */);
|
||||
configureSystemOverlay(OVERLAY2, true /* mutable */, false /* enabled */, 0 /* priority */);
|
||||
impl.updateOverlaysForUser(USER);
|
||||
|
||||
final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
|
||||
final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER);
|
||||
assertNotNull(o3);
|
||||
assertEquals(1, o3.priority);
|
||||
assertTrue(o3.isEnabled());
|
||||
|
||||
final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER);
|
||||
final OverlayInfo o4 = impl.getOverlayInfo(IDENTIFIER2, USER);
|
||||
assertNotNull(o4);
|
||||
assertEquals(0, o4.priority);
|
||||
assertFalse(o4.isEnabled());
|
||||
@@ -220,19 +211,19 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
|
||||
@Test
|
||||
public void testImmutablePriorityChange() throws Exception {
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY2, TARGET), USER);
|
||||
installPackage(target(TARGET), USER);
|
||||
installPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installPackage(overlay(OVERLAY2, TARGET), USER);
|
||||
configureSystemOverlay(OVERLAY, false /* mutable */, true /* enabled */, 0 /* priority */);
|
||||
configureSystemOverlay(OVERLAY2, false /* mutable */, true /* enabled */, 1 /* priority */);
|
||||
impl.updateOverlaysForUser(USER);
|
||||
|
||||
final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
|
||||
final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER);
|
||||
assertNotNull(o1);
|
||||
assertEquals(0, o1.priority);
|
||||
assertTrue(o1.isEnabled());
|
||||
|
||||
final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER);
|
||||
final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER2, USER);
|
||||
assertNotNull(o2);
|
||||
assertEquals(1, o2.priority);
|
||||
assertTrue(o2.isEnabled());
|
||||
@@ -242,12 +233,12 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
|
||||
configureSystemOverlay(OVERLAY2, false /* mutable */, true /* enabled */, 0 /* priority */);
|
||||
impl.updateOverlaysForUser(USER);
|
||||
|
||||
final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY, USER);
|
||||
final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER, USER);
|
||||
assertNotNull(o3);
|
||||
assertEquals(1, o3.priority);
|
||||
assertTrue(o3.isEnabled());
|
||||
|
||||
final OverlayInfo o4 = impl.getOverlayInfo(OVERLAY2, USER);
|
||||
final OverlayInfo o4 = impl.getOverlayInfo(IDENTIFIER2, USER);
|
||||
assertNotNull(o4);
|
||||
assertEquals(0, o4.priority);
|
||||
assertTrue(o4.isEnabled());
|
||||
|
||||
@@ -28,6 +28,7 @@ import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import android.content.om.OverlayIdentifier;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.util.Pair;
|
||||
|
||||
@@ -39,19 +40,23 @@ import org.junit.runner.RunWith;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
|
||||
|
||||
private static final String OVERLAY = "com.test.overlay";
|
||||
private static final OverlayIdentifier IDENTIFIER = new OverlayIdentifier(OVERLAY);
|
||||
private static final String TARGET = "com.test.target";
|
||||
private static final int USER = 0;
|
||||
|
||||
private static final String OVERLAY2 = OVERLAY + "2";
|
||||
private static final String TARGET2 = TARGET + "2";
|
||||
private static final OverlayIdentifier IDENTIFIER2 = new OverlayIdentifier(OVERLAY2);
|
||||
private static final int USER2 = USER + 1;
|
||||
|
||||
private static final String OVERLAY3 = OVERLAY + "3";
|
||||
private static final OverlayIdentifier IDENTIFIER3 = new OverlayIdentifier(OVERLAY3);
|
||||
private static final int USER3 = USER2 + 1;
|
||||
|
||||
private static final String CONFIG_SIGNATURE_REFERENCE_PKG = "com.test.ref";
|
||||
@@ -60,10 +65,10 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
|
||||
@Test
|
||||
public void testGetOverlayInfo() throws Exception {
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installPackage(overlay(OVERLAY, TARGET), USER);
|
||||
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
final OverlayInfo oi = impl.getOverlayInfo(OVERLAY, USER);
|
||||
final OverlayInfo oi = impl.getOverlayInfo(IDENTIFIER, USER);
|
||||
assertNotNull(oi);
|
||||
assertEquals(oi.packageName, OVERLAY);
|
||||
assertEquals(oi.targetPackageName, TARGET);
|
||||
@@ -72,19 +77,19 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
|
||||
@Test
|
||||
public void testGetOverlayInfosForTarget() throws Exception {
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY2, TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY3, TARGET), USER2);
|
||||
installPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installPackage(overlay(OVERLAY2, TARGET), USER);
|
||||
installPackage(overlay(OVERLAY3, TARGET), USER2);
|
||||
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
final List<OverlayInfo> ois = impl.getOverlayInfosForTarget(TARGET, USER);
|
||||
assertEquals(ois.size(), 2);
|
||||
assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY, USER)));
|
||||
assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY2, USER)));
|
||||
assertTrue(ois.contains(impl.getOverlayInfo(IDENTIFIER, USER)));
|
||||
assertTrue(ois.contains(impl.getOverlayInfo(IDENTIFIER2, USER)));
|
||||
|
||||
final List<OverlayInfo> ois2 = impl.getOverlayInfosForTarget(TARGET, USER2);
|
||||
assertEquals(ois2.size(), 1);
|
||||
assertTrue(ois2.contains(impl.getOverlayInfo(OVERLAY3, USER2)));
|
||||
assertTrue(ois2.contains(impl.getOverlayInfo(IDENTIFIER3, USER2)));
|
||||
|
||||
final List<OverlayInfo> ois3 = impl.getOverlayInfosForTarget(TARGET, USER3);
|
||||
assertNotNull(ois3);
|
||||
@@ -97,10 +102,10 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
|
||||
@Test
|
||||
public void testGetOverlayInfosForUser() throws Exception {
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY2, TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY3, TARGET2), USER);
|
||||
installPackage(target(TARGET), USER);
|
||||
installPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installPackage(overlay(OVERLAY2, TARGET), USER);
|
||||
installPackage(overlay(OVERLAY3, TARGET2), USER);
|
||||
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
final Map<String, List<OverlayInfo>> everything = impl.getOverlaysForUser(USER);
|
||||
@@ -109,13 +114,13 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
final List<OverlayInfo> ois = everything.get(TARGET);
|
||||
assertNotNull(ois);
|
||||
assertEquals(ois.size(), 2);
|
||||
assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY, USER)));
|
||||
assertTrue(ois.contains(impl.getOverlayInfo(OVERLAY2, USER)));
|
||||
assertTrue(ois.contains(impl.getOverlayInfo(IDENTIFIER, USER)));
|
||||
assertTrue(ois.contains(impl.getOverlayInfo(IDENTIFIER2, USER)));
|
||||
|
||||
final List<OverlayInfo> ois2 = everything.get(TARGET2);
|
||||
assertNotNull(ois2);
|
||||
assertEquals(ois2.size(), 1);
|
||||
assertTrue(ois2.contains(impl.getOverlayInfo(OVERLAY3, USER)));
|
||||
assertTrue(ois2.contains(impl.getOverlayInfo(IDENTIFIER3, USER)));
|
||||
|
||||
final Map<String, List<OverlayInfo>> everything2 = impl.getOverlaysForUser(USER2);
|
||||
assertNotNull(everything2);
|
||||
@@ -124,26 +129,26 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
|
||||
@Test
|
||||
public void testPriority() throws Exception {
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY2, TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY3, TARGET), USER);
|
||||
installPackage(overlay(OVERLAY, TARGET), USER);
|
||||
installPackage(overlay(OVERLAY2, TARGET), USER);
|
||||
installPackage(overlay(OVERLAY3, TARGET), USER);
|
||||
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
final OverlayInfo o1 = impl.getOverlayInfo(OVERLAY, USER);
|
||||
final OverlayInfo o2 = impl.getOverlayInfo(OVERLAY2, USER);
|
||||
final OverlayInfo o3 = impl.getOverlayInfo(OVERLAY3, USER);
|
||||
final OverlayInfo o1 = impl.getOverlayInfo(IDENTIFIER, USER);
|
||||
final OverlayInfo o2 = impl.getOverlayInfo(IDENTIFIER2, USER);
|
||||
final OverlayInfo o3 = impl.getOverlayInfo(IDENTIFIER3, USER);
|
||||
|
||||
assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
|
||||
|
||||
assertEquals(impl.setLowestPriority(OVERLAY3, USER),
|
||||
assertEquals(impl.setLowestPriority(IDENTIFIER3, USER),
|
||||
Optional.of(new PackageAndUser(TARGET, USER)));
|
||||
assertOverlayInfoForTarget(TARGET, USER, o3, o1, o2);
|
||||
|
||||
assertEquals(impl.setHighestPriority(OVERLAY3, USER),
|
||||
Optional.of(new PackageAndUser(TARGET, USER)));
|
||||
assertEquals(impl.setHighestPriority(IDENTIFIER3, USER),
|
||||
Set.of(new PackageAndUser(TARGET, USER)));
|
||||
assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
|
||||
|
||||
assertEquals(impl.setPriority(OVERLAY, OVERLAY2, USER),
|
||||
assertEquals(impl.setPriority(IDENTIFIER, IDENTIFIER2, USER),
|
||||
Optional.of(new PackageAndUser(TARGET, USER)));
|
||||
assertOverlayInfoForTarget(TARGET, USER, o2, o1, o3);
|
||||
}
|
||||
@@ -151,61 +156,63 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
@Test
|
||||
public void testOverlayInfoStateTransitions() throws Exception {
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
assertNull(impl.getOverlayInfo(OVERLAY, USER));
|
||||
assertNull(impl.getOverlayInfo(IDENTIFIER, USER));
|
||||
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
assertState(STATE_MISSING_TARGET, OVERLAY, USER);
|
||||
installPackage(overlay(OVERLAY, TARGET), USER);
|
||||
assertState(STATE_MISSING_TARGET, IDENTIFIER, USER);
|
||||
|
||||
final FakeDeviceState.PackageBuilder target = target(TARGET);
|
||||
installNewPackage(target, USER);
|
||||
assertState(STATE_DISABLED, OVERLAY, USER);
|
||||
installPackage(target, USER);
|
||||
assertState(STATE_DISABLED, IDENTIFIER, USER);
|
||||
|
||||
assertEquals(impl.setEnabled(OVERLAY, true, USER),
|
||||
Optional.of(new PackageAndUser(TARGET, USER)));
|
||||
assertState(STATE_ENABLED, OVERLAY, USER);
|
||||
assertEquals(impl.setEnabled(IDENTIFIER, true, USER),
|
||||
Set.of(new PackageAndUser(TARGET, USER)));
|
||||
assertState(STATE_ENABLED, IDENTIFIER, USER);
|
||||
|
||||
// target upgrades do not change the state of the overlay
|
||||
upgradePackage(target, USER);
|
||||
assertState(STATE_ENABLED, OVERLAY, USER);
|
||||
assertState(STATE_ENABLED, IDENTIFIER, USER);
|
||||
|
||||
uninstallPackage(TARGET, USER);
|
||||
assertState(STATE_MISSING_TARGET, OVERLAY, USER);
|
||||
assertState(STATE_MISSING_TARGET, IDENTIFIER, USER);
|
||||
|
||||
installNewPackage(target, USER);
|
||||
assertState(STATE_ENABLED, OVERLAY, USER);
|
||||
installPackage(target, USER);
|
||||
assertState(STATE_ENABLED, IDENTIFIER, USER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnOverlayPackageUpgraded() throws Exception {
|
||||
final FakeDeviceState.PackageBuilder target = target(TARGET);
|
||||
final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
|
||||
installNewPackage(target, USER);
|
||||
installNewPackage(overlay, USER);
|
||||
installPackage(target, USER);
|
||||
installPackage(overlay, USER);
|
||||
upgradePackage(overlay, USER);
|
||||
|
||||
// upgrade to a version where the overlay has changed its target
|
||||
final FakeDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target");
|
||||
final Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> pair =
|
||||
upgradePackage(overlay2, USER);
|
||||
assertEquals(pair.first, Optional.of(new PackageAndUser(TARGET, USER)));
|
||||
assertEquals(pair.second, Optional.of(new PackageAndUser("some.other.target", USER)));
|
||||
final Pair<Set<PackageAndUser>, Set<PackageAndUser>> pair = upgradePackage(overlay2, USER);
|
||||
assertEquals(pair.first, Set.of(new PackageAndUser(TARGET, USER)));
|
||||
assertEquals(
|
||||
Set.of(new PackageAndUser(TARGET, USER),
|
||||
new PackageAndUser("some.other.target", USER)),
|
||||
pair.second);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetEnabledAtVariousConditions() throws Exception {
|
||||
final OverlayManagerServiceImpl impl = getImpl();
|
||||
assertThrows(OverlayManagerServiceImpl.OperationFailedException.class,
|
||||
() -> impl.setEnabled(OVERLAY, true, USER));
|
||||
() -> impl.setEnabled(IDENTIFIER, true, USER));
|
||||
|
||||
// request succeeded, and there was a change that needs to be
|
||||
// propagated to the rest of the system
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET), USER);
|
||||
assertEquals(impl.setEnabled(OVERLAY, true, USER),
|
||||
Optional.of(new PackageAndUser(TARGET, USER)));
|
||||
installPackage(target(TARGET), USER);
|
||||
installPackage(overlay(OVERLAY, TARGET), USER);
|
||||
assertEquals(impl.setEnabled(IDENTIFIER, true, USER),
|
||||
Set.of(new PackageAndUser(TARGET, USER)));
|
||||
|
||||
// request succeeded, but nothing changed
|
||||
assertFalse(impl.setEnabled(OVERLAY, true, USER).isPresent());
|
||||
assertTrue(impl.setEnabled(IDENTIFIER, true, USER).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -214,8 +221,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
reinitializeImpl();
|
||||
|
||||
addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER);
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_OK), USER);
|
||||
installPackage(target(TARGET), USER);
|
||||
installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_OK), USER);
|
||||
|
||||
final FakeIdmapDaemon idmapd = getIdmapd();
|
||||
final FakeDeviceState state = getState();
|
||||
@@ -232,8 +239,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
reinitializeImpl();
|
||||
|
||||
addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER);
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
|
||||
installPackage(target(TARGET), USER);
|
||||
installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
|
||||
|
||||
final FakeIdmapDaemon idmapd = getIdmapd();
|
||||
final FakeDeviceState state = getState();
|
||||
@@ -247,8 +254,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
@Test
|
||||
public void testConfigSignaturePolicyNoConfig() throws Exception {
|
||||
addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER);
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
|
||||
installPackage(target(TARGET), USER);
|
||||
installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
|
||||
|
||||
final FakeIdmapDaemon idmapd = getIdmapd();
|
||||
final FakeDeviceState state = getState();
|
||||
@@ -261,8 +268,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
|
||||
@Test
|
||||
public void testConfigSignaturePolicyNoRefPkg() throws Exception {
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
|
||||
installPackage(target(TARGET), USER);
|
||||
installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
|
||||
|
||||
final FakeIdmapDaemon idmapd = getIdmapd();
|
||||
final FakeDeviceState state = getState();
|
||||
@@ -279,8 +286,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
|
||||
reinitializeImpl();
|
||||
|
||||
addPackage(app(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER);
|
||||
installNewPackage(target(TARGET), USER);
|
||||
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
|
||||
installPackage(target(TARGET), USER);
|
||||
installPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
|
||||
|
||||
final FakeIdmapDaemon idmapd = getIdmapd();
|
||||
final FakeDeviceState state = getState();
|
||||
|
||||
@@ -24,11 +24,12 @@ import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.om.OverlayIdentifier;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.content.om.OverlayInfo.State;
|
||||
import android.content.om.OverlayableInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.os.FabricatedOverlayInfo;
|
||||
import android.os.FabricatedOverlayInternal;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
@@ -37,17 +38,19 @@ import android.util.Pair;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.content.om.OverlayConfig;
|
||||
import com.android.internal.util.CollectionUtils;
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Set;
|
||||
|
||||
/** Base class for creating {@link OverlayManagerServiceImplTests} tests. */
|
||||
class OverlayManagerServiceImplTestsBase {
|
||||
@@ -94,12 +97,11 @@ class OverlayManagerServiceImplTestsBase {
|
||||
mConfigSignaturePackageName = packageName;
|
||||
}
|
||||
|
||||
void assertState(@State int expected, final String overlayPackageName, int userId) {
|
||||
final OverlayInfo info = mImpl.getOverlayInfo(overlayPackageName, userId);
|
||||
void assertState(@State int expected, final OverlayIdentifier overlay, int userId) {
|
||||
final OverlayInfo info = mImpl.getOverlayInfo(overlay, userId);
|
||||
if (info == null) {
|
||||
throw new IllegalStateException("package not installed");
|
||||
throw new IllegalStateException("overlay '" + overlay + "' not installed");
|
||||
}
|
||||
|
||||
final String msg = String.format("expected %s but was %s:",
|
||||
OverlayInfo.stateToString(expected), OverlayInfo.stateToString(info.state));
|
||||
assertEquals(msg, expected, info.state);
|
||||
@@ -152,17 +154,13 @@ class OverlayManagerServiceImplTestsBase {
|
||||
*
|
||||
* @throws IllegalStateException if the package is currently installed
|
||||
*/
|
||||
void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId)
|
||||
Set<PackageAndUser> installPackage(FakeDeviceState.PackageBuilder pkg, int userId)
|
||||
throws OperationFailedException {
|
||||
if (mState.select(pkg.packageName, userId) != null) {
|
||||
throw new IllegalStateException("package " + pkg.packageName + " already installed");
|
||||
}
|
||||
mState.add(pkg, userId);
|
||||
if (pkg.targetPackage == null) {
|
||||
mImpl.onTargetPackageAdded(pkg.packageName, userId);
|
||||
} else {
|
||||
mImpl.onOverlayPackageAdded(pkg.packageName, userId);
|
||||
}
|
||||
return CollectionUtils.emptyIfNull(mImpl.onPackageAdded(pkg.packageName, userId));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,26 +176,21 @@ class OverlayManagerServiceImplTestsBase {
|
||||
*
|
||||
* @throws IllegalStateException if the package is not currently installed
|
||||
*/
|
||||
Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> upgradePackage(
|
||||
Pair<Set<PackageAndUser>, Set<PackageAndUser>> upgradePackage(
|
||||
FakeDeviceState.PackageBuilder pkg, int userId) throws OperationFailedException {
|
||||
final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId);
|
||||
if (replacedPackage == null) {
|
||||
throw new IllegalStateException("package " + pkg.packageName + " not installed");
|
||||
}
|
||||
Optional<PackageAndUser> opt1 = Optional.empty();
|
||||
if (replacedPackage.targetPackageName != null) {
|
||||
opt1 = mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
|
||||
}
|
||||
|
||||
final Set<PackageAndUser> updatedPackages1 =
|
||||
CollectionUtils.emptyIfNull(mImpl.onPackageReplacing(pkg.packageName, userId));
|
||||
|
||||
mState.add(pkg, userId);
|
||||
Optional<PackageAndUser> opt2;
|
||||
if (pkg.targetPackage == null) {
|
||||
opt2 = mImpl.onTargetPackageReplaced(pkg.packageName, userId);
|
||||
} else {
|
||||
opt2 = mImpl.onOverlayPackageReplaced(pkg.packageName, userId);
|
||||
}
|
||||
final Set<PackageAndUser> updatedPackages2 =
|
||||
CollectionUtils.emptyIfNull(mImpl.onPackageReplaced(pkg.packageName, userId));
|
||||
|
||||
return Pair.create(opt1, opt2);
|
||||
return Pair.create(updatedPackages1, updatedPackages2);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,17 +201,13 @@ class OverlayManagerServiceImplTestsBase {
|
||||
*
|
||||
* @throws IllegalStateException if the package is not currently installed
|
||||
*/
|
||||
void uninstallPackage(String packageName, int userId) throws OperationFailedException {
|
||||
Set<PackageAndUser> uninstallPackage(String packageName, int userId) {
|
||||
final FakeDeviceState.Package pkg = mState.select(packageName, userId);
|
||||
if (pkg == null) {
|
||||
throw new IllegalStateException("package " + packageName+ " not installed");
|
||||
}
|
||||
mState.remove(pkg.packageName);
|
||||
if (pkg.targetPackageName == null) {
|
||||
mImpl.onTargetPackageRemoved(pkg.packageName, userId);
|
||||
} else {
|
||||
mImpl.onOverlayPackageRemoved(pkg.packageName, userId);
|
||||
}
|
||||
return CollectionUtils.emptyIfNull(mImpl.onPackageRemoved(packageName, userId));
|
||||
}
|
||||
|
||||
/** Represents the state of packages installed on a fake device. */
|
||||
@@ -247,11 +236,6 @@ class OverlayManagerServiceImplTestsBase {
|
||||
}
|
||||
}
|
||||
|
||||
List<Package> select(int userId) {
|
||||
return mPackages.values().stream().filter(p -> p.installedUserIds.contains(userId))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
Package select(String packageName, int userId) {
|
||||
final Package pkg = mPackages.get(packageName);
|
||||
return pkg != null && pkg.installedUserIds.contains(userId) ? pkg : null;
|
||||
@@ -335,6 +319,21 @@ class OverlayManagerServiceImplTestsBase {
|
||||
this.apkPath = apkPath;
|
||||
this.certificate = certificate;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private AndroidPackage getPackageForUser(int user) {
|
||||
if (!installedUserIds.contains(user)) {
|
||||
return null;
|
||||
}
|
||||
final AndroidPackage pkg = Mockito.mock(AndroidPackage.class);
|
||||
when(pkg.getPackageName()).thenReturn(packageName);
|
||||
when(pkg.getBaseApkPath()).thenReturn(apkPath);
|
||||
when(pkg.getLongVersionCode()).thenReturn((long) versionCode);
|
||||
when(pkg.getOverlayTarget()).thenReturn(targetPackageName);
|
||||
when(pkg.getOverlayTargetName()).thenReturn(targetOverlayableName);
|
||||
when(pkg.getOverlayCategory()).thenReturn("Fake-category-" + targetPackageName);
|
||||
return pkg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,21 +344,29 @@ class OverlayManagerServiceImplTestsBase {
|
||||
mState = state;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PackageInfo getPackageInfo(@NonNull String packageName, int userId) {
|
||||
final FakeDeviceState.Package pkg = mState.select(packageName, userId);
|
||||
if (pkg == null) {
|
||||
return null;
|
||||
}
|
||||
final ApplicationInfo ai = new ApplicationInfo();
|
||||
ai.sourceDir = pkg.apkPath;
|
||||
PackageInfo pi = new PackageInfo();
|
||||
pi.applicationInfo = ai;
|
||||
pi.packageName = pkg.packageName;
|
||||
pi.overlayTarget = pkg.targetPackageName;
|
||||
pi.targetOverlayableName = pkg.targetOverlayableName;
|
||||
pi.overlayCategory = "Fake-category-" + pkg.targetPackageName;
|
||||
return pi;
|
||||
public ArrayMap<String, AndroidPackage> initializeForUser(int userId) {
|
||||
final ArrayMap<String, AndroidPackage> packages = new ArrayMap<>();
|
||||
mState.mPackages.forEach((key, value) -> {
|
||||
final AndroidPackage pkg = value.getPackageForUser(userId);
|
||||
if (pkg != null) {
|
||||
packages.put(key, pkg);
|
||||
}
|
||||
});
|
||||
return packages;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public AndroidPackage getPackageForUser(@NonNull String packageName, int userId) {
|
||||
final FakeDeviceState.Package pkgState = mState.select(packageName, userId);
|
||||
return pkgState == null ? null : pkgState.getPackageForUser(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstantApp(@NonNull String packageName, int userId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -370,14 +377,6 @@ class OverlayManagerServiceImplTestsBase {
|
||||
return pkg1 != null && pkg2 != null && pkg1.certificate.equals(pkg2.certificate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PackageInfo> getOverlayPackages(int userId) {
|
||||
return mState.select(userId).stream()
|
||||
.filter(p -> p.targetPackageName != null)
|
||||
.map(p -> getPackageInfo(p.packageName, userId))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getConfigSignaturePackage() {
|
||||
return mConfigSignaturePackageName;
|
||||
@@ -421,6 +420,9 @@ class OverlayManagerServiceImplTestsBase {
|
||||
static class FakeIdmapDaemon extends IdmapDaemon {
|
||||
private final FakeDeviceState mState;
|
||||
private final ArrayMap<String, IdmapHeader> mIdmapFiles = new ArrayMap<>();
|
||||
private final ArrayMap<String, FabricatedOverlayInfo> mFabricatedOverlays =
|
||||
new ArrayMap<>();
|
||||
private int mFabricatedAssetSeq = 0;
|
||||
|
||||
FakeIdmapDaemon(FakeDeviceState state) {
|
||||
this.mState = state;
|
||||
@@ -433,10 +435,10 @@ class OverlayManagerServiceImplTestsBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
String createIdmap(String targetPath, String overlayPath, int policies, boolean enforce,
|
||||
int userId) {
|
||||
String createIdmap(String targetPath, String overlayPath, String overlayName,
|
||||
int policies, boolean enforce, int userId) {
|
||||
mIdmapFiles.put(overlayPath, new IdmapHeader(getCrc(targetPath),
|
||||
getCrc(overlayPath), targetPath, policies, enforce));
|
||||
getCrc(overlayPath), targetPath, overlayName, policies, enforce));
|
||||
return overlayPath;
|
||||
}
|
||||
|
||||
@@ -446,8 +448,8 @@ class OverlayManagerServiceImplTestsBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean verifyIdmap(String targetPath, String overlayPath, int policies, boolean enforce,
|
||||
int userId) {
|
||||
boolean verifyIdmap(String targetPath, String overlayPath, String overlayName, int policies,
|
||||
boolean enforce, int userId) {
|
||||
final IdmapHeader idmap = mIdmapFiles.get(overlayPath);
|
||||
if (idmap == null) {
|
||||
return false;
|
||||
@@ -461,6 +463,29 @@ class OverlayManagerServiceImplTestsBase {
|
||||
return mIdmapFiles.containsKey(overlayPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
FabricatedOverlayInfo createFabricatedOverlay(@NonNull FabricatedOverlayInternal overlay) {
|
||||
final String path = Integer.toString(mFabricatedAssetSeq++);
|
||||
final FabricatedOverlayInfo info = new FabricatedOverlayInfo();
|
||||
info.path = path;
|
||||
info.overlayName = overlay.overlayName;
|
||||
info.packageName = overlay.packageName;
|
||||
info.targetPackageName = overlay.targetPackageName;
|
||||
info.targetOverlayable = overlay.targetOverlayable;
|
||||
mFabricatedOverlays.put(path, info);
|
||||
return info;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean deleteFabricatedOverlay(@NonNull String path) {
|
||||
return mFabricatedOverlays.remove(path) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
List<FabricatedOverlayInfo> getFabricatedOverlayInfos() {
|
||||
return new ArrayList<>(mFabricatedOverlays.values());
|
||||
}
|
||||
|
||||
IdmapHeader getIdmap(String overlayPath) {
|
||||
return mIdmapFiles.get(overlayPath);
|
||||
}
|
||||
@@ -469,14 +494,16 @@ class OverlayManagerServiceImplTestsBase {
|
||||
private final int targetCrc;
|
||||
private final int overlayCrc;
|
||||
final String targetPath;
|
||||
final String overlayName;
|
||||
final int policies;
|
||||
final boolean enforceOverlayable;
|
||||
|
||||
private IdmapHeader(int targetCrc, int overlayCrc, String targetPath, int policies,
|
||||
boolean enforceOverlayable) {
|
||||
private IdmapHeader(int targetCrc, int overlayCrc, String targetPath,
|
||||
String overlayName, int policies, boolean enforceOverlayable) {
|
||||
this.targetCrc = targetCrc;
|
||||
this.overlayCrc = overlayCrc;
|
||||
this.targetPath = targetPath;
|
||||
this.overlayName = overlayName;
|
||||
this.policies = policies;
|
||||
this.enforceOverlayable = enforceOverlayable;
|
||||
}
|
||||
|
||||
@@ -19,17 +19,20 @@ package com.android.server.om;
|
||||
import static android.content.om.OverlayInfo.STATE_DISABLED;
|
||||
import static android.content.om.OverlayInfo.STATE_ENABLED;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.content.om.OverlayIdentifier;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedXmlPullParser;
|
||||
import android.util.Xml;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -43,67 +46,32 @@ import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class OverlayManagerSettingsTests {
|
||||
private OverlayManagerSettings mSettings;
|
||||
private static int USER_0 = 0;
|
||||
private static int USER_1 = 1;
|
||||
|
||||
private static final OverlayInfo OVERLAY_A0 = new OverlayInfo(
|
||||
"com.test.overlay_a",
|
||||
"com.test.target",
|
||||
null,
|
||||
"some-category",
|
||||
"/data/app/com.test.overlay_a-1/base.apk",
|
||||
STATE_DISABLED,
|
||||
0,
|
||||
0,
|
||||
true);
|
||||
private static OverlayIdentifier OVERLAY_A = new OverlayIdentifier("com.test.overlay_a",
|
||||
null /* overlayName */);
|
||||
private static OverlayIdentifier OVERLAY_B = new OverlayIdentifier("com.test.overlay_b",
|
||||
null /* overlayName */);
|
||||
private static OverlayIdentifier OVERLAY_C = new OverlayIdentifier("com.test.overlay_c",
|
||||
null /* overlayName */);
|
||||
|
||||
private static final OverlayInfo OVERLAY_B0 = new OverlayInfo(
|
||||
"com.test.overlay_b",
|
||||
"com.test.target",
|
||||
null,
|
||||
"some-category",
|
||||
"/data/app/com.test.overlay_b-1/base.apk",
|
||||
STATE_DISABLED,
|
||||
0,
|
||||
0,
|
||||
true);
|
||||
private static final OverlayInfo OVERLAY_A_USER0 = createInfo(OVERLAY_A, USER_0);
|
||||
private static final OverlayInfo OVERLAY_B_USER0 = createInfo(OVERLAY_B, USER_0);
|
||||
private static final OverlayInfo OVERLAY_C_USER0 = createInfo(OVERLAY_C, USER_0);
|
||||
|
||||
private static final OverlayInfo OVERLAY_C0 = new OverlayInfo(
|
||||
"com.test.overlay_c",
|
||||
"com.test.target",
|
||||
null,
|
||||
"some-category",
|
||||
"/data/app/com.test.overlay_c-1/base.apk",
|
||||
STATE_DISABLED,
|
||||
0,
|
||||
0,
|
||||
true);
|
||||
private static final OverlayInfo OVERLAY_A_USER1 = createInfo(OVERLAY_A, USER_1);
|
||||
private static final OverlayInfo OVERLAY_B_USER1 = createInfo(OVERLAY_B, USER_1);
|
||||
|
||||
private static final OverlayInfo OVERLAY_A1 = new OverlayInfo(
|
||||
"com.test.overlay_a",
|
||||
"com.test.target",
|
||||
null,
|
||||
"some-category",
|
||||
"/data/app/com.test.overlay_a-1/base.apk",
|
||||
STATE_DISABLED,
|
||||
1,
|
||||
0,
|
||||
true);
|
||||
|
||||
private static final OverlayInfo OVERLAY_B1 = new OverlayInfo(
|
||||
"com.test.overlay_b",
|
||||
"com.test.target",
|
||||
null,
|
||||
"some-category",
|
||||
"/data/app/com.test.overlay_b-1/base.apk",
|
||||
STATE_DISABLED,
|
||||
1,
|
||||
0,
|
||||
true);
|
||||
private static final String TARGET_PACKAGE = "com.test.target";
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@@ -114,124 +82,112 @@ public class OverlayManagerSettingsTests {
|
||||
|
||||
@Test
|
||||
public void testSettingsInitiallyEmpty() throws Exception {
|
||||
final int userId = 0;
|
||||
Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(userId);
|
||||
final Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(0 /* userId */);
|
||||
assertEquals(0, map.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicSetAndGet() throws Exception {
|
||||
assertDoesNotContain(mSettings, OVERLAY_A0.packageName, OVERLAY_A0.userId);
|
||||
assertDoesNotContain(mSettings, OVERLAY_A_USER0);
|
||||
|
||||
insert(OVERLAY_A0);
|
||||
assertContains(mSettings, OVERLAY_A0);
|
||||
OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_A0.packageName, OVERLAY_A0.userId);
|
||||
assertEquals(OVERLAY_A0, oi);
|
||||
insertSetting(OVERLAY_A_USER0);
|
||||
assertContains(mSettings, OVERLAY_A_USER0);
|
||||
final OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_A, USER_0);
|
||||
assertEquals(OVERLAY_A_USER0, oi);
|
||||
|
||||
assertTrue(mSettings.remove(OVERLAY_A0.packageName, OVERLAY_A0.userId));
|
||||
assertDoesNotContain(mSettings, OVERLAY_A0.packageName, OVERLAY_A0.userId);
|
||||
assertTrue(mSettings.remove(OVERLAY_A, USER_0));
|
||||
assertDoesNotContain(mSettings, OVERLAY_A, USER_0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUsers() throws Exception {
|
||||
int[] users = mSettings.getUsers();
|
||||
assertEquals(0, users.length);
|
||||
assertArrayEquals(new int[]{}, mSettings.getUsers());
|
||||
|
||||
insert(OVERLAY_A0);
|
||||
users = mSettings.getUsers();
|
||||
assertEquals(1, users.length);
|
||||
assertContains(users, OVERLAY_A0.userId);
|
||||
insertSetting(OVERLAY_A_USER0);
|
||||
assertArrayEquals(new int[]{USER_0}, mSettings.getUsers());
|
||||
|
||||
insert(OVERLAY_A1);
|
||||
insert(OVERLAY_B1);
|
||||
users = mSettings.getUsers();
|
||||
assertEquals(2, users.length);
|
||||
assertContains(users, OVERLAY_A0.userId);
|
||||
assertContains(users, OVERLAY_A1.userId);
|
||||
insertSetting(OVERLAY_A_USER1);
|
||||
insertSetting(OVERLAY_B_USER1);
|
||||
assertArrayEquals(new int[]{USER_0, USER_1}, mSettings.getUsers());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOverlaysForUser() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_B0);
|
||||
insert(OVERLAY_A1);
|
||||
insert(OVERLAY_B1);
|
||||
insertSetting(OVERLAY_A_USER0);
|
||||
insertSetting(OVERLAY_B_USER0);
|
||||
insertSetting(OVERLAY_A_USER1);
|
||||
insertSetting(OVERLAY_B_USER0);
|
||||
|
||||
Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(OVERLAY_A0.userId);
|
||||
assertEquals(1, map.keySet().size());
|
||||
assertTrue(map.keySet().contains(OVERLAY_A0.targetPackageName));
|
||||
final Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(USER_0);
|
||||
assertEquals(Set.of(TARGET_PACKAGE), map.keySet());
|
||||
|
||||
List<OverlayInfo> list = map.get(OVERLAY_A0.targetPackageName);
|
||||
assertEquals(2, list.size());
|
||||
assertTrue(list.contains(OVERLAY_A0));
|
||||
assertTrue(list.contains(OVERLAY_B0));
|
||||
// Two overlays in user 0 target the same package
|
||||
final List<OverlayInfo> list = map.get(TARGET_PACKAGE);
|
||||
assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0), list);
|
||||
|
||||
// getOverlaysForUser should never return null
|
||||
map = mSettings.getOverlaysForUser(-1);
|
||||
assertNotNull(map);
|
||||
assertEquals(0, map.size());
|
||||
// No users installed for user 3
|
||||
assertEquals(Map.<String, List<OverlayInfo>>of(), mSettings.getOverlaysForUser(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveUser() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_B0);
|
||||
insert(OVERLAY_A1);
|
||||
insertSetting(OVERLAY_A_USER0);
|
||||
insertSetting(OVERLAY_B_USER0);
|
||||
insertSetting(OVERLAY_A_USER1);
|
||||
|
||||
assertContains(mSettings, OVERLAY_A0);
|
||||
assertContains(mSettings, OVERLAY_B0);
|
||||
assertContains(mSettings, OVERLAY_A1);
|
||||
assertContains(mSettings, OVERLAY_A_USER0);
|
||||
assertContains(mSettings, OVERLAY_B_USER0);
|
||||
assertContains(mSettings, OVERLAY_A_USER1);
|
||||
|
||||
mSettings.removeUser(OVERLAY_A0.userId);
|
||||
mSettings.removeUser(USER_0);
|
||||
|
||||
assertDoesNotContain(mSettings, OVERLAY_A0);
|
||||
assertDoesNotContain(mSettings, OVERLAY_B0);
|
||||
assertContains(mSettings, OVERLAY_A1);
|
||||
assertDoesNotContain(mSettings, OVERLAY_A_USER0);
|
||||
assertDoesNotContain(mSettings, OVERLAY_B_USER0);
|
||||
assertContains(mSettings, OVERLAY_A_USER1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrderOfNewlyAddedItems() throws Exception {
|
||||
// new items are appended to the list
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_B0);
|
||||
insert(OVERLAY_C0);
|
||||
insertSetting(OVERLAY_A_USER0);
|
||||
insertSetting(OVERLAY_B_USER0);
|
||||
insertSetting(OVERLAY_C_USER0);
|
||||
|
||||
List<OverlayInfo> list =
|
||||
mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0);
|
||||
assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0),
|
||||
mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
|
||||
|
||||
// overlays keep their positions when updated
|
||||
mSettings.setState(OVERLAY_B0.packageName, OVERLAY_B0.userId, STATE_ENABLED);
|
||||
OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_B0.packageName, OVERLAY_B0.userId);
|
||||
mSettings.setState(OVERLAY_B, USER_0, STATE_ENABLED);
|
||||
final OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_B, USER_0);
|
||||
assertNotNull(oi);
|
||||
|
||||
list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_A0, oi, OVERLAY_C0);
|
||||
assertListsAreEqual(List.of(OVERLAY_A_USER0, oi, OVERLAY_C_USER0),
|
||||
mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetPriority() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_B0);
|
||||
insert(OVERLAY_C0);
|
||||
insertSetting(OVERLAY_A_USER0);
|
||||
insertSetting(OVERLAY_B_USER0);
|
||||
insertSetting(OVERLAY_C_USER0);
|
||||
|
||||
List<OverlayInfo> list =
|
||||
mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0);
|
||||
assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0),
|
||||
mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
|
||||
|
||||
boolean changed = mSettings.setPriority(OVERLAY_B0.packageName, OVERLAY_C0.packageName,
|
||||
OVERLAY_B0.userId);
|
||||
assertTrue(changed);
|
||||
list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0);
|
||||
assertTrue(mSettings.setPriority(OVERLAY_B, OVERLAY_C, USER_0));
|
||||
assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_C_USER0, OVERLAY_B_USER0),
|
||||
mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
|
||||
|
||||
changed =
|
||||
mSettings.setPriority(OVERLAY_B0.packageName, "does.not.exist", OVERLAY_B0.userId);
|
||||
assertFalse(changed);
|
||||
list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0);
|
||||
// Nothing happens if the parent package cannot be found
|
||||
assertFalse(mSettings.setPriority(OVERLAY_B, new OverlayIdentifier("does.not.exist"),
|
||||
USER_0));
|
||||
assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_C_USER0, OVERLAY_B_USER0),
|
||||
mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
|
||||
|
||||
OverlayInfo otherTarget = new OverlayInfo(
|
||||
// An overlay should not affect the priority of overlays targeting a different package
|
||||
final OverlayInfo otherTarget = new OverlayInfo(
|
||||
"com.test.overlay_other",
|
||||
null,
|
||||
"com.test.some.other.target",
|
||||
null,
|
||||
"some-category",
|
||||
@@ -239,45 +195,36 @@ public class OverlayManagerSettingsTests {
|
||||
STATE_DISABLED,
|
||||
0,
|
||||
0,
|
||||
true);
|
||||
insert(otherTarget);
|
||||
changed = mSettings.setPriority(OVERLAY_A0.packageName, otherTarget.packageName,
|
||||
OVERLAY_A0.userId);
|
||||
assertFalse(changed);
|
||||
true,
|
||||
false);
|
||||
insertSetting(otherTarget);
|
||||
assertFalse(mSettings.setPriority(OVERLAY_A, otherTarget.getOverlayIdentifier(), USER_0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetLowestPriority() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_B0);
|
||||
insert(OVERLAY_C0);
|
||||
insertSetting(OVERLAY_A_USER0);
|
||||
insertSetting(OVERLAY_B_USER0);
|
||||
insertSetting(OVERLAY_C_USER0);
|
||||
assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0),
|
||||
mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
|
||||
|
||||
List<OverlayInfo> list =
|
||||
mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0);
|
||||
|
||||
boolean changed = mSettings.setLowestPriority(OVERLAY_B0.packageName, OVERLAY_B0.userId);
|
||||
assertTrue(changed);
|
||||
|
||||
list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_B0, OVERLAY_A0, OVERLAY_C0);
|
||||
assertTrue(mSettings.setLowestPriority(OVERLAY_B, USER_0));
|
||||
assertListsAreEqual(List.of(OVERLAY_B_USER0, OVERLAY_A_USER0, OVERLAY_C_USER0),
|
||||
mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetHighestPriority() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_B0);
|
||||
insert(OVERLAY_C0);
|
||||
insertSetting(OVERLAY_A_USER0);
|
||||
insertSetting(OVERLAY_B_USER0);
|
||||
insertSetting(OVERLAY_C_USER0);
|
||||
assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0, OVERLAY_C_USER0),
|
||||
mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
|
||||
|
||||
List<OverlayInfo> list =
|
||||
mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0);
|
||||
|
||||
boolean changed = mSettings.setHighestPriority(OVERLAY_B0.packageName, OVERLAY_B0.userId);
|
||||
assertTrue(changed);
|
||||
|
||||
list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0);
|
||||
assertTrue(mSettings.setHighestPriority(OVERLAY_B, USER_0));
|
||||
assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_C_USER0, OVERLAY_B_USER0),
|
||||
mSettings.getOverlaysForTarget(OVERLAY_A_USER0.targetPackageName, USER_0));
|
||||
}
|
||||
|
||||
// tests: persist and restore
|
||||
@@ -294,8 +241,8 @@ public class OverlayManagerSettingsTests {
|
||||
|
||||
@Test
|
||||
public void testPersistDifferentOverlaysSameUser() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_B0);
|
||||
insertSetting(OVERLAY_A_USER0);
|
||||
insertSetting(OVERLAY_B_USER0);
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
mSettings.persist(os);
|
||||
@@ -304,17 +251,17 @@ public class OverlayManagerSettingsTests {
|
||||
assertEquals(1, countXmlTags(xml, "overlays"));
|
||||
assertEquals(2, countXmlTags(xml, "item"));
|
||||
assertEquals(1, countXmlAttributesWhere(xml, "item", "packageName",
|
||||
OVERLAY_A0.packageName));
|
||||
OVERLAY_A.getPackageName()));
|
||||
assertEquals(1, countXmlAttributesWhere(xml, "item", "packageName",
|
||||
OVERLAY_B0.packageName));
|
||||
OVERLAY_B.getPackageName()));
|
||||
assertEquals(2, countXmlAttributesWhere(xml, "item", "userId",
|
||||
Integer.toString(OVERLAY_A0.userId)));
|
||||
Integer.toString(USER_0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersistSameOverlayDifferentUsers() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_A1);
|
||||
insertSetting(OVERLAY_A_USER0);
|
||||
insertSetting(OVERLAY_A_USER1);
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
mSettings.persist(os);
|
||||
@@ -323,17 +270,17 @@ public class OverlayManagerSettingsTests {
|
||||
assertEquals(1, countXmlTags(xml, "overlays"));
|
||||
assertEquals(2, countXmlTags(xml, "item"));
|
||||
assertEquals(2, countXmlAttributesWhere(xml, "item", "packageName",
|
||||
OVERLAY_A0.packageName));
|
||||
OVERLAY_A.getPackageName()));
|
||||
assertEquals(1, countXmlAttributesWhere(xml, "item", "userId",
|
||||
Integer.toString(OVERLAY_A0.userId)));
|
||||
Integer.toString(USER_0)));
|
||||
assertEquals(1, countXmlAttributesWhere(xml, "item", "userId",
|
||||
Integer.toString(OVERLAY_A1.userId)));
|
||||
Integer.toString(USER_1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersistEnabled() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
mSettings.setEnabled(OVERLAY_A0.packageName, OVERLAY_A0.userId, true);
|
||||
insertSetting(OVERLAY_A_USER0);
|
||||
mSettings.setEnabled(OVERLAY_A, USER_0, true);
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
mSettings.persist(os);
|
||||
@@ -351,7 +298,7 @@ public class OverlayManagerSettingsTests {
|
||||
ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
|
||||
|
||||
mSettings.restore(is);
|
||||
assertDoesNotContain(mSettings, "com.test.overlay", 0);
|
||||
assertDoesNotContain(mSettings, new OverlayIdentifier("com.test.overlay"), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -361,6 +308,7 @@ public class OverlayManagerSettingsTests {
|
||||
"<?xml version='1.0' encoding='utf-8' standalone='yes'?>\n"
|
||||
+ "<overlays version='" + version + "'>\n"
|
||||
+ "<item packageName='com.test.overlay'\n"
|
||||
+ " overlayName='test'\n"
|
||||
+ " userId='1234'\n"
|
||||
+ " targetPackageName='com.test.target'\n"
|
||||
+ " baseCodePath='/data/app/com.test.overlay-1/base.apk'\n"
|
||||
@@ -373,20 +321,22 @@ public class OverlayManagerSettingsTests {
|
||||
ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
|
||||
|
||||
mSettings.restore(is);
|
||||
OverlayInfo oi = mSettings.getOverlayInfo("com.test.overlay", 1234);
|
||||
final OverlayIdentifier identifier = new OverlayIdentifier("com.test.overlay", "test");
|
||||
OverlayInfo oi = mSettings.getOverlayInfo(identifier, 1234);
|
||||
assertNotNull(oi);
|
||||
assertEquals("com.test.overlay", oi.packageName);
|
||||
assertEquals("test", oi.overlayName);
|
||||
assertEquals("com.test.target", oi.targetPackageName);
|
||||
assertEquals("/data/app/com.test.overlay-1/base.apk", oi.baseCodePath);
|
||||
assertEquals(1234, oi.userId);
|
||||
assertEquals(STATE_DISABLED, oi.state);
|
||||
assertFalse(mSettings.getEnabled("com.test.overlay", 1234));
|
||||
assertFalse(mSettings.getEnabled(identifier, 1234));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersistAndRestore() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_B1);
|
||||
insertSetting(OVERLAY_A_USER0);
|
||||
insertSetting(OVERLAY_B_USER1);
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
mSettings.persist(os);
|
||||
@@ -394,11 +344,11 @@ public class OverlayManagerSettingsTests {
|
||||
OverlayManagerSettings newSettings = new OverlayManagerSettings();
|
||||
newSettings.restore(is);
|
||||
|
||||
OverlayInfo a = newSettings.getOverlayInfo(OVERLAY_A0.packageName, OVERLAY_A0.userId);
|
||||
assertEquals(OVERLAY_A0, a);
|
||||
OverlayInfo a = newSettings.getOverlayInfo(OVERLAY_A, USER_0);
|
||||
assertEquals(OVERLAY_A_USER0, a);
|
||||
|
||||
OverlayInfo b = newSettings.getOverlayInfo(OVERLAY_B1.packageName, OVERLAY_B1.userId);
|
||||
assertEquals(OVERLAY_B1, b);
|
||||
OverlayInfo b = newSettings.getOverlayInfo(OVERLAY_B, USER_1);
|
||||
assertEquals(OVERLAY_B_USER1, b);
|
||||
}
|
||||
|
||||
private int countXmlTags(InputStream in, String tagToLookFor) throws Exception {
|
||||
@@ -433,43 +383,53 @@ public class OverlayManagerSettingsTests {
|
||||
return count;
|
||||
}
|
||||
|
||||
private void insert(OverlayInfo oi) throws Exception {
|
||||
mSettings.init(oi.packageName, oi.userId, oi.targetPackageName, null, oi.baseCodePath,
|
||||
true, false,0, oi.category);
|
||||
mSettings.setState(oi.packageName, oi.userId, oi.state);
|
||||
mSettings.setEnabled(oi.packageName, oi.userId, false);
|
||||
private void insertSetting(OverlayInfo oi) throws Exception {
|
||||
mSettings.init(oi.getOverlayIdentifier(), oi.userId, oi.targetPackageName, null,
|
||||
oi.baseCodePath, true, false,0, oi.category, oi.isFabricated);
|
||||
mSettings.setState(oi.getOverlayIdentifier(), oi.userId, oi.state);
|
||||
mSettings.setEnabled(oi.getOverlayIdentifier(), oi.userId, false);
|
||||
}
|
||||
|
||||
private static void assertContains(final OverlayManagerSettings settings,
|
||||
final OverlayInfo oi) {
|
||||
assertContains(settings, oi.packageName, oi.userId);
|
||||
}
|
||||
|
||||
private static void assertContains(final OverlayManagerSettings settings,
|
||||
final String packageName, int userId) {
|
||||
try {
|
||||
settings.getOverlayInfo(packageName, userId);
|
||||
settings.getOverlayInfo(oi.getOverlayIdentifier(), oi.userId);
|
||||
} catch (OverlayManagerSettings.BadKeyException e) {
|
||||
fail(String.format("settings does not contain packageName=%s userId=%d",
|
||||
packageName, userId));
|
||||
fail(String.format("settings does not contain overlay=%s userId=%d",
|
||||
oi.getOverlayIdentifier(), oi.userId));
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertDoesNotContain(final OverlayManagerSettings settings,
|
||||
final OverlayInfo oi) {
|
||||
assertDoesNotContain(settings, oi.packageName, oi.userId);
|
||||
assertDoesNotContain(settings, oi.getOverlayIdentifier(), oi.userId);
|
||||
}
|
||||
|
||||
private static void assertDoesNotContain(final OverlayManagerSettings settings,
|
||||
final String packageName, int userId) {
|
||||
final OverlayIdentifier overlay, int userId) {
|
||||
try {
|
||||
settings.getOverlayInfo(packageName, userId);
|
||||
fail(String.format("settings contains packageName=%s userId=%d", packageName, userId));
|
||||
settings.getOverlayInfo(overlay, userId);
|
||||
fail(String.format("settings contains overlay=%s userId=%d", overlay, userId));
|
||||
} catch (OverlayManagerSettings.BadKeyException e) {
|
||||
// do nothing: we expect to end up here
|
||||
}
|
||||
}
|
||||
|
||||
private static OverlayInfo createInfo(@NonNull OverlayIdentifier identifier, int userId) {
|
||||
return new OverlayInfo(
|
||||
identifier.getPackageName(),
|
||||
identifier.getOverlayName(),
|
||||
"com.test.target",
|
||||
null,
|
||||
"some-category",
|
||||
"/data/app/" + identifier + "/base.apk",
|
||||
STATE_DISABLED,
|
||||
userId,
|
||||
0,
|
||||
true,
|
||||
false);
|
||||
}
|
||||
|
||||
private static void assertContains(int[] haystack, int needle) {
|
||||
List<Integer> list = IntStream.of(haystack)
|
||||
.boxed()
|
||||
@@ -490,16 +450,11 @@ public class OverlayManagerSettingsTests {
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertListsAreEqual(List<OverlayInfo> list, OverlayInfo... array) {
|
||||
List<OverlayInfo> other = Stream.of(array)
|
||||
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
|
||||
assertListsAreEqual(list, other);
|
||||
}
|
||||
|
||||
private static void assertListsAreEqual(List<OverlayInfo> list, List<OverlayInfo> other) {
|
||||
if (!list.equals(other)) {
|
||||
private static void assertListsAreEqual(
|
||||
@NonNull List<OverlayInfo> expected, @Nullable List<OverlayInfo> actual) {
|
||||
if (!expected.equals(actual)) {
|
||||
fail(String.format("lists [%s] and [%s] differ",
|
||||
TextUtils.join(",", list), TextUtils.join(",", other)));
|
||||
TextUtils.join(",", expected), TextUtils.join(",", actual)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user