Add enforcement of idmap policies

Teaches idmap2 to recognize policy restrictions put on overlayable
resources. If overlayable enforcement is turned on for an overlay, then
any resources defined within the overlayable api of the target will have
policy restrictions imposed on them. All resources without overlayable
definitions will continue to be overlayable without policy restrictions.

Bug: 119390857
Test: atest idmap2 and booting

Co-authored-by: Ryan Mitchell <rtmitchell@google.com>
Change-Id: I7e435648eb6e4a87b0b90a7b2a0c3f33c1516ea6
This commit is contained in:
Mårten Kongstad
2019-01-07 17:26:25 -08:00
committed by Ryan Mitchell
parent f9d1062118
commit d10d06d0b0
33 changed files with 912 additions and 74 deletions

View File

@@ -38,6 +38,7 @@ cc_library {
"libidmap2/CommandLineOptions.cpp",
"libidmap2/FileUtils.cpp",
"libidmap2/Idmap.cpp",
"libidmap2/Policies.cpp",
"libidmap2/PrettyPrintVisitor.cpp",
"libidmap2/RawPrintVisitor.cpp",
"libidmap2/ResourceUtils.cpp",
@@ -87,6 +88,7 @@ cc_test {
"tests/Idmap2BinaryTests.cpp",
"tests/IdmapTests.cpp",
"tests/Main.cpp",
"tests/PoliciesTests.cpp",
"tests/PrettyPrintVisitorTests.cpp",
"tests/RawPrintVisitorTests.cpp",
"tests/ResourceUtilsTests.cpp",

View File

@@ -27,17 +27,25 @@
#include "idmap2/CommandLineOptions.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
#include "idmap2/Policies.h"
#include "idmap2/Result.h"
using android::ApkAssets;
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::CommandLineOptions;
using android::idmap2::Idmap;
using android::idmap2::PoliciesToBitmask;
using android::idmap2::PolicyBitmask;
using android::idmap2::PolicyFlags;
using android::idmap2::Result;
using android::idmap2::utils::kIdmapFilePermissionMask;
bool Create(const std::vector<std::string>& args, std::ostream& out_error) {
std::string target_apk_path;
std::string overlay_apk_path;
std::string idmap_path;
std::vector<std::string> policies;
bool ignore_overlayable;
const CommandLineOptions opts =
CommandLineOptions("idmap2 create")
@@ -47,12 +55,28 @@ bool Create(const std::vector<std::string>& args, std::ostream& out_error) {
.MandatoryOption("--overlay-apk-path",
"input: path to apk which contains the new resource values",
&overlay_apk_path)
.MandatoryOption("--idmap-path", "output: path to where to write idmap file",
&idmap_path);
.MandatoryOption("--idmap-path", "output: path to where to write idmap file", &idmap_path)
.OptionalOption("--policy",
"input: an overlayable policy this overlay fulfills "
"(if none or supplied, the overlay policy will default to \"public\")",
&policies)
.OptionalFlag("--ignore-overlayable", "disables overlayable and policy checks",
&ignore_overlayable);
if (!opts.Parse(args, out_error)) {
return false;
}
PolicyBitmask fulfilled_policies = 0;
if (auto result = PoliciesToBitmask(policies, out_error)) {
fulfilled_policies |= *result;
} else {
return false;
}
if (fulfilled_policies == 0) {
fulfilled_policies |= PolicyFlags::POLICY_PUBLIC;
}
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
if (!target_apk) {
out_error << "error: failed to load apk " << target_apk_path << std::endl;
@@ -66,7 +90,8 @@ bool Create(const std::vector<std::string>& args, std::ostream& out_error) {
}
const std::unique_ptr<const Idmap> idmap =
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, out_error);
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
fulfilled_policies, !ignore_overlayable, out_error);
if (!idmap) {
return false;
}

View File

@@ -21,8 +21,11 @@
#include <set>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "android-base/properties.h"
#include "idmap2/CommandLineOptions.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
@@ -34,6 +37,10 @@
using android::idmap2::CommandLineOptions;
using android::idmap2::Idmap;
using android::idmap2::MemoryChunk;
using android::idmap2::PoliciesToBitmask;
using android::idmap2::PolicyBitmask;
using android::idmap2::PolicyFlags;
using android::idmap2::Result;
using android::idmap2::Xml;
using android::idmap2::ZipFile;
using android::idmap2::utils::FindFiles;
@@ -45,11 +52,19 @@ struct InputOverlay {
return priority < rhs.priority || (priority == rhs.priority && apk_path < rhs.apk_path);
}
std::string apk_path; // NOLINT(misc-non-private-member-variables-in-classes)
std::string idmap_path; // NOLINT(misc-non-private-member-variables-in-classes)
int priority; // NOLINT(misc-non-private-member-variables-in-classes)
std::string apk_path; // NOLINT(misc-non-private-member-variables-in-classes)
std::string idmap_path; // NOLINT(misc-non-private-member-variables-in-classes)
int priority; // NOLINT(misc-non-private-member-variables-in-classes)
std::vector<std::string> policies; // NOLINT(misc-non-private-member-variables-in-classes)
bool ignore_overlayable; // NOLINT(misc-non-private-member-variables-in-classes)
};
bool VendorIsQOrLater() {
// STOPSHIP(b/119390857): Check api version once Q sdk version is finalized
std::string version = android::base::GetProperty("ro.vndk.version", "Q");
return version == "Q" || version == "q";
}
std::unique_ptr<std::vector<std::string>> FindApkFiles(const std::vector<std::string>& dirs,
bool recursive, std::ostream& out_error) {
const auto predicate = [](unsigned char type, const std::string& path) -> bool {
@@ -70,6 +85,22 @@ std::unique_ptr<std::vector<std::string>> FindApkFiles(const std::vector<std::st
return std::make_unique<std::vector<std::string>>(paths.cbegin(), paths.cend());
}
PolicyBitmask PolicyForPath(const std::string& apk_path) {
static const std::vector<std::pair<std::string, PolicyBitmask>> values = {
{"/product/", PolicyFlags::POLICY_PRODUCT_PARTITION},
{"/system/", PolicyFlags::POLICY_SYSTEM_PARTITION},
{"/vendor/", PolicyFlags::POLICY_VENDOR_PARTITION},
};
for (auto const& pair : values) {
if (apk_path.compare(0, pair.first.size(), pair.first) == 0) {
return pair.second | PolicyFlags::POLICY_PUBLIC;
}
}
return PolicyFlags::POLICY_PUBLIC;
}
} // namespace
bool Scan(const std::vector<std::string>& args, std::ostream& out_error) {
@@ -77,6 +108,7 @@ bool Scan(const std::vector<std::string>& args, std::ostream& out_error) {
std::string target_package_name;
std::string target_apk_path;
std::string output_directory;
std::vector<std::string> override_policies;
bool recursive = false;
const CommandLineOptions opts =
@@ -89,7 +121,12 @@ bool Scan(const std::vector<std::string>& args, std::ostream& out_error) {
.MandatoryOption("--target-apk-path", "path to target apk", &target_apk_path)
.MandatoryOption("--output-directory",
"directory in which to write artifacts (idmap files and overlays.list)",
&output_directory);
&output_directory)
.OptionalOption(
"--override-policy",
"input: an overlayable policy this overlay fulfills "
"(if none or supplied, the overlays will not have their policies overriden",
&override_policies);
if (!opts.Parse(args, out_error)) {
return false;
}
@@ -144,29 +181,63 @@ bool Scan(const std::vector<std::string>& args, std::ostream& out_error) {
continue;
}
// Sort the static overlays in ascending priority order
PolicyBitmask fulfilled_policies;
if (!override_policies.empty()) {
if (Result<PolicyBitmask> result = PoliciesToBitmask(override_policies, out_error)) {
fulfilled_policies = *result;
} else {
return false;
}
} else {
fulfilled_policies = PolicyForPath(path);
}
bool ignore_overlayable = false;
if ((fulfilled_policies & PolicyFlags::POLICY_VENDOR_PARTITION) != 0 && !VendorIsQOrLater()) {
// 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.
ignore_overlayable = true;
}
std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, path);
InputOverlay input{path, idmap_path, priority};
// Sort the static overlays in ascending priority order
InputOverlay input{path, idmap_path, priority, override_policies, ignore_overlayable};
interesting_apks.insert(
std::lower_bound(interesting_apks.begin(), interesting_apks.end(), input), input);
}
std::stringstream stream;
for (const auto& overlay : interesting_apks) {
// Create the idmap for the overlay if it currently does not exist or if it is not up to date.
std::stringstream dev_null;
if (!Verify(std::vector<std::string>({"--idmap-path", overlay.idmap_path}), dev_null) &&
!Create(std::vector<std::string>({
"--target-apk-path",
target_apk_path,
"--overlay-apk-path",
overlay.apk_path,
"--idmap-path",
overlay.idmap_path,
}),
out_error)) {
return false;
std::vector<std::string> verify_args = {"--idmap-path", overlay.idmap_path};
for (const std::string& policy : overlay.policies) {
verify_args.emplace_back("--policy");
verify_args.emplace_back(policy);
}
stream << overlay.idmap_path << std::endl;
if (!Verify(std::vector<std::string>(verify_args), dev_null)) {
std::vector<std::string> create_args = {"--target-apk-path", target_apk_path,
"--overlay-apk-path", overlay.apk_path,
"--idmap-path", overlay.idmap_path};
if (overlay.ignore_overlayable) {
create_args.emplace_back("--ignore-overlayable");
}
for (const std::string& policy : overlay.policies) {
verify_args.emplace_back("--policy");
verify_args.emplace_back(policy);
}
if (!Create(create_args, out_error)) {
return false;
}
}
stream << overlay.idmap_path << std::endl;
}
std::cout << stream.str();

View File

@@ -26,12 +26,15 @@
#include <string>
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
#include "utils/String8.h"
#include "utils/Trace.h"
#include "idmap2/BinaryStreamVisitor.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
#include "idmap2/Policies.h"
#include "idmap2/Result.h"
#include "idmap2d/Idmap2Service.h"
@@ -39,6 +42,8 @@ using android::binder::Status;
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::Idmap;
using android::idmap2::IdmapHeader;
using android::idmap2::PolicyBitmask;
using android::idmap2::Result;
using android::idmap2::utils::kIdmapFilePermissionMask;
namespace {
@@ -54,6 +59,10 @@ Status error(const std::string& msg) {
return Status::fromExceptionCode(Status::EX_NONE, msg.c_str());
}
PolicyBitmask ConvertAidlArgToPolicyBitmask(int32_t arg) {
return static_cast<PolicyBitmask>(arg);
}
} // namespace
namespace android::os {
@@ -78,6 +87,8 @@ Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path,
}
Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path,
int32_t fulfilled_policies ATTRIBUTE_UNUSED,
bool enforce_overlayable ATTRIBUTE_UNUSED,
int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
assert(_aidl_return);
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
@@ -86,11 +97,15 @@ Status Idmap2Service::verifyIdmap(const std::string& overlay_apk_path,
fin.close();
std::stringstream dev_null;
*_aidl_return = header && header->IsUpToDate(dev_null);
// TODO(b/119328308): Check that the set of fulfilled policies of the overlay has not changed
return ok();
}
Status Idmap2Service::createIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t user_id,
const std::string& overlay_apk_path, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id,
std::unique_ptr<std::string>* _aidl_return) {
assert(_aidl_return);
std::stringstream trace;
@@ -101,6 +116,8 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path,
_aidl_return->reset(nullptr);
const PolicyBitmask policy_bitmask = ConvertAidlArgToPolicyBitmask(fulfilled_policies);
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);
@@ -113,7 +130,8 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path,
std::stringstream err;
const std::unique_ptr<const Idmap> idmap =
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, err);
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
policy_bitmask, enforce_overlayable, err);
if (!idmap) {
return error(err.str());
}

View File

@@ -39,11 +39,12 @@ class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 {
binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id,
bool* _aidl_return);
binder::Status verifyIdmap(const std::string& overlay_apk_path, int32_t user_id,
bool* _aidl_return);
binder::Status verifyIdmap(const std::string& overlay_apk_path, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id, bool* _aidl_return);
binder::Status createIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t user_id,
const std::string& overlay_apk_path, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id,
std::unique_ptr<std::string>* _aidl_return);
};

View File

@@ -20,9 +20,18 @@ package android.os;
* @hide
*/
interface IIdmap2 {
const int POLICY_PUBLIC = 0x00000001;
const int POLICY_SYSTEM_PARTITION = 0x00000002;
const int POLICY_VENDOR_PARTITION = 0x00000004;
const int POLICY_PRODUCT_PARTITION = 0x00000008;
@utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId);
boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId);
boolean verifyIdmap(@utf8InCpp String overlayApkPath, int userId);
boolean verifyIdmap(@utf8InCpp String overlayApkPath, int fulfilledPolicies,
boolean enforceOverlayable, int userId);
@nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath,
@utf8InCpp String overlayApkPath, int userId);
@utf8InCpp String overlayApkPath,
int fulfilledPolicies,
boolean enforceOverlayable,
int userId);
}

View File

@@ -44,6 +44,8 @@ class CommandLineOptions {
std::vector<std::string>* value);
CommandLineOptions& OptionalOption(const std::string& name, const std::string& description,
std::string* value);
CommandLineOptions& OptionalOption(const std::string& name, const std::string& description,
std::vector<std::string>* value);
bool Parse(const std::vector<std::string>& argv, std::ostream& outError) const;
void Usage(std::ostream& out) const;
@@ -56,6 +58,7 @@ class CommandLineOptions {
COUNT_OPTIONAL,
COUNT_EXACTLY_ONCE,
COUNT_ONCE_OR_MORE,
COUNT_OPTIONAL_ONCE_OR_MORE,
} count;
bool argument;
};

View File

@@ -57,6 +57,8 @@
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
#include "idmap2/Policies.h"
namespace android::idmap2 {
class Idmap;
@@ -233,11 +235,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 std::unique_ptr<const Idmap> FromApkAssets(const std::string& target_apk_path,
const ApkAssets& target_apk_assets,
const std::string& overlay_apk_path,
const ApkAssets& overlay_apk_assets,
std::ostream& out_error);
static std::unique_ptr<const Idmap> FromApkAssets(
const std::string& target_apk_path, const ApkAssets& target_apk_assets,
const std::string& overlay_apk_path, const ApkAssets& overlay_apk_assets,
const PolicyBitmask& fulfilled_policies, bool enforce_overlayable, std::ostream& out_error);
inline const std::unique_ptr<const IdmapHeader>& GetHeader() const {
return header_;

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2019 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 <ostream>
#include <string>
#include <vector>
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
#include "Result.h"
#ifndef IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
#define IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
namespace android::idmap2 {
using PolicyFlags = ResTable_overlayable_policy_header::PolicyFlags;
using PolicyBitmask = uint32_t;
// Parses a the string representation of a set of policies into a bitmask. The format of the string
// is the same as for the <policy> element.
Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies,
std::ostream& err);
} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_POLICIES_H_

View File

@@ -68,9 +68,18 @@ CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
return *this;
}
CommandLineOptions& CommandLineOptions::OptionalOption(const std::string& name,
const std::string& description,
std::vector<std::string>* value) {
assert(value != nullptr);
auto func = [value](const std::string& arg) -> void { value->push_back(arg); };
options_.push_back(Option{name, description, func, Option::COUNT_OPTIONAL_ONCE_OR_MORE, true});
return *this;
}
bool CommandLineOptions::Parse(const std::vector<std::string>& argv, std::ostream& outError) const {
const auto pivot = std::partition(options_.begin(), options_.end(), [](const Option& opt) {
return opt.count != Option::COUNT_OPTIONAL;
return opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
});
std::set<std::string> mandatory_opts;
std::transform(options_.begin(), pivot, std::inserter(mandatory_opts, mandatory_opts.end()),
@@ -122,7 +131,8 @@ void CommandLineOptions::Usage(std::ostream& out) const {
size_t maxLength = 0;
out << "usage: " << name_;
for (const Option& opt : options_) {
const bool mandatory = opt.count != Option::COUNT_OPTIONAL;
const bool mandatory =
opt.count != Option::COUNT_OPTIONAL && opt.count != Option::COUNT_OPTIONAL_ONCE_OR_MORE;
out << " ";
if (!mandatory) {
out << "[";
@@ -134,9 +144,15 @@ void CommandLineOptions::Usage(std::ostream& out) const {
out << opt.name;
maxLength = std::max(maxLength, opt.name.size());
}
if (opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
out << " [..]";
}
if (!mandatory) {
out << "]";
}
if (opt.count == Option::COUNT_ONCE_OR_MORE) {
out << " [" << opt.name << " arg [..]]";
}
@@ -150,7 +166,8 @@ void CommandLineOptions::Usage(std::ostream& out) const {
out << opt.name;
}
out << " " << opt.description;
if (opt.count == Option::COUNT_ONCE_OR_MORE) {
if (opt.count == Option::COUNT_ONCE_OR_MORE ||
opt.count == Option::COUNT_OPTIONAL_ONCE_OR_MORE) {
out << " (can be provided multiple times)";
}
out << std::endl;

View File

@@ -274,11 +274,23 @@ std::unique_ptr<const Idmap> Idmap::FromBinaryStream(std::istream& stream,
return std::move(idmap);
}
std::unique_ptr<const Idmap> Idmap::FromApkAssets(const std::string& target_apk_path,
const ApkAssets& target_apk_assets,
const std::string& overlay_apk_path,
const ApkAssets& overlay_apk_assets,
std::ostream& out_error) {
bool CheckOverlayable(const LoadedPackage& target_package, PolicyBitmask fulfilled_polices,
ResourceId resid) {
const OverlayableInfo* info = target_package.GetOverlayableInfo(resid);
if (info == nullptr) {
// If the resource does not have an overlayable definition, allow the resource to be overlaid.
// Once overlayable enforcement is turned on, this check will return false.
return true;
}
// Enforce policy restrictions if the resource is declared as overlayable.
return (info->policy_flags & fulfilled_polices) != 0;
}
std::unique_ptr<const Idmap> Idmap::FromApkAssets(
const std::string& target_apk_path, const ApkAssets& target_apk_assets,
const std::string& overlay_apk_path, const ApkAssets& overlay_apk_assets,
const PolicyBitmask& fulfilled_policies, bool enforce_overlayable, std::ostream& out_error) {
AssetManager2 target_asset_manager;
if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true, false)) {
out_error << "error: failed to create target asset manager" << std::endl;
@@ -380,6 +392,15 @@ std::unique_ptr<const Idmap> Idmap::FromApkAssets(const std::string& target_apk_
if (target_resid == 0) {
continue;
}
if (enforce_overlayable && !CheckOverlayable(*target_pkg, fulfilled_policies, target_resid)) {
// The resources must be defined as overlayable and the overlay must fulfill at least one
// policy enforced on the overlayable resource
LOG(WARNING) << "overlay \"" << overlay_apk_path << "\" is not allowed to overlay resource \""
<< full_name << "\"" << std::endl;
continue;
}
matching_resources.Add(target_resid, overlay_resid);
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2019 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 <iterator>
#include <map>
#include <sstream>
#include <string>
#include <vector>
#include "androidfw/ResourceTypes.h"
#include "idmap2/Idmap.h"
#include "idmap2/Policies.h"
#include "idmap2/Result.h"
namespace android::idmap2 {
namespace {
const std::map<android::StringPiece, PolicyFlags> kStringToFlag = {
{"public", PolicyFlags::POLICY_PUBLIC},
{"product", PolicyFlags::POLICY_PRODUCT_PARTITION},
{"system", PolicyFlags::POLICY_SYSTEM_PARTITION},
{"vendor", PolicyFlags::POLICY_VENDOR_PARTITION},
};
} // namespace
Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies,
std::ostream& err) {
PolicyBitmask bitmask = 0;
for (const std::string& policy : policies) {
const auto iter = kStringToFlag.find(policy);
if (iter != kStringToFlag.end()) {
bitmask |= iter->second;
} else {
err << "error: unknown policy \"" << policy << "\"";
return kResultError;
}
}
return Result<PolicyBitmask>(bitmask);
}
} // namespace android::idmap2

View File

@@ -78,7 +78,8 @@ TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) {
std::stringstream error;
std::unique_ptr<const Idmap> idmap =
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error);
ASSERT_THAT(idmap, NotNull());
std::stringstream stream;
@@ -101,25 +102,52 @@ TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) {
header = loaded_idmap->GetEntryMapForType(0x02);
ASSERT_THAT(header, NotNull());
success = LoadedIdmap::Lookup(header, 0x0002, &entry);
success = LoadedIdmap::Lookup(header, 0x0000, &entry); // string/a
ASSERT_FALSE(success);
success = LoadedIdmap::Lookup(header, 0x0003, &entry);
success = LoadedIdmap::Lookup(header, 0x0001, &entry); // string/b
ASSERT_FALSE(success);
success = LoadedIdmap::Lookup(header, 0x0002, &entry); // string/c
ASSERT_FALSE(success);
success = LoadedIdmap::Lookup(header, 0x0003, &entry); // string/not_overlayable
ASSERT_FALSE(success);
success = LoadedIdmap::Lookup(header, 0x0004, &entry); // string/policy_product
ASSERT_FALSE(success);
success = LoadedIdmap::Lookup(header, 0x0005, &entry); // string/policy_public
ASSERT_FALSE(success);
success = LoadedIdmap::Lookup(header, 0x0006, &entry); // string/policy_system
ASSERT_FALSE(success);
success = LoadedIdmap::Lookup(header, 0x0007, &entry); // string/policy_system_vendor
ASSERT_FALSE(success);
success = LoadedIdmap::Lookup(header, 0x0008, &entry); // string/str1
ASSERT_TRUE(success);
ASSERT_EQ(entry, 0x0000);
success = LoadedIdmap::Lookup(header, 0x0004, &entry);
success = LoadedIdmap::Lookup(header, 0x0009, &entry); // string/str2
ASSERT_FALSE(success);
success = LoadedIdmap::Lookup(header, 0x0005, &entry);
success = LoadedIdmap::Lookup(header, 0x000a, &entry); // string/str3
ASSERT_TRUE(success);
ASSERT_EQ(entry, 0x0001);
success = LoadedIdmap::Lookup(header, 0x0006, &entry);
success = LoadedIdmap::Lookup(header, 0x000b, &entry); // string/str4
ASSERT_TRUE(success);
ASSERT_EQ(entry, 0x0002);
success = LoadedIdmap::Lookup(header, 0x0007, &entry);
success = LoadedIdmap::Lookup(header, 0x000c, &entry); // string/x
ASSERT_FALSE(success);
success = LoadedIdmap::Lookup(header, 0x000d, &entry); // string/y
ASSERT_FALSE(success);
success = LoadedIdmap::Lookup(header, 0x000e, &entry); // string/z
ASSERT_FALSE(success);
}

View File

@@ -121,6 +121,56 @@ TEST(CommandLineOptionsTests, OptionalOption) {
ASSERT_FALSE(success);
}
TEST(CommandLineOptionsTests, OptionalOptionList) {
std::vector<std::string> foo;
std::vector<std::string> bar;
CommandLineOptions opts = CommandLineOptions("test")
.OptionalOption("--foo", "", &foo)
.OptionalOption("--bar", "", &bar);
std::ostream fakeStdErr(nullptr);
bool success = opts.Parse({"--foo", "FOO", "--bar", "BAR"}, fakeStdErr);
ASSERT_TRUE(success);
ASSERT_EQ(foo.size(), 1U);
ASSERT_EQ(foo[0], "FOO");
ASSERT_EQ(bar.size(), 1U);
ASSERT_EQ(bar[0], "BAR");
foo.clear();
bar.clear();
success = opts.Parse({"--foo", "BAZ"}, fakeStdErr);
ASSERT_TRUE(success);
ASSERT_EQ(foo.size(), 1U);
ASSERT_EQ(foo[0], "BAZ");
ASSERT_EQ(bar.size(), 0U);
foo.clear();
bar.clear();
success =
opts.Parse({"--foo", "BAZ", "--foo", "BIZ", "--bar", "FIZ", "--bar", "FUZZ"}, fakeStdErr);
ASSERT_TRUE(success);
ASSERT_EQ(foo.size(), 2U);
ASSERT_EQ(foo[0], "BAZ");
ASSERT_EQ(foo[1], "BIZ");
ASSERT_EQ(bar.size(), 2U);
ASSERT_EQ(bar[0], "FIZ");
ASSERT_EQ(bar[1], "FUZZ");
foo.clear();
bar.clear();
success = opts.Parse({"--foo"}, fakeStdErr);
ASSERT_FALSE(success);
foo.clear();
bar.clear();
success = opts.Parse({"--foo", "--bar", "BAR"}, fakeStdErr);
ASSERT_FALSE(success);
foo.clear();
bar.clear();
success = opts.Parse({"--foo", "FOO", "--bar"}, fakeStdErr);
ASSERT_FALSE(success);
}
TEST(CommandLineOptionsTests, CornerCases) {
std::string foo;
std::string bar;
@@ -172,6 +222,7 @@ TEST(CommandLineOptionsTests, Usage) {
bool arg5 = false;
bool arg6 = false;
std::vector<std::string> arg7;
std::vector<std::string> arg8;
CommandLineOptions opts = CommandLineOptions("test")
.MandatoryOption("--aa", "description-aa", &arg1)
.OptionalFlag("--bb", "description-bb", &arg5)
@@ -179,12 +230,13 @@ TEST(CommandLineOptionsTests, Usage) {
.OptionalOption("--dd", "description-dd", &arg3)
.MandatoryOption("--ee", "description-ee", &arg4)
.OptionalFlag("--ff", "description-ff", &arg6)
.MandatoryOption("--gg", "description-gg", &arg7);
.MandatoryOption("--gg", "description-gg", &arg7)
.OptionalOption("--hh", "description-hh", &arg8);
std::stringstream stream;
opts.Usage(stream);
const std::string s = stream.str();
ASSERT_NE(s.find("usage: test --aa arg [--bb] [--cc arg] [--dd arg] --ee arg [--ff] --gg arg "
"[--gg arg [..]]"),
"[--gg arg [..]] [--hh arg [..]]"),
std::string::npos);
ASSERT_NE(s.find("--aa arg description-aa"), std::string::npos);
ASSERT_NE(s.find("--ff description-ff"), std::string::npos);

View File

@@ -37,10 +37,10 @@ TEST(FileUtilsTests, FindFilesFindEverythingNonRecursive) {
[](unsigned char type ATTRIBUTE_UNUSED,
const std::string& path ATTRIBUTE_UNUSED) -> bool { return true; });
ASSERT_THAT(v, NotNull());
ASSERT_EQ(v->size(), 4U);
ASSERT_EQ(
std::set<std::string>(v->begin(), v->end()),
std::set<std::string>({root + "/.", root + "/..", root + "/overlay", root + "/target"}));
ASSERT_EQ(v->size(), 6U);
ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
std::set<std::string>({root + "/.", root + "/..", root + "/overlay", root + "/target",
root + "/system-overlay", root + "/system-overlay-invalid"}));
}
TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) {
@@ -49,11 +49,13 @@ TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) {
return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0;
});
ASSERT_THAT(v, NotNull());
ASSERT_EQ(v->size(), 4U);
ASSERT_EQ(v->size(), 6U);
ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
std::set<std::string>({root + "/target/target.apk", root + "/overlay/overlay.apk",
root + "/overlay/overlay-static-1.apk",
root + "/overlay/overlay-static-2.apk"}));
root + "/overlay/overlay-static-2.apk",
root + "/system-overlay/system-overlay.apk",
root + "/system-overlay-invalid/system-overlay-invalid.apk"}));
}
TEST(FileUtilsTests, ReadFile) {

View File

@@ -38,6 +38,7 @@
#include "gtest/gtest.h"
#include "androidfw/PosixUtils.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
@@ -114,8 +115,9 @@ TEST_F(Idmap2BinaryTests, Dump) {
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
ASSERT_NE(result->stdout.find("0x7f010000 -> 0x7f010000 integer/int1"), std::string::npos);
ASSERT_NE(result->stdout.find("0x7f020003 -> 0x7f020000 string/str1"), std::string::npos);
ASSERT_NE(result->stdout.find("0x7f020005 -> 0x7f020001 string/str3"), std::string::npos);
ASSERT_NE(result->stdout.find("0x7f020008 -> 0x7f020000 string/str1"), std::string::npos);
ASSERT_NE(result->stdout.find("0x7f02000a -> 0x7f020001 string/str3"), std::string::npos);
ASSERT_NE(result->stdout.find("0x7f02000b -> 0x7f020002 string/str4"), std::string::npos);
ASSERT_EQ(result->stdout.find("00000210: 007f target package id"), std::string::npos);
// clang-format off
@@ -157,7 +159,8 @@ TEST_F(Idmap2BinaryTests, Scan) {
"--recursive",
"--target-package-name", "test.target",
"--target-apk-path", GetTargetApkPath(),
"--output-directory", GetTempDirPath()});
"--output-directory", GetTempDirPath(),
"--override-policy", "public"});
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
@@ -190,7 +193,8 @@ TEST_F(Idmap2BinaryTests, Scan) {
"--input-directory", GetTestDataPath() + "/overlay",
"--target-package-name", "test.target",
"--target-apk-path", GetTargetApkPath(),
"--output-directory", GetTempDirPath()});
"--output-directory", GetTempDirPath(),
"--override-policy", "public"});
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
@@ -207,7 +211,8 @@ TEST_F(Idmap2BinaryTests, Scan) {
"--recursive",
"--target-package-name", "test.target",
"--target-apk-path", GetTargetApkPath(),
"--output-directory", GetTempDirPath()});
"--output-directory", GetTempDirPath(),
"--override-policy", "public"});
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
@@ -222,7 +227,8 @@ TEST_F(Idmap2BinaryTests, Scan) {
"--input-directory", GetTempDirPath(),
"--target-package-name", "test.target",
"--target-apk-path", GetTargetApkPath(),
"--output-directory", GetTempDirPath()});
"--output-directory", GetTempDirPath(),
"--override-policy", "public"});
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
@@ -245,7 +251,7 @@ TEST_F(Idmap2BinaryTests, Lookup) {
"lookup",
"--idmap-path", GetIdmapPath(),
"--config", "",
"--resid", "0x7f020003"}); // string/str1
"--resid", "0x7f020008"}); // string/str1
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
@@ -310,6 +316,18 @@ TEST_F(Idmap2BinaryTests, InvalidCommandLineOptions) {
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_NE(result->status, EXIT_SUCCESS);
// unknown policy
// clang-format off
result = ExecuteBinary({"idmap2",
"create",
"--target-apk-path", GetTargetApkPath(),
"--overlay-apk-path", GetOverlayApkPath(),
"--idmap-path", GetIdmapPath(),
"--policy", "this-does-not-exist"});
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_NE(result->status, EXIT_SUCCESS);
}
} // namespace android::idmap2

View File

@@ -184,13 +184,14 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) {
std::stringstream error;
std::unique_ptr<const Idmap> idmap =
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error);
ASSERT_THAT(idmap, NotNull());
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xf5ad1d1d);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xca2093da);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xd470336b);
ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
@@ -216,13 +217,127 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) {
ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U);
ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U);
ASSERT_EQ(types[1]->GetEntryCount(), 4U);
ASSERT_EQ(types[1]->GetEntryOffset(), 3U);
ASSERT_EQ(types[1]->GetEntryOffset(), 8U);
ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
ASSERT_EQ(types[1]->GetEntry(3), 0x0002U);
}
TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
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());
const std::string overlay_apk_path(GetTestDataPath() + "/system-overlay/system-overlay.apk");
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
std::stringstream error;
std::unique_ptr<const Idmap> idmap =
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
/* enforce_overlayable */ true, error);
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_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
ASSERT_EQ(types.size(), 1U);
ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
ASSERT_EQ(types[0]->GetEntryCount(), 3U);
ASSERT_EQ(types[0]->GetEntryOffset(), 5U);
ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_public
ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/policy_system
ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_system_vendor
}
TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
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());
const std::string overlay_apk_path(GetTestDataPath() +
"/system-overlay-invalid/system-overlay-invalid.apk");
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
std::stringstream error;
std::unique_ptr<const Idmap> idmap =
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
/* enforce_overlayable */ true, error);
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_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
ASSERT_EQ(types.size(), 1U);
ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
ASSERT_EQ(types[0]->GetEntryCount(), 5U);
ASSERT_EQ(types[0]->GetEntryOffset(), 3U);
ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable
ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_product
ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_public
ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/policy_system
ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_system_vendor
}
TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
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());
const std::string overlay_apk_path(GetTestDataPath() +
"/system-overlay-invalid/system-overlay-invalid.apk");
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
std::stringstream error;
std::unique_ptr<const Idmap> idmap =
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
/* enforce_overlayable */ false, error);
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_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
ASSERT_EQ(types.size(), 1U);
ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
ASSERT_EQ(types[0]->GetEntryCount(), 5U);
ASSERT_EQ(types[0]->GetEntryOffset(), 3U);
ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable
ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/policy_product
ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_public
ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/policy_system
ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_system_vendor
}
TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
std::string target_apk_path(GetTestDataPath());
for (int i = 0; i < 32; i++) {
@@ -239,7 +354,8 @@ TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
std::stringstream error;
std::unique_ptr<const Idmap> idmap =
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error);
ASSERT_THAT(idmap, IsNull());
}
@@ -255,8 +371,9 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
ASSERT_THAT(overlay_apk, NotNull());
std::stringstream error;
std::unique_ptr<const Idmap> idmap =
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
std::unique_ptr<const Idmap> idmap = Idmap::FromApkAssets(
target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
/* enforce_overlayable */ true, error);
ASSERT_THAT(idmap, NotNull());
std::stringstream stream;

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2019 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 <string>
#include "gtest/gtest.h"
#include "TestHelpers.h"
#include "idmap2/Policies.h"
using android::idmap2::PolicyBitmask;
using android::idmap2::PolicyFlags;
namespace android::idmap2 {
TEST(PoliciesTests, PoliciesToBitmasks) {
const Result<PolicyBitmask> bitmask1 = PoliciesToBitmask({"system"}, std::cerr);
ASSERT_NE(bitmask1, kResultError);
ASSERT_EQ(bitmask1, PolicyFlags::POLICY_SYSTEM_PARTITION);
const Result<PolicyBitmask> bitmask2 = PoliciesToBitmask({"system", "vendor"}, std::cerr);
ASSERT_NE(bitmask2, kResultError);
ASSERT_EQ(bitmask2, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION);
const Result<PolicyBitmask> bitmask3 = PoliciesToBitmask({"vendor", "system"}, std::cerr);
ASSERT_NE(bitmask3, kResultError);
ASSERT_EQ(bitmask3, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION);
const Result<PolicyBitmask> bitmask4 =
PoliciesToBitmask({"public", "product", "system", "vendor"}, std::cerr);
ASSERT_NE(bitmask4, kResultError);
ASSERT_EQ(bitmask4, PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_PRODUCT_PARTITION |
PolicyFlags::POLICY_SYSTEM_PARTITION |
PolicyFlags::POLICY_VENDOR_PARTITION);
const Result<PolicyBitmask> bitmask5 =
PoliciesToBitmask({"system", "system", "system"}, std::cerr);
ASSERT_NE(bitmask5, kResultError);
ASSERT_EQ(bitmask5, PolicyFlags::POLICY_SYSTEM_PARTITION);
const Result<PolicyBitmask> bitmask6 = PoliciesToBitmask({""}, std::cerr);
ASSERT_EQ(bitmask6, kResultError);
const Result<PolicyBitmask> bitmask7 = PoliciesToBitmask({"foo"}, std::cerr);
ASSERT_EQ(bitmask7, kResultError);
const Result<PolicyBitmask> bitmask8 = PoliciesToBitmask({"system", "foo"}, std::cerr);
ASSERT_EQ(bitmask8, kResultError);
const Result<PolicyBitmask> bitmask9 = PoliciesToBitmask({"system", ""}, std::cerr);
ASSERT_EQ(bitmask9, kResultError);
const Result<PolicyBitmask> bitmask10 = PoliciesToBitmask({"system "}, std::cerr);
ASSERT_EQ(bitmask10, kResultError);
}
} // namespace android::idmap2

View File

@@ -25,6 +25,7 @@
#include "androidfw/Idmap.h"
#include "idmap2/Idmap.h"
#include "idmap2/Policies.h"
#include "idmap2/PrettyPrintVisitor.h"
#include "TestHelpers.h"
@@ -32,6 +33,7 @@
using ::testing::NotNull;
using android::ApkAssets;
using android::idmap2::PolicyBitmask;
namespace android::idmap2 {
@@ -46,7 +48,8 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
std::stringstream error;
std::unique_ptr<const Idmap> idmap =
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error);
ASSERT_THAT(idmap, NotNull());
std::stringstream stream;

View File

@@ -42,7 +42,8 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
std::stringstream error;
std::unique_ptr<const Idmap> idmap =
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, error);
Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true, error);
ASSERT_THAT(idmap, NotNull());
std::stringstream stream;
@@ -51,7 +52,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos);
ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos);
ASSERT_NE(stream.str().find("00000008: f5ad1d1d target crc\n"), std::string::npos);
ASSERT_NE(stream.str().find("00000008: ca2093da target crc\n"), std::string::npos);
ASSERT_NE(stream.str().find("0000000c: d470336b overlay crc\n"), std::string::npos);
ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f010000 -> 0x7f010000 integer/int1\n"),
std::string::npos);

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2019 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.
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="test.overlay.system.invalid">
<overlay
android:targetPackage="test.target" />
</manifest>

View File

@@ -0,0 +1,26 @@
# Copyright (C) 2019 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.
FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar
aapt2 compile --dir res -o compiled.flata
aapt2 link \
--no-resource-removal \
-I "$FRAMEWORK_RES_APK" \
--manifest AndroidManifest.xml \
-o system-overlay-invalid.apk \
compiled.flata
rm compiled.flata

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2019 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.
-->
<resources>
<!-- This overlay will fulfill the policies "public|system". This allows it overlay the
following resources. -->
<string name="policy_system">policy_system</string>
<string name="policy_system_vendor">policy_system_vendor</string>
<string name="policy_public">policy_public</string>
<!-- It also requests to overlay a resource that belongs to a policy the overlay does not
fulfill.-->
<string name="policy_product">policy_product</string>
<!-- It also requests to overlay a resource that is not declared as overlayable.-->
<string name="not_overlayable">not_overlayable</string>
</resources>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2019 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.
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="test.overlay.system">
<overlay
android:targetPackage="test.target" />
</manifest>

View File

@@ -0,0 +1,26 @@
# Copyright (C) 2019 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.
FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar
aapt2 compile --dir res -o compiled.flata
aapt2 link \
--no-resource-removal \
-I "$FRAMEWORK_RES_APK" \
--manifest AndroidManifest.xml \
-o system-overlay.apk \
compiled.flata
rm compiled.flata

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2019 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.
-->
<resources>
<!-- This overlay will fulfill the policies "public|system". This allows it overlay the
following resources. -->
<string name="policy_system">policy_system</string>
<string name="policy_system_vendor">policy_system_vendor</string>
<string name="policy_public">policy_public</string>
</resources>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2019 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.
-->
<resources>
<overlayable name="TestResources">
<!-- Publicly overlayable resources -->
<item type="string" name="a" />
<item type="string" name="b" />
<item type="string" name="c" />
<item type="string" name="str1" />
<item type="string" name="str2" />
<item type="string" name="str3" />
<item type="string" name="str4" />
<item type="string" name="x" />
<item type="string" name="y" />
<item type="string" name="z" />
<item type="integer" name="int1" />
<!-- Resources with partition restrictins -->
<policy type="system">
<item type="string" name="policy_system" />
</policy>
<policy type="system|vendor">
<item type="string" name="policy_system_vendor" />
</policy>
<policy type="product">
<item type="string" name="policy_product" />
</policy>
<policy type="public">
<item type="string" name="policy_public" />
</policy>
</overlayable>
</resources>

View File

@@ -25,4 +25,12 @@
<string name="y">y</string>
<string name="z">z</string>
<integer name="int1">1</integer>
<!-- This resources is not marked as overlayable -->
<string name="not_overlayable">not_overlayable</string>
<string name="policy_system">policy_system</string>
<string name="policy_system_vendor">policy_system_vendor</string>
<string name="policy_product">policy_product</string>
<string name="policy_public">policy_public</string>
</resources>

View File

@@ -24,11 +24,14 @@ import static com.android.server.om.OverlayManagerService.TAG;
import android.annotation.NonNull;
import android.content.om.OverlayInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.os.Build.VERSION_CODES;
import android.os.IBinder;
import android.os.IIdmap2;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.Slog;
@@ -51,6 +54,13 @@ class IdmapManager {
private final Installer mInstaller;
private IIdmap2 mIdmap2Service;
private static final boolean VENDOR_IS_Q_OR_LATER;
static {
// STOPSHIP(b/119390857): Check api version once Q sdk version is finalized
final String value = SystemProperties.get("ro.vndk.version", "Q");
VENDOR_IS_Q_OR_LATER = value.equals("Q") || value.equals("q");
}
IdmapManager(final Installer installer) {
mInstaller = installer;
if (FEATURE_FLAG_IDMAP2) {
@@ -69,10 +79,13 @@ class IdmapManager {
final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath();
try {
if (FEATURE_FLAG_IDMAP2) {
if (mIdmap2Service.verifyIdmap(overlayPath, userId)) {
int policies = determineFulfilledPolicies(overlayPackage);
boolean enforce = enforceOverlayable(overlayPackage);
if (mIdmap2Service.verifyIdmap(overlayPath, policies, enforce, userId)) {
return true;
}
return mIdmap2Service.createIdmap(targetPath, overlayPath, userId) != null;
return mIdmap2Service.createIdmap(targetPath, overlayPath, policies, enforce,
userId) != null;
} else {
mInstaller.idmap(targetPath, overlayPath, sharedGid);
return true;
@@ -156,4 +169,71 @@ class IdmapManager {
}, SECOND_IN_MILLIS);
}
}
/**
* 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) {
// Always enforce policies for overlays targeting Q+.
return true;
}
if (ai.isVendor() && !VENDOR_IS_Q_OR_LATER) {
// 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.
return false;
}
// Do not enforce overlayable restrictions on pre-Q overlays signed with the
// platform signature.
return !ai.isSignedWithPlatformKey();
}
/**
* Retrieves a bitmask for idmap2 that represents the policies the specified overlay fulfills.
* @throws SecurityException if the overlay is not allowed to overlay any resource
*/
private int determineFulfilledPolicies(@NonNull final PackageInfo overlayPackage)
throws SecurityException {
final ApplicationInfo ai = overlayPackage.applicationInfo;
final boolean overlayIsQOrLater = ai.targetSdkVersion >= VERSION_CODES.Q;
int fulfilledPolicies = 0;
// TODO(b/119402606) : Add signature policy
// Vendor partition (/vendor)
if (ai.isVendor()) {
if (overlayIsQOrLater) {
fulfilledPolicies |= IIdmap2.POLICY_VENDOR_PARTITION;
} else if (VENDOR_IS_Q_OR_LATER) {
throw new SecurityException("Overlay must target Q sdk or higher");
}
}
// Product partition (/product)
if (ai.isProduct()) {
if (overlayIsQOrLater) {
fulfilledPolicies |= IIdmap2.POLICY_PRODUCT_PARTITION;
} else {
throw new SecurityException("Overlay must target Q sdk or higher");
}
}
// System partition (/system)
if (ai.isSystemApp()) {
if (overlayIsQOrLater) {
fulfilledPolicies |= IIdmap2.POLICY_SYSTEM_PARTITION;
} else {
throw new SecurityException("Overlay must target Q sdk or higher");
}
}
// All overlays can overlay resources with the public policy
return fulfilledPolicies | IIdmap2.POLICY_PUBLIC;
}
}