summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/idmap2/Android.bp1
-rw-r--r--cmds/idmap2/idmap2/Create.cpp5
-rw-r--r--cmds/idmap2/idmap2/CreateMultiple.cpp6
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.cpp33
-rw-r--r--cmds/idmap2/idmap2d/Idmap2Service.h3
-rw-r--r--cmds/idmap2/idmap2d/aidl/core/android/os/OverlayConstraint.aidl25
-rw-r--r--cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl7
-rw-r--r--cmds/idmap2/include/idmap2/BinaryStreamVisitor.h1
-rw-r--r--cmds/idmap2/include/idmap2/Idmap.h42
-rw-r--r--cmds/idmap2/include/idmap2/PrettyPrintVisitor.h1
-rw-r--r--cmds/idmap2/include/idmap2/RawPrintVisitor.h1
-rw-r--r--cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp8
-rw-r--r--cmds/idmap2/libidmap2/Idmap.cpp42
-rw-r--r--cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp13
-rw-r--r--cmds/idmap2/libidmap2/RawPrintVisitor.cpp8
-rw-r--r--cmds/idmap2/self_targeting/SelfTargeting.cpp6
-rw-r--r--cmds/idmap2/tests/BinaryStreamVisitorTests.cpp2
-rw-r--r--cmds/idmap2/tests/Idmap2BinaryTests.cpp1
-rw-r--r--cmds/idmap2/tests/IdmapTests.cpp44
-rw-r--r--cmds/idmap2/tests/PrettyPrintVisitorTests.cpp4
-rw-r--r--cmds/idmap2/tests/RawPrintVisitorTests.cpp14
-rw-r--r--cmds/idmap2/tests/TestHelpers.h108
-rw-r--r--core/java/android/content/om/IOverlayManager.aidl19
-rw-r--r--core/java/android/content/om/OverlayConstraint.aidl19
-rw-r--r--core/java/android/content/om/OverlayConstraint.java151
-rw-r--r--core/java/android/content/om/OverlayInfo.java67
-rw-r--r--core/java/android/content/om/OverlayManager.java44
-rw-r--r--core/java/android/content/om/OverlayManagerTransaction.java94
-rw-r--r--core/java/android/content/res/flags.aconfig8
-rw-r--r--core/java/com/android/internal/content/om/OverlayManagerImpl.java2
-rw-r--r--libs/androidfw/Idmap.cpp38
-rw-r--r--libs/androidfw/ResourceTypes.cpp17
-rw-r--r--libs/androidfw/include/androidfw/Idmap.h8
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h4
-rw-r--r--libs/androidfw/tests/data/overlay/overlay.idmapbin732 -> 736 bytes
-rw-r--r--services/core/java/com/android/server/om/IdmapDaemon.java13
-rw-r--r--services/core/java/com/android/server/om/IdmapManager.java24
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java21
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerServiceImpl.java53
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerSettings.java74
-rw-r--r--services/tests/servicestests/src/com/android/server/om/OverlayConstraintsTests.java195
-rw-r--r--services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java75
-rw-r--r--services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java201
45 files changed, 1287 insertions, 223 deletions
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index d9ff19051de9..f6bee52661da 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -348,6 +348,7 @@ filegroup {
"idmap2d/aidl/core/android/os/FabricatedOverlayInternal.aidl",
"idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl",
"idmap2d/aidl/core/android/os/FabricatedOverlayInfo.aidl",
+ "idmap2d/aidl/core/android/os/OverlayConstraint.aidl",
],
path: "idmap2d/aidl/core/",
}
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index d5f1b895facf..d94940131c8b 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -35,6 +35,7 @@ using android::idmap2::BinaryStreamVisitor;
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
+using android::idmap2::IdmapConstraints;
using android::idmap2::OverlayResourceContainer;
using android::idmap2::Result;
using android::idmap2::TargetResourceContainer;
@@ -104,8 +105,10 @@ Result<Unit> Create(const std::vector<std::string>& args) {
return Error("failed to load apk overlay '%s'", overlay_apk_path.c_str());
}
+ // TODO(b/371801644): Add command-line support for RRO constraints.
+ auto constraints = std::make_unique<const IdmapConstraints>();
const auto idmap = Idmap::FromContainers(**target, **overlay, overlay_name, fulfilled_policies,
- !ignore_overlayable);
+ !ignore_overlayable, std::move(constraints));
if (!idmap) {
return Error(idmap.GetError(), "failed to create idmap");
}
diff --git a/cmds/idmap2/idmap2/CreateMultiple.cpp b/cmds/idmap2/idmap2/CreateMultiple.cpp
index 2608c69be66f..70a2ed1940b2 100644
--- a/cmds/idmap2/idmap2/CreateMultiple.cpp
+++ b/cmds/idmap2/idmap2/CreateMultiple.cpp
@@ -39,6 +39,7 @@ using android::idmap2::BinaryStreamVisitor;
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
+using android::idmap2::IdmapConstraints;
using android::idmap2::OverlayResourceContainer;
using android::idmap2::Result;
using android::idmap2::TargetResourceContainer;
@@ -115,8 +116,11 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
continue;
}
+ // TODO(b/371801644): Add command-line support for RRO constraints.
+ auto constraints = std::make_unique<const IdmapConstraints>();
const auto idmap =
- Idmap::FromContainers(**target, **overlay, "", fulfilled_policies, !ignore_overlayable);
+ Idmap::FromContainers(**target, **overlay, "", fulfilled_policies, !ignore_overlayable,
+ std::move(constraints));
if (!idmap) {
LOG(WARNING) << "failed to create idmap";
continue;
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 6902d6db6751..2495c55cc065 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -46,6 +46,8 @@ using android::binder::Status;
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::FabricatedOverlayContainer;
using android::idmap2::Idmap;
+using android::idmap2::IdmapConstraint;
+using android::idmap2::IdmapConstraints;
using android::idmap2::IdmapHeader;
using android::idmap2::OverlayResourceContainer;
using android::idmap2::PrettyPrintVisitor;
@@ -74,6 +76,18 @@ PolicyBitmask ConvertAidlArgToPolicyBitmask(int32_t arg) {
return static_cast<PolicyBitmask>(arg);
}
+std::unique_ptr<const IdmapConstraints> ConvertAidlConstraintsToIdmapConstraints(
+ const std::vector<android::os::OverlayConstraint>& constraints) {
+ auto idmapConstraints = std::make_unique<IdmapConstraints>();
+ for (const auto& constraint : constraints) {
+ IdmapConstraint idmapConstraint{};
+ idmapConstraint.constraint_type = constraint.type;
+ idmapConstraint.constraint_value = constraint.value;
+ idmapConstraints->constraints.insert(idmapConstraint);
+ }
+ return idmapConstraints;
+}
+
} // namespace
namespace android::os {
@@ -113,6 +127,7 @@ Status Idmap2Service::removeIdmap(const std::string& overlay_path, int32_t user_
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,
+ const std::vector<os::OverlayConstraint>& constraints,
bool* _aidl_return) {
SYSTRACE << "Idmap2Service::verifyIdmap " << overlay_path;
assert(_aidl_return);
@@ -120,12 +135,19 @@ Status Idmap2Service::verifyIdmap(const std::string& target_path, const std::str
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);
+ const std::unique_ptr<const IdmapConstraints> oldConstraints =
+ IdmapConstraints::FromBinaryStream(fin);
fin.close();
if (!header) {
*_aidl_return = false;
LOG(WARNING) << "failed to parse idmap header of '" << idmap_path << "'";
return ok();
}
+ if (!oldConstraints) {
+ *_aidl_return = false;
+ LOG(WARNING) << "failed to parse idmap constraints of '" << idmap_path << "'";
+ return ok();
+ }
const auto target = GetTargetContainer(target_path);
if (!target) {
@@ -145,7 +167,10 @@ Status Idmap2Service::verifyIdmap(const std::string& target_path, const std::str
header->IsUpToDate(*GetPointer(*target), **overlay, overlay_name,
ConvertAidlArgToPolicyBitmask(fulfilled_policies), enforce_overlayable);
- *_aidl_return = static_cast<bool>(up_to_date);
+ std::unique_ptr<const IdmapConstraints> newConstraints =
+ ConvertAidlConstraintsToIdmapConstraints(constraints);
+
+ *_aidl_return = static_cast<bool>(up_to_date && (*oldConstraints == *newConstraints));
if (!up_to_date) {
LOG(WARNING) << "idmap '" << idmap_path
<< "' not up to date : " << up_to_date.GetErrorMessage();
@@ -156,6 +181,7 @@ Status Idmap2Service::verifyIdmap(const std::string& target_path, const std::str
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,
+ const std::vector<os::OverlayConstraint>& constraints,
std::optional<std::string>* _aidl_return) {
assert(_aidl_return);
SYSTRACE << "Idmap2Service::createIdmap " << target_path << " " << overlay_path;
@@ -186,8 +212,11 @@ Status Idmap2Service::createIdmap(const std::string& target_path, const std::str
return error("failed to load apk overlay '%s'" + overlay_path);
}
+ std::unique_ptr<const IdmapConstraints> idmapConstraints =
+ ConvertAidlConstraintsToIdmapConstraints(constraints);
const auto idmap = Idmap::FromContainers(*GetPointer(*target), **overlay, overlay_name,
- policy_bitmask, enforce_overlayable);
+ policy_bitmask, enforce_overlayable,
+ std::move(idmapConstraints));
if (!idmap) {
return error(idmap.GetErrorMessage());
}
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 272ec6be3bac..344a77f5581f 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -20,6 +20,7 @@
#include <android-base/unique_fd.h>
#include <android/os/BnIdmap2.h>
#include <android/os/FabricatedOverlayInfo.h>
+#include <android/os/OverlayConstraint.h>
#include <binder/BinderService.h>
#include <idmap2/ResourceContainer.h>
#include <idmap2/Result.h>
@@ -49,11 +50,13 @@ class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 {
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,
+ const std::vector<os::OverlayConstraint>& constraints,
bool* _aidl_return) override;
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,
+ const std::vector<os::OverlayConstraint>& constraints,
std::optional<std::string>* _aidl_return) override;
binder::Status createFabricatedOverlay(
diff --git a/cmds/idmap2/idmap2d/aidl/core/android/os/OverlayConstraint.aidl b/cmds/idmap2/idmap2d/aidl/core/android/os/OverlayConstraint.aidl
new file mode 100644
index 000000000000..8fce3d6567ab
--- /dev/null
+++ b/cmds/idmap2/idmap2d/aidl/core/android/os/OverlayConstraint.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2025 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 OverlayConstraint {
+ int type;
+ int value;
+} \ No newline at end of file
diff --git a/cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl
index 2bbfba97a6c6..4f4f075a0e63 100644
--- a/cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl
+++ b/cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl
@@ -18,6 +18,7 @@ package android.os;
import android.os.FabricatedOverlayInfo;
import android.os.FabricatedOverlayInternal;
+import android.os.OverlayConstraint;
/**
* @hide
@@ -30,13 +31,15 @@ interface IIdmap2 {
@utf8InCpp String overlayName,
int fulfilledPolicies,
boolean enforceOverlayable,
- int userId);
+ int userId,
+ in OverlayConstraint[] constraints);
@nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath,
@utf8InCpp String overlayApkPath,
@utf8InCpp String overlayName,
int fulfilledPolicies,
boolean enforceOverlayable,
- int userId);
+ int userId,
+ in OverlayConstraint[] constraints);
@nullable FabricatedOverlayInfo createFabricatedOverlay(in FabricatedOverlayInternal overlay);
boolean deleteFabricatedOverlay(@utf8InCpp String path);
diff --git a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
index 57af1b61c300..3009293bc4ab 100644
--- a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
+++ b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
@@ -32,6 +32,7 @@ class BinaryStreamVisitor : public Visitor {
~BinaryStreamVisitor() override = default;
void visit(const Idmap& idmap) override;
void visit(const IdmapHeader& header) override;
+ void visit(const IdmapConstraints& constraints) override;
void visit(const IdmapData& data) override;
void visit(const IdmapData::Header& header) override;
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index b0ba01957ab6..1f15daf1ba47 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -17,10 +17,11 @@
/*
* # idmap file format (current version)
*
- * idmap := header data*
+ * idmap := header constraints_count constraint* data*
* header := magic version target_crc overlay_crc fulfilled_policies
* enforce_overlayable target_path overlay_path overlay_name
* debug_info
+ * constraints := constraint_type constraint_value
* data := data_header target_entries target_inline_entries
target_inline_entry_value* config* overlay_entries string_pool
* data_header := target_entry_count target_inline_entry_count
@@ -67,6 +68,9 @@
* value_type := <uint8_t>
* value_data := <uint32_t>
* version := <uint32_t>
+ * constraints_count := <uint32_t>
+ * constraint_type := <uint32_t>
+ * constraint_value := <uint32_t>
*/
#ifndef IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
@@ -76,6 +80,7 @@
#include <memory>
#include <string>
#include <string_view>
+#include <unordered_set>
#include <vector>
#include "android-base/macros.h"
@@ -171,6 +176,33 @@ class IdmapHeader {
friend Idmap;
DISALLOW_COPY_AND_ASSIGN(IdmapHeader);
};
+
+struct IdmapConstraint {
+ // Constraint type can be TYPE_DISPLAY_ID or TYP_DEVICE_ID, please refer
+ // to ConstraintType in OverlayConstraint.java
+ uint32_t constraint_type;
+ uint32_t constraint_value;
+
+ bool operator==(const IdmapConstraint&) const = default;
+};
+
+struct IdmapConstraints {
+ static std::unique_ptr<const IdmapConstraints> FromBinaryStream(std::istream& stream);
+
+ struct Hash {
+ static std::size_t operator()(const IdmapConstraint& constraint) {
+ return std::hash<int>()(constraint.constraint_type) * 31
+ + std::hash<int>()(constraint.constraint_value);
+ }
+ };
+
+ bool operator == (const IdmapConstraints& constraints) const = default;
+
+ void accept(Visitor* v) const;
+
+ std::unordered_set<IdmapConstraint, Hash> constraints;
+};
+
class IdmapData {
public:
class Header {
@@ -286,12 +318,16 @@ class Idmap {
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);
+ bool enforce_overlayable, std::unique_ptr<const IdmapConstraints>&& constraints);
const std::unique_ptr<const IdmapHeader>& GetHeader() const {
return header_;
}
+ const std::unique_ptr<const IdmapConstraints>& GetConstraints() const {
+ return constraints_;
+ }
+
const std::vector<std::unique_ptr<const IdmapData>>& GetData() const {
return data_;
}
@@ -302,6 +338,7 @@ class Idmap {
Idmap() = default;
std::unique_ptr<const IdmapHeader> header_;
+ std::unique_ptr<const IdmapConstraints> constraints_;
std::vector<std::unique_ptr<const IdmapData>> data_;
DISALLOW_COPY_AND_ASSIGN(Idmap);
@@ -312,6 +349,7 @@ class Visitor {
virtual ~Visitor() = default;
virtual void visit(const Idmap& idmap) = 0;
virtual void visit(const IdmapHeader& header) = 0;
+ virtual void visit(const IdmapConstraints& constraints) = 0;
virtual void visit(const IdmapData& data) = 0;
virtual void visit(const IdmapData::Header& header) = 0;
};
diff --git a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
index ed18d9cbf20f..033ef85f5133 100644
--- a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
@@ -36,6 +36,7 @@ class PrettyPrintVisitor : public Visitor {
~PrettyPrintVisitor() override = default;
void visit(const Idmap& idmap) override;
void visit(const IdmapHeader& header) override;
+ void visit(const IdmapConstraints& constraints) override;
void visit(const IdmapData& data) override;
void visit(const IdmapData::Header& header) override;
diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
index 849ba11aacff..bd27c0d62c0d 100644
--- a/cmds/idmap2/include/idmap2/RawPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
@@ -37,6 +37,7 @@ class RawPrintVisitor : public Visitor {
~RawPrintVisitor() override = default;
void visit(const Idmap& idmap) override;
void visit(const IdmapHeader& header) override;
+ void visit(const IdmapConstraints& constraints) override;
void visit(const IdmapData& data) override;
void visit(const IdmapData::Header& header) override;
diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
index 00ef0c7f8cf0..b029aea1a1bf 100644
--- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -63,6 +63,14 @@ void BinaryStreamVisitor::visit(const IdmapHeader& header) {
WriteString(header.GetDebugInfo());
}
+void BinaryStreamVisitor::visit(const IdmapConstraints& constraints) {
+ Write32(static_cast<uint32_t>(constraints.constraints.size()));
+ for (const auto& constraint : constraints.constraints) {
+ Write32(constraint.constraint_type);
+ Write32(constraint.constraint_value);
+ }
+}
+
void BinaryStreamVisitor::visit(const IdmapData& data) {
for (const auto& target_entry : data.GetTargetEntries()) {
Write32(target_entry.target_id);
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 7680109f1d54..556ca228e83d 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -182,6 +182,26 @@ Result<Unit> IdmapHeader::IsUpToDate(const std::string& target_path,
return Unit{};
}
+std::unique_ptr<const IdmapConstraints> IdmapConstraints::FromBinaryStream(std::istream& stream) {
+ auto idmap_constraints = std::make_unique<IdmapConstraints>();
+ uint32_t count = 0;
+ if (!Read32(stream, &count)) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < count; i++) {
+ IdmapConstraint constraint{};
+ if (!Read32(stream, &constraint.constraint_type)) {
+ return nullptr;
+ }
+ if (!Read32(stream, &constraint.constraint_value)) {
+ return nullptr;
+ }
+ idmap_constraints->constraints.insert(constraint);
+ }
+
+ return idmap_constraints;
+}
+
std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) {
std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header());
if (!Read32(stream, &idmap_data_header->target_entry_count) ||
@@ -315,6 +335,10 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromBinaryStream(std::istream& strea
if (!idmap->header_) {
return Error("failed to parse idmap header");
}
+ idmap->constraints_ = IdmapConstraints::FromBinaryStream(stream);
+ if (!idmap->constraints_) {
+ return Error("failed to parse idmap constraints");
+ }
// idmap version 0x01 does not specify the number of data blocks that follow
// the idmap header; assume exactly one data block
@@ -374,10 +398,9 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
}
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) {
+ const OverlayResourceContainer& overlay, const std::string& overlay_name,
+ const PolicyBitmask& fulfilled_policies, bool enforce_overlayable,
+ std::unique_ptr<const IdmapConstraints>&& constraints) {
SYSTRACE << "Idmap::FromApkAssets";
std::unique_ptr<IdmapHeader> header(new IdmapHeader());
header->magic_ = kIdmapMagic;
@@ -424,6 +447,11 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromContainers(const TargetResourceC
header->debug_info_ = log_info.GetString();
idmap->header_ = std::move(header);
idmap->data_.push_back(std::move(*idmap_data));
+ if (constraints == nullptr) {
+ idmap->constraints_ = std::make_unique<IdmapConstraints>();
+ } else {
+ idmap->constraints_ = std::move(constraints);
+ }
return {std::move(idmap)};
}
@@ -433,6 +461,11 @@ void IdmapHeader::accept(Visitor* v) const {
v->visit(*this);
}
+void IdmapConstraints::accept(Visitor* v) const {
+ assert(v != nullptr);
+ v->visit(*this);
+}
+
void IdmapData::Header::accept(Visitor* v) const {
assert(v != nullptr);
v->visit(*this);
@@ -447,6 +480,7 @@ void IdmapData::accept(Visitor* v) const {
void Idmap::accept(Visitor* v) const {
assert(v != nullptr);
header_->accept(v);
+ constraints_->accept(v);
v->visit(*this);
auto end = data_.cend();
for (auto iter = data_.cbegin(); iter != end; ++iter) {
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index eb9458268dad..0ec31f4f63f6 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -58,6 +58,19 @@ void PrettyPrintVisitor::visit(const IdmapHeader& header) {
if (auto overlay = OverlayResourceContainer::FromPath(header.GetOverlayPath())) {
overlay_ = std::move(*overlay);
}
+}
+
+void PrettyPrintVisitor::visit(const IdmapConstraints& constraints) {
+ stream_ << "Constraints:" << '\n';
+ if (constraints.constraints.empty()) {
+ stream_ << TAB << "None\n";
+ } else {
+ for (const IdmapConstraint& constraint : constraints.constraints) {
+ stream_ << TAB
+ << base::StringPrintf("Type: %d, Value: %d\n", constraint.constraint_type,
+ constraint.constraint_value);
+ }
+ }
stream_ << "Mapping:" << '\n';
}
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index 9d04a7f87400..41a3da39d872 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -55,6 +55,14 @@ void RawPrintVisitor::visit(const IdmapHeader& header) {
}
}
+void RawPrintVisitor::visit(const IdmapConstraints &idmapConstraints) {
+ print(static_cast<uint32_t>(idmapConstraints.constraints.size()), "constraints count");
+ for (const auto& constraint : idmapConstraints.constraints) {
+ print(constraint.constraint_type, "constraint type");
+ print(constraint.constraint_value, "constraint value");
+ }
+}
+
void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
for (auto& target_entry : data.GetTargetEntries()) {
Result<std::string> target_name(Error(""));
diff --git a/cmds/idmap2/self_targeting/SelfTargeting.cpp b/cmds/idmap2/self_targeting/SelfTargeting.cpp
index 7f9c4686c55a..26888ab17d66 100644
--- a/cmds/idmap2/self_targeting/SelfTargeting.cpp
+++ b/cmds/idmap2/self_targeting/SelfTargeting.cpp
@@ -31,6 +31,7 @@ using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::Idmap;
+using android::idmap2::IdmapConstraints;
using android::idmap2::OverlayResourceContainer;
namespace android::self_targeting {
@@ -155,9 +156,10 @@ CreateIdmapFile(std::string& out_err, const std::string& targetPath, const std::
// Overlay self target process. Only allow self-targeting types.
const auto fulfilled_policies = GetFulfilledPolicy(isSystem, isVendor, isProduct,
isTargetSignature, isOdm, isOem);
-
+ auto constraints = std::make_unique<const IdmapConstraints>();
const auto idmap = Idmap::FromContainers(**target, **overlay, overlayName,
- fulfilled_policies, true /* enforce_overlayable */);
+ fulfilled_policies, true /* enforce_overlayable */,
+ std::move(constraints));
if (!idmap) {
out_err = base::StringPrintf("Failed to create idmap because of %s",
idmap.GetErrorMessage().c_str());
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index f1eeab9c803b..76cccb556ca2 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -58,6 +58,8 @@ TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) {
ASSERT_EQ(idmap1->GetData().size(), 1U);
ASSERT_EQ(idmap1->GetData().size(), idmap2->GetData().size());
+ ASSERT_EQ(idmap1->GetConstraints()->constraints, idmap2->GetConstraints()->constraints);
+
const std::vector<std::unique_ptr<const IdmapData>>& data_blocks1 = idmap1->GetData();
ASSERT_EQ(data_blocks1.size(), 1U);
const std::unique_ptr<const IdmapData>& data1 = data_blocks1[0];
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index 5a7fcd519cfd..760bbb3f72ba 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -105,6 +105,7 @@ TEST_F(Idmap2BinaryTests, Create) {
fin.close();
ASSERT_TRUE(idmap);
+ ASSERT_EQ((*idmap)->GetConstraints()->constraints.size(), 0);
ASSERT_IDMAP(**idmap, GetTargetApkPath(), GetOverlayApkPath());
unlink(GetIdmapPath().c_str());
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 7093614f4047..4de2a6b7c125 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -68,7 +68,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) {
std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
ASSERT_EQ(header->GetMagic(), 0x504d4449U);
- ASSERT_EQ(header->GetVersion(), 10);
+ ASSERT_EQ(header->GetVersion(), 11);
ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
ASSERT_EQ(header->GetFulfilledPolicies(), 0x11);
@@ -96,6 +96,19 @@ TEST(IdmapTests, IdmapFailParsingDifferentMagic) {
ASSERT_FALSE(Idmap::FromBinaryStream(stream));
}
+TEST(IdmapTests, CreateIdmapConstraintsFromBinaryStream) {
+ std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
+ std::istringstream stream(raw);
+ std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
+ std::unique_ptr<const IdmapConstraints> constraints = IdmapConstraints::FromBinaryStream(stream);
+ ASSERT_THAT(constraints, NotNull());
+ ASSERT_EQ(constraints->constraints.size(), 2);
+ IdmapConstraint constraint1{.constraint_type = 0, .constraint_value = 1};
+ IdmapConstraint constraint2{.constraint_type = 1, .constraint_value = 2};
+ ASSERT_NE(constraints->constraints.find(constraint1), constraints->constraints.end());
+ ASSERT_NE(constraints->constraints.find(constraint2), constraints->constraints.end());
+}
+
TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
const size_t offset = kIdmapRawDataOffset;
std::string raw(reinterpret_cast<const char*>(kIdmapRawData + offset), kIdmapRawDataLen - offset);
@@ -143,7 +156,7 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) {
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 10);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 11);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), kIdmapRawDataPolicies);
@@ -195,16 +208,17 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) {
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
ASSERT_TRUE(overlay);
+ auto constraints = std::make_unique<const IdmapConstraints>();
auto idmap_result = Idmap::FromContainers(
**target, **overlay, TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
+ /* enforce_overlayable */ true, std::move(constraints));
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
ASSERT_THAT(idmap, NotNull());
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 10);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 11);
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);
@@ -238,9 +252,10 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssets) {
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
ASSERT_TRUE(overlay);
+ auto constraints = std::make_unique<const IdmapConstraints>();
auto idmap_result = Idmap::FromContainers(
**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
+ /* enforce_overlayable */ true, std::move(constraints));
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
ASSERT_THAT(idmap, NotNull());
@@ -296,8 +311,9 @@ TEST(IdmapTests, FabricatedOverlay) {
auto overlay = OverlayResourceContainer::FromPath(tf.path);
ASSERT_TRUE(overlay);
+ auto constraints = std::make_unique<const IdmapConstraints>();
auto idmap_result = Idmap::FromContainers(**target, **overlay, "SandTheme", PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
+ /* enforce_overlayable */ true, std::move(constraints));
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
ASSERT_THAT(idmap, NotNull());
@@ -341,13 +357,17 @@ TEST(IdmapTests, FailCreateIdmapInvalidName) {
ASSERT_TRUE(overlay);
{
+ auto constraints = std::make_unique<const IdmapConstraints>();
auto idmap_result = Idmap::FromContainers(**target, **overlay, "", PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
+ /* enforce_overlayable */ true,
+ std::move(constraints));
ASSERT_FALSE(idmap_result);
}
{
+ auto constraints = std::make_unique<const IdmapConstraints>();
auto idmap_result = Idmap::FromContainers(**target, **overlay, "unknown", PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
+ /* enforce_overlayable */ true,
+ std::move(constraints));
ASSERT_FALSE(idmap_result);
}
}
@@ -362,9 +382,10 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) {
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
ASSERT_TRUE(overlay);
+ auto constraints = std::make_unique<const IdmapConstraints>();
auto idmap_result = Idmap::FromContainers(
**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
- /* enforce_overlayable */ true);
+ /* enforce_overlayable */ true, std::move(constraints));
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
ASSERT_THAT(idmap, NotNull());
@@ -634,6 +655,10 @@ class TestVisitor : public Visitor {
stream_ << "TestVisitor::visit(IdmapHeader)" << '\n';
}
+ void visit(const IdmapConstraints& idmap ATTRIBUTE_UNUSED) override {
+ stream_ << "TestVisitor::visit(IdmapConstraints)" << '\n';
+ }
+
void visit(const IdmapData& idmap ATTRIBUTE_UNUSED) override {
stream_ << "TestVisitor::visit(IdmapData)" << '\n';
}
@@ -659,6 +684,7 @@ TEST(IdmapTests, TestVisitor) {
ASSERT_EQ(test_stream.str(),
"TestVisitor::visit(IdmapHeader)\n"
+ "TestVisitor::visit(IdmapConstraints)\n"
"TestVisitor::visit(Idmap)\n"
"TestVisitor::visit(IdmapData::Header)\n"
"TestVisitor::visit(IdmapData)\n");
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
index 3d3d82a8c7dd..2f42f798f64a 100644
--- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -42,8 +42,10 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
ASSERT_TRUE(overlay);
+ auto constraints = std::make_unique<const IdmapConstraints>();
const auto idmap = Idmap::FromContainers(**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT,
- PolicyFlags::PUBLIC, /* enforce_overlayable */ true);
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true,
+ std::move(constraints));
ASSERT_TRUE(idmap);
std::stringstream stream;
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index 7fae1c64f014..d5aafe6b8d35 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -55,8 +55,10 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
ASSERT_TRUE(overlay);
+ auto constraints = std::make_unique<const IdmapConstraints>();
const auto idmap = Idmap::FromContainers(**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT,
- PolicyFlags::PUBLIC, /* enforce_overlayable */ true);
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true,
+ std::move(constraints));
ASSERT_TRUE(idmap);
std::stringstream stream;
@@ -64,7 +66,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
(*idmap)->accept(&visitor);
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "0000000a version\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "0000000b version\n", stream.str());
ASSERT_CONTAINS_REGEX(
StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING),
stream.str());
@@ -73,6 +75,7 @@ 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 "00000000 constraints count\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 "00000000 target inline entry value count", stream.str());
@@ -113,7 +116,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
(*idmap)->accept(&visitor);
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "0000000a version\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "0000000b 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());
@@ -124,6 +127,11 @@ 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 "00000002 constraints count\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000000 constraint type\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000001 constraint value\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000001 constraint type\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000002 constraint value\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 "00000001 target inline entry value count", stream.str());
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
index 2b4ebd1ae800..6f645bd01229 100644
--- a/cmds/idmap2/tests/TestHelpers.h
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -34,7 +34,7 @@ const unsigned char kIdmapRawData[] = {
0x49, 0x44, 0x4d, 0x50,
// 0x4: version
- 0x0a, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00,
// 0x8: target crc
0x34, 0x12, 0x00, 0x00,
@@ -73,131 +73,147 @@ const unsigned char kIdmapRawData[] = {
// 0x4c string contents "debug\0\0\0" (padded to word alignment)
0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00,
+ // CONSTRAINTS
+ // 0x54: constraints_count
+ 0x02, 0x00, 0x00, 0x00,
+
+ // 0x58: constraint_type
+ 0x00, 0x00, 0x00, 0x00,
+
+ // 0x5c: constraint_value
+ 0x01, 0x00, 0x00, 0x00,
+
+ // 0x60: constraint_type
+ 0x01, 0x00, 0x00, 0x00,
+
+ // 0x64: constraint_value
+ 0x02, 0x00, 0x00, 0x00,
+
// DATA HEADER
- // 0x54: target_entry_count
+ // 0x68: target_entry_count
0x03, 0x00, 0x00, 0x00,
- // 0x58: target_inline_entry_count
+ // 0x6c: target_inline_entry_count
0x01, 0x00, 0x00, 0x00,
- // 0x5c: target_inline_entry_value_count
+ // 0x70: target_inline_entry_value_count
0x01, 0x00, 0x00, 0x00,
// 0x60: config_count
0x01, 0x00, 0x00, 0x00,
- // 0x64: overlay_entry_count
+ // 0x74: overlay_entry_count
0x03, 0x00, 0x00, 0x00,
- // 0x68: string_pool_offset
+ // 0x78: string_pool_offset
0x00, 0x00, 0x00, 0x00,
// TARGET ENTRIES
- // 0x6c: target id (0x7f020000)
+ // 0x7c: target id (0x7f020000)
0x00, 0x00, 0x02, 0x7f,
- // 0x70: target id (0x7f030000)
+ // 0x80: target id (0x7f030000)
0x00, 0x00, 0x03, 0x7f,
- // 0x74: target id (0x7f030002)
+ // 0x84: target id (0x7f030002)
0x02, 0x00, 0x03, 0x7f,
- // 0x78: overlay_id (0x7f020000)
+ // 0x88: overlay_id (0x7f020000)
0x00, 0x00, 0x02, 0x7f,
- // 0x7c: overlay_id (0x7f030000)
+ // 0x8c: overlay_id (0x7f030000)
0x00, 0x00, 0x03, 0x7f,
- // 0x80: overlay_id (0x7f030001)
+ // 0x90: overlay_id (0x7f030001)
0x01, 0x00, 0x03, 0x7f,
// INLINE TARGET ENTRIES
- // 0x84: target_id
+ // 0x94: target_id
0x00, 0x00, 0x04, 0x7f,
- // 0x88: start value index
+ // 0x98: start value index
0x00, 0x00, 0x00, 0x00,
- // 0x8c: value count
+ // 0x9c: value count
0x01, 0x00, 0x00, 0x00,
// INLINE TARGET ENTRY VALUES
- // 0x90: config index
+ // 0xa0: config index
0x00, 0x00, 0x00, 0x00,
- // 0x94: Res_value::size (value ignored by idmap)
+ // 0xa4: Res_value::size (value ignored by idmap)
0x08, 0x00,
- // 0x98: Res_value::res0 (value ignored by idmap)
+ // 0xa8: Res_value::res0 (value ignored by idmap)
0x00,
- // 0x9c: Res_value::dataType (TYPE_INT_HEX)
+ // 0xac: Res_value::dataType (TYPE_INT_HEX)
0x11,
- // 0xa0: Res_value::data
+ // 0xb0: Res_value::data
0x78, 0x56, 0x34, 0x12,
// CONFIGURATIONS
- // 0xa4: ConfigDescription
+ // 0xb4: ConfigDescription
// size
0x40, 0x00, 0x00, 0x00,
- // 0xa8: imsi
+ // 0xb8: imsi
0x00, 0x00, 0x00, 0x00,
- // 0xac: locale
+ // 0xbc: locale
0x00, 0x00, 0x00, 0x00,
- // 0xb0: screenType
+ // 0xc0: screenType
0x02, 0x00, 0xe0, 0x01,
- // 0xb4: input
+ // 0xc4: input
0x00, 0x00, 0x00, 0x00,
- // 0xb8: screenSize
+ // 0xc8: screenSize
0x00, 0x00, 0x00, 0x00,
- // 0xbc: version
+ // 0xcc: version
0x07, 0x00, 0x00, 0x00,
- // 0xc0: screenConfig
+ // 0xd0: screenConfig
0x00, 0x00, 0x00, 0x00,
- // 0xc4: screenSizeDp
+ // 0xd4: screenSizeDp
0x00, 0x00, 0x00, 0x00,
- // 0xc8: localeScript
+ // 0xd8: localeScript
0x00, 0x00, 0x00, 0x00,
- // 0xcc: localVariant(1)
+ // 0xdc: localVariant(1)
0x00, 0x00, 0x00, 0x00,
- // 0xd0: localVariant(2)
+ // 0xe0: localVariant(2)
0x00, 0x00, 0x00, 0x00,
- // 0xd4: screenConfig2
+ // 0xe4: screenConfig2
0x00, 0x00, 0x00, 0x00,
- // 0xd8: localeScriptWasComputed
+ // 0xe8: localeScriptWasComputed
0x00,
- // 0xd9: localeNumberingSystem(1)
+ // 0xe9: localeNumberingSystem(1)
0x00, 0x00, 0x00, 0x00,
- // 0xdd: localeNumberingSystem(2)
+ // 0xed: localeNumberingSystem(2)
0x00, 0x00, 0x00, 0x00,
- // 0xe1: padding
+ // 0xf1: padding
0x00, 0x00, 0x00,
// OVERLAY ENTRIES
- // 0xe4: 0x7f020000 -> ...
+ // 0xf4: 0x7f020000 -> ...
0x00, 0x00, 0x02, 0x7f,
- // 0xe8: 0x7f030000 -> ...
+ // 0xf8: 0x7f030000 -> ...
0x00, 0x00, 0x03, 0x7f,
- // 0xec: 0x7f030001 -> ...
+ // 0xfc: 0x7f030001 -> ...
0x01, 0x00, 0x03, 0x7f,
- // 0xf0: ... -> 0x7f020000
+ // 0x100: ... -> 0x7f020000
0x00, 0x00, 0x02, 0x7f,
- // 0xf4: ... -> 0x7f030000
+ // 0x104: ... -> 0x7f030000
0x00, 0x00, 0x03, 0x7f,
- // 0xf8: ... -> 0x7f030002
+ // 0x108: ... -> 0x7f030002
0x02, 0x00, 0x03, 0x7f,
- // 0xfc: string pool
+ // 0x10c: string pool
// string length,
0x04, 0x00, 0x00, 0x00,
- // 0x100 string contents "test"
+ // 0x110 string contents "test"
0x74, 0x65, 0x73, 0x74};
constexpr unsigned int kIdmapRawDataLen = std::size(kIdmapRawData);
-const unsigned int kIdmapRawDataOffset = 0x54;
+const unsigned int kIdmapRawDataOffset = 0x68;
const unsigned int kIdmapRawDataTargetCrc = 0x1234;
const unsigned int kIdmapRawOverlayCrc = 0x5678;
const unsigned int kIdmapRawDataPolicies = 0x11;
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index 122ab486948f..d865ba749adc 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -16,10 +16,13 @@
package android.content.om;
+import android.content.om.OverlayConstraint;
import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.content.om.OverlayManagerTransaction;
+import java.util.List;
+
/**
* Api for getting information about overlay packages.
*
@@ -103,6 +106,22 @@ interface IOverlayManager {
boolean setEnabled(in String packageName, in boolean enable, in int userId);
/**
+ * Enable an overlay package for a specific set of constraints. In case of multiple constraints,
+ * the overlay would be enabled when any of the given constraints are satisfied.
+ *
+ * Re-enabling an overlay with new constraints updates the constraints for the overlay.
+ *
+ * The caller must pass the actor requirements specified in the class comment.
+ *
+ * @param packageName the name of the overlay package to enable.
+ * @param user The user for which to change the overlay.
+ * @param constraints list of {@link OverlayConstraint} for enabling the overlay.
+ * @return true if the system successfully registered the request, false otherwise.
+ */
+ boolean enableWithConstraints(in String packageName, in int userId,
+ in List<OverlayConstraint> constraints);
+
+ /**
* Request that an overlay package is enabled and any other overlay packages with the same
* target package are disabled.
*
diff --git a/core/java/android/content/om/OverlayConstraint.aidl b/core/java/android/content/om/OverlayConstraint.aidl
new file mode 100644
index 000000000000..95aac8069617
--- /dev/null
+++ b/core/java/android/content/om/OverlayConstraint.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2025 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 OverlayConstraint;
diff --git a/core/java/android/content/om/OverlayConstraint.java b/core/java/android/content/om/OverlayConstraint.java
new file mode 100644
index 000000000000..c1902def882f
--- /dev/null
+++ b/core/java/android/content/om/OverlayConstraint.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2025 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.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Constraint for enabling a RRO. Currently this can be a displayId or a deviceId, i.e.,
+ * the overlay would be applied only when a target package is running on the given displayId
+ * or deviceId.
+ *
+ * @hide
+ */
+public final class OverlayConstraint implements Parcelable {
+
+ /**
+ * Constraint type for enabling a RRO for a specific display id. For contexts associated with
+ * the default display, this would be {@link android.view.Display#DEFAULT_DISPLAY}, and
+ * for contexts associated with a virtual display, this would be the id of the virtual display.
+ */
+ public static final int TYPE_DISPLAY_ID = 0;
+
+ /**
+ * Constraint type for enabling a RRO for a specific device id. For contexts associated with
+ * the default device, this would be {@link android.content.Context#DEVICE_ID_DEFAULT}, and
+ * for contexts associated with virtual device, this would be the id of the virtual device.
+ */
+ public static final int TYPE_DEVICE_ID = 1;
+
+ @IntDef(prefix = "TYPE_", value = {
+ TYPE_DISPLAY_ID,
+ TYPE_DEVICE_ID,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ConstraintType {
+ }
+
+ @ConstraintType
+ private final int mType;
+ private final int mValue;
+
+ public OverlayConstraint(int type, int value) {
+ if (type != TYPE_DEVICE_ID && type != TYPE_DISPLAY_ID) {
+ throw new IllegalArgumentException(
+ "Type must be either TYPE_DISPLAY_ID or TYPE_DEVICE_ID");
+ }
+ if (value < 0) {
+ throw new IllegalArgumentException("Value must be greater than 0");
+ }
+ this.mType = type;
+ this.mValue = value;
+ }
+
+ private OverlayConstraint(Parcel in) {
+ this(in.readInt(), in.readInt());
+ }
+
+ /**
+ * Returns the type of the constraint.
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the value of the constraint.
+ */
+ public int getValue() {
+ return mValue;
+ }
+
+ @Override
+ public String toString() {
+ return "{type: " + typeToString(mType) + ", value: " + mValue + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof OverlayConstraint that)) {
+ return false;
+ }
+ return mType == that.mType && mValue == that.mValue;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mType, mValue);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeInt(mValue);
+ }
+
+ public static final Creator<OverlayConstraint> CREATOR = new Creator<>() {
+ @Override
+ public OverlayConstraint createFromParcel(Parcel in) {
+ return new OverlayConstraint(in);
+ }
+
+ @Override
+ public OverlayConstraint[] newArray(int size) {
+ return new OverlayConstraint[size];
+ }
+ };
+
+ /**
+ * Returns a string description for a list of constraints.
+ */
+ public static String constraintsToString(final List<OverlayConstraint> overlayConstraints) {
+ if (overlayConstraints == null || overlayConstraints.isEmpty()) {
+ return "None";
+ }
+ return "[" + TextUtils.join(",", overlayConstraints) + "]";
+ }
+
+ private static String typeToString(@ConstraintType int type) {
+ return type == TYPE_DEVICE_ID ? "DEVICE_ID" : "DISPLAY_ID";
+ }
+}
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index 2e898562655b..4977c820ba55 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -30,6 +30,9 @@ import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
/**
@@ -230,12 +233,17 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
private OverlayIdentifier mIdentifierCached;
/**
- *
* @hide
*/
public final boolean isFabricated;
/**
+ * @hide
+ */
+ @NonNull
+ public final List<OverlayConstraint> constraints;
+
+ /**
* Create a new OverlayInfo based on source with an updated state.
*
* @param source the source OverlayInfo to base the new instance on
@@ -246,7 +254,8 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
public OverlayInfo(@NonNull OverlayInfo source, @State int state) {
this(source.packageName, source.overlayName, source.targetPackageName,
source.targetOverlayableName, source.category, source.baseCodePath, state,
- source.userId, source.priority, source.isMutable, source.isFabricated);
+ source.userId, source.priority, source.isMutable, source.isFabricated,
+ source.constraints);
}
/** @hide */
@@ -264,6 +273,17 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
@NonNull String targetPackageName, @Nullable String targetOverlayableName,
@Nullable String category, @NonNull String baseCodePath, int state, int userId,
int priority, boolean isMutable, boolean isFabricated) {
+ this(packageName, overlayName, targetPackageName, targetOverlayableName, category,
+ baseCodePath, state, userId, priority, isMutable, isFabricated,
+ Collections.emptyList() /* constraints */);
+ }
+
+ /** @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,
+ @NonNull List<OverlayConstraint> constraints) {
this.packageName = packageName;
this.overlayName = overlayName;
this.targetPackageName = targetPackageName;
@@ -275,6 +295,7 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
this.priority = priority;
this.isMutable = isMutable;
this.isFabricated = isFabricated;
+ this.constraints = constraints;
ensureValidState();
}
@@ -291,6 +312,7 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
priority = source.readInt();
isMutable = source.readBoolean();
isFabricated = source.readBoolean();
+ constraints = Arrays.asList(source.createTypedArray(OverlayConstraint.CREATOR));
ensureValidState();
}
@@ -395,6 +417,17 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
return mIdentifierCached;
}
+ /**
+ * Returns the currently applied constraints (if any) for the overlay. An overlay
+ * may have constraints only when it is enabled.
+ *
+ * @hide
+ */
+ @NonNull
+ public List<OverlayConstraint> getConstraints() {
+ return constraints;
+ }
+
@SuppressWarnings("ConstantConditions")
private void ensureValidState() {
if (packageName == null) {
@@ -406,6 +439,9 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
if (baseCodePath == null) {
throw new IllegalArgumentException("baseCodePath must not be null");
}
+ if (constraints == null) {
+ throw new IllegalArgumentException("constraints must not be null");
+ }
switch (state) {
case STATE_UNKNOWN:
case STATE_MISSING_TARGET:
@@ -439,20 +475,21 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
dest.writeInt(priority);
dest.writeBoolean(isMutable);
dest.writeBoolean(isFabricated);
+ dest.writeTypedArray(constraints.toArray(new OverlayConstraint[0]), flags);
}
public static final @NonNull Parcelable.Creator<OverlayInfo> CREATOR =
- new Parcelable.Creator<OverlayInfo>() {
- @Override
- public OverlayInfo createFromParcel(Parcel source) {
- return new OverlayInfo(source);
- }
+ new Parcelable.Creator<>() {
+ @Override
+ public OverlayInfo createFromParcel(Parcel source) {
+ return new OverlayInfo(source);
+ }
- @Override
- public OverlayInfo[] newArray(int size) {
- return new OverlayInfo[size];
- }
- };
+ @Override
+ public OverlayInfo[] newArray(int size) {
+ return new OverlayInfo[size];
+ }
+ };
/**
* Return true if this overlay is enabled, i.e. should be used to overlay
@@ -461,6 +498,7 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
* Disabled overlay packages are installed but are currently not in use.
*
* @return true if the overlay is enabled, else false.
+ *
* @hide
*/
@SystemApi
@@ -479,6 +517,7 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
* debugging purposes.
*
* @return a human readable String representing the state.
+ *
* @hide
*/
public static String stateToString(@State int state) {
@@ -522,6 +561,7 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
: targetOverlayableName.hashCode());
result = prime * result + ((category == null) ? 0 : category.hashCode());
result = prime * result + ((baseCodePath == null) ? 0 : baseCodePath.hashCode());
+ result = prime * result + (constraints.isEmpty() ? 0 : constraints.hashCode());
return result;
}
@@ -566,7 +606,7 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
if (!baseCodePath.equals(other.baseCodePath)) {
return false;
}
- return true;
+ return Objects.equals(constraints, other.constraints);
}
/**
@@ -584,6 +624,7 @@ public final class OverlayInfo implements CriticalOverlayInfo, Parcelable {
+ ", targetOverlayable=" + targetOverlayableName
+ ", state=" + state + " (" + stateToString(state) + "),"
+ ", userId=" + userId
+ + ", constraints=" + OverlayConstraint.constraintsToString(constraints)
+ " }";
}
}
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index 6db7dfe4f705..fd9bfa274289 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -59,7 +59,6 @@ import java.util.List;
public class OverlayManager {
private final IOverlayManager mService;
- private final Context mContext;
private final OverlayManagerImpl mOverlayManagerImpl;
/**
@@ -137,7 +136,6 @@ public class OverlayManager {
*/
@SuppressLint("ReferencesHidden")
public OverlayManager(@NonNull Context context, @Nullable IOverlayManager service) {
- mContext = context;
mService = service;
mOverlayManagerImpl = new OverlayManagerImpl(context);
}
@@ -161,7 +159,7 @@ public class OverlayManager {
* @param packageName the name of the overlay package to enable.
* @param user The user for which to change the overlay.
*
- * @throws SecurityException when caller is not allowed to enable {@param packageName}
+ * @throws SecurityException when caller is not allowed to enable {@code packageName}
* @throws IllegalStateException when enabling fails otherwise
*
* @hide
@@ -196,7 +194,7 @@ public class OverlayManager {
* @param enable {@code false} if the overlay should be turned off.
* @param user The user for which to change the overlay.
*
- * @throws SecurityException when caller is not allowed to enable/disable {@param packageName}
+ * @throws SecurityException when caller is not allowed to enable/disable {@code packageName}
* @throws IllegalStateException when enabling/disabling fails otherwise
*
* @hide
@@ -220,6 +218,43 @@ public class OverlayManager {
}
/**
+ * Enable an overlay package for a specific set of constraints. In case of multiple constraints,
+ * the overlay would be enabled when any of the given constraints are satisfied.
+ *
+ * Re-enabling an overlay with new constraints updates the constraints for the overlay.
+ *
+ * The caller must pass the actor requirements specified in the class comment.
+ *
+ * @param packageName the name of the overlay package to enable.
+ * @param user The user for which to change the overlay.
+ * @param constraints list of {@link OverlayConstraint} for enabling the overlay.
+ *
+ * @throws SecurityException when caller is not allowed to enable {@code packageName}
+ * @throws IllegalStateException when enabling fails otherwise
+ *
+ * @see OverlayConstraint
+ *
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ "android.permission.INTERACT_ACROSS_USERS",
+ "android.permission.INTERACT_ACROSS_USERS_FULL"
+ })
+ public void enableWithConstraints(@NonNull final String packageName, @NonNull UserHandle user,
+ @Nullable final List<OverlayConstraint> constraints)
+ throws SecurityException, IllegalStateException {
+ try {
+ if (!mService.enableWithConstraints(packageName, user.getIdentifier(), constraints)) {
+ throw new IllegalStateException("enableWithConstraints failed");
+ }
+ } catch (SecurityException e) {
+ rethrowSecurityException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns information about the overlay with the given package name for
* the specified user.
*
@@ -299,7 +334,6 @@ public class OverlayManager {
@RequiresPermission(anyOf = {
"android.permission.INTERACT_ACROSS_USERS",
})
- @NonNull
public void invalidateCachesForOverlay(@NonNull final String targetPackageName,
@NonNull UserHandle user) {
try {
diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java
index 87b2e9350aa1..f9eb5e010d71 100644
--- a/core/java/android/content/om/OverlayManagerTransaction.java
+++ b/core/java/android/content/om/OverlayManagerTransaction.java
@@ -18,8 +18,6 @@ package android.content.om;
import static android.annotation.SystemApi.Client.SYSTEM_SERVER;
-import static com.android.internal.util.Preconditions.checkNotNull;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -29,13 +27,15 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.text.TextUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
-import java.util.Locale;
import java.util.Objects;
/**
@@ -106,7 +106,9 @@ public final class OverlayManagerTransaction implements Parcelable {
final OverlayIdentifier overlay = source.readParcelable(null, android.content.om.OverlayIdentifier.class);
final int userId = source.readInt();
final Bundle extras = source.readBundle(null);
- mRequests.add(new Request(request, overlay, userId, extras));
+ OverlayConstraint[] constraints = source.createTypedArray(OverlayConstraint.CREATOR);
+ mRequests.add(new Request(request, overlay, userId, extras,
+ Arrays.asList(constraints)));
}
mSelfTargeting = false;
}
@@ -115,6 +117,7 @@ public final class OverlayManagerTransaction implements Parcelable {
* Get the iterator of requests
*
* @return the iterator of request
+ *
* @hide
*/
@SuppressLint("ReferencesHidden")
@@ -145,6 +148,8 @@ public final class OverlayManagerTransaction implements Parcelable {
@IntDef(prefix = "TYPE_", value = {
TYPE_SET_ENABLED,
TYPE_SET_DISABLED,
+ TYPE_REGISTER_FABRICATED,
+ TYPE_UNREGISTER_FABRICATED,
})
@Retention(RetentionPolicy.SOURCE)
@interface RequestType {}
@@ -166,23 +171,51 @@ public final class OverlayManagerTransaction implements Parcelable {
@Nullable
public final Bundle extras;
+ /**
+ * @hide
+ */
+ @NonNull
+ public final List<OverlayConstraint> constraints;
+
public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay,
final int userId) {
- this(type, overlay, userId, null /* extras */);
+ this(type, overlay, userId, null /* extras */,
+ Collections.emptyList() /* constraints */);
}
public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay,
final int userId, @Nullable Bundle extras) {
+ this(type, overlay, userId, extras, Collections.emptyList() /* constraints */);
+ }
+
+ /**
+ * @hide
+ */
+ public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay,
+ final int userId, @NonNull List<OverlayConstraint> constraints) {
+ this(type, overlay, userId, null /* extras */, constraints);
+ }
+
+ /**
+ * @hide
+ */
+ public Request(@RequestType final int type, @NonNull final OverlayIdentifier overlay,
+ final int userId, @Nullable Bundle extras,
+ @NonNull List<OverlayConstraint> constraints) {
this.type = type;
this.overlay = overlay;
this.userId = userId;
this.extras = extras;
+ Objects.requireNonNull(constraints);
+ this.constraints = constraints;
}
@Override
public String toString() {
- return String.format(Locale.US, "Request{type=0x%02x (%s), overlay=%s, userId=%d}",
- type, typeToString(), overlay, userId);
+ return TextUtils.formatSimple(
+ "Request{type=0x%02x (%s), overlay=%s, userId=%d, constraints=%s}",
+ type, typeToString(), overlay, userId,
+ OverlayConstraint.constraintsToString(constraints));
}
/**
@@ -205,6 +238,7 @@ public final class OverlayManagerTransaction implements Parcelable {
/**
* Builder class for OverlayManagerTransaction objects.
* TODO(b/269197647): mark the API used by the systemUI.
+ *
* @hide
*/
public static final class Builder {
@@ -238,11 +272,27 @@ public final class OverlayManagerTransaction implements Parcelable {
/**
* @hide
*/
+ public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable,
+ @NonNull List<OverlayConstraint> constraints) {
+ return setEnabled(overlay, enable, UserHandle.myUserId(), constraints);
+ }
+
+ /**
+ * @hide
+ */
public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable, int userId) {
- checkNotNull(overlay);
+ return setEnabled(overlay, enable, userId, Collections.emptyList() /* constraints */);
+ }
+
+ /**
+ * @hide
+ */
+ public Builder setEnabled(@NonNull OverlayIdentifier overlay, boolean enable, int userId,
+ @NonNull List<OverlayConstraint> constraints) {
+ Objects.requireNonNull(overlay);
@Request.RequestType final int type =
enable ? Request.TYPE_SET_ENABLED : Request.TYPE_SET_DISABLED;
- mRequests.add(new Request(type, overlay, userId));
+ mRequests.add(new Request(type, overlay, userId, constraints));
return this;
}
@@ -251,6 +301,7 @@ public final class OverlayManagerTransaction implements Parcelable {
* applications to overlay on itself resources. The overlay target is itself, or the Android
* package, and the work range is only in caller application.
* @param selfTargeting whether the overlay is self-targeting, the default is false.
+ *
* @hide
*/
public Builder setSelfTargeting(boolean selfTargeting) {
@@ -324,23 +375,24 @@ public final class OverlayManagerTransaction implements Parcelable {
dest.writeParcelable(req.overlay, flags);
dest.writeInt(req.userId);
dest.writeBundle(req.extras);
+ dest.writeTypedArray(req.constraints.toArray(new OverlayConstraint[0]), flags);
}
}
@NonNull
public static final Parcelable.Creator<OverlayManagerTransaction> CREATOR =
- new Parcelable.Creator<OverlayManagerTransaction>() {
-
- @Override
- public OverlayManagerTransaction createFromParcel(Parcel source) {
- return new OverlayManagerTransaction(source);
- }
-
- @Override
- public OverlayManagerTransaction[] newArray(int size) {
- return new OverlayManagerTransaction[size];
- }
- };
+ new Parcelable.Creator<>() {
+
+ @Override
+ public OverlayManagerTransaction createFromParcel(Parcel source) {
+ return new OverlayManagerTransaction(source);
+ }
+
+ @Override
+ public OverlayManagerTransaction[] newArray(int size) {
+ return new OverlayManagerTransaction[size];
+ }
+ };
private static Request generateRegisterFabricatedOverlayRequest(
@NonNull FabricatedOverlay overlay) {
diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index ecb4bb1394b6..5cbb596bb498 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -76,6 +76,14 @@ flag {
}
flag {
+ name: "rro_constraints"
+ is_exported: false
+ namespace: "resource_manager"
+ description: "Feature flag for setting constraints for a RRO"
+ bug: "371801644"
+}
+
+flag {
name: "rro_control_for_android_no_overlayable"
is_exported: true
namespace: "resource_manager"
diff --git a/core/java/com/android/internal/content/om/OverlayManagerImpl.java b/core/java/com/android/internal/content/om/OverlayManagerImpl.java
index 5d4e6a083af4..4b3365221bf5 100644
--- a/core/java/com/android/internal/content/om/OverlayManagerImpl.java
+++ b/core/java/com/android/internal/content/om/OverlayManagerImpl.java
@@ -21,7 +21,6 @@ import static android.content.om.OverlayManagerTransaction.Request.BUNDLE_FABRIC
import static android.content.om.OverlayManagerTransaction.Request.TYPE_REGISTER_FABRICATED;
import static android.content.om.OverlayManagerTransaction.Request.TYPE_UNREGISTER_FABRICATED;
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
import static com.android.internal.content.om.OverlayConfig.DEFAULT_PRIORITY;
@@ -85,7 +84,6 @@ public class OverlayManagerImpl {
*
* @param context the context to create overlay environment
*/
- @VisibleForTesting(visibility = PACKAGE)
public OverlayManagerImpl(@NonNull Context context) {
mContext = Objects.requireNonNull(context);
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 3ecd82b074a1..095be57a5dc8 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -55,6 +55,13 @@ struct Idmap_header {
// without having to read/store each header entry separately.
};
+struct Idmap_constraint {
+ // Constraint type can be TYPE_DISPLAY_ID or TYP_DEVICE_ID, please refer
+ // to ConstraintType in OverlayConstraint.java
+ uint32_t constraint_type;
+ uint32_t constraint_value;
+};
+
struct Idmap_data_header {
uint32_t target_entry_count;
uint32_t target_inline_entry_count;
@@ -254,13 +261,18 @@ std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size
#endif
LoadedIdmap::LoadedIdmap(const std::string& idmap_path, const Idmap_header* header,
- const Idmap_data_header* data_header, Idmap_target_entries target_entries,
+ const Idmap_constraint* constraints,
+ uint32_t constraints_count,
+ const Idmap_data_header* data_header,
+ Idmap_target_entries target_entries,
Idmap_target_inline_entries target_inline_entries,
const Idmap_target_entry_inline_value* inline_entry_values,
const ConfigDescription* configs, Idmap_overlay_entries overlay_entries,
std::unique_ptr<ResStringPool>&& string_pool,
std::string_view overlay_apk_path, std::string_view target_apk_path)
: header_(header),
+ constraints_(constraints),
+ constraints_count_(constraints_count),
data_header_(data_header),
target_entries_(target_entries),
target_inline_entries_(target_inline_entries),
@@ -298,9 +310,9 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPie
return {};
}
std::optional<std::string_view> target_path = ReadString(&data_ptr, &data_size, "target path");
- if (!target_path) {
- return {};
- }
+ if (!target_path) {
+ return {};
+ }
std::optional<std::string_view> overlay_path = ReadString(&data_ptr, &data_size, "overlay path");
if (!overlay_path) {
return {};
@@ -310,6 +322,17 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPie
return {};
}
+ auto constraints_count = ReadType<uint32_t>(&data_ptr, &data_size, "constraints count");
+ if (!constraints_count) {
+ return {};
+ }
+ auto constraints = *constraints_count > 0 ?
+ ReadType<Idmap_constraint>(&data_ptr, &data_size, "constraints", *constraints_count)
+ : nullptr;
+ if (*constraints_count > 0 && !constraints) {
+ return {};
+ }
+
// Parse the idmap data blocks. Currently idmap2 can only generate one data block.
auto data_header = ReadType<Idmap_data_header>(&data_ptr, &data_size, "data header");
if (data_header == nullptr) {
@@ -376,9 +399,10 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPie
// Can't use make_unique because LoadedIdmap constructor is private.
return std::unique_ptr<LoadedIdmap>(
- new LoadedIdmap(std::string(idmap_path), header, data_header, target_entries,
- target_inline_entries, target_inline_entry_values, configurations,
- overlay_entries, std::move(idmap_string_pool), *overlay_path, *target_path));
+ new LoadedIdmap(std::string(idmap_path), header, constraints, *constraints_count,
+ data_header, target_entries, target_inline_entries,
+ target_inline_entry_values,configurations, overlay_entries,
+ std::move(idmap_string_pool),*overlay_path, *target_path));
}
bool LoadedIdmap::IsUpToDate() const {
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index de9991a8be5e..978bc768cd3d 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -290,11 +290,11 @@ static bool assertIdmapHeader(const void* idmap, size_t size) {
}
const uint32_t version = htodl(*(reinterpret_cast<const uint32_t*>(idmap) + 1));
- if (version != ResTable::IDMAP_CURRENT_VERSION) {
+ if (version != kIdmapCurrentVersion) {
// We are strict about versions because files with this format are
// auto-generated and don't need backwards compatibility.
ALOGW("idmap: version mismatch in header (is 0x%08x, expected 0x%08x)",
- version, ResTable::IDMAP_CURRENT_VERSION);
+ version, kIdmapCurrentVersion);
return false;
}
return true;
@@ -400,14 +400,18 @@ status_t parseIdmap(const void* idmap, size_t size, uint8_t* outPackageId, Keyed
return UNKNOWN_ERROR;
}
- size -= ResTable::IDMAP_HEADER_SIZE_BYTES;
+ size_t sizeOfHeaderAndConstraints = ResTable::IDMAP_HEADER_SIZE_BYTES +
+ // This accounts for zero constraints, and hence takes only 4 bytes for
+ // the constraints count.
+ ResTable::IDMAP_CONSTRAINTS_COUNT_SIZE_BYTES;
+ size -= sizeOfHeaderAndConstraints;
if (size < sizeof(uint16_t) * 2) {
ALOGE("idmap: too small to contain any mapping");
return UNKNOWN_ERROR;
}
const uint16_t* data = reinterpret_cast<const uint16_t*>(
- reinterpret_cast<const uint8_t*>(idmap) + ResTable::IDMAP_HEADER_SIZE_BYTES);
+ reinterpret_cast<const uint8_t*>(idmap) + sizeOfHeaderAndConstraints);
uint16_t targetPackageId = dtohs(*(data++));
if (targetPackageId == 0 || targetPackageId > 255) {
@@ -7492,7 +7496,7 @@ status_t ResTable::createIdmap(const ResTable& targetResTable,
// write idmap header
uint32_t* data = reinterpret_cast<uint32_t*>(*outData);
*data++ = htodl(IDMAP_MAGIC); // write: magic
- *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION); // write: version
+ *data++ = htodl(kIdmapCurrentVersion); // write: version
*data++ = htodl(targetCrc); // write: target crc
*data++ = htodl(overlayCrc); // write: overlay crc
@@ -7507,6 +7511,9 @@ status_t ResTable::createIdmap(const ResTable& targetResTable,
}
data += (2 * 256) / sizeof(uint32_t);
+ // write zero constraints count (no constraints)
+ *data++ = htodl(0);
+
// write idmap data header
uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
*typeData++ = htods(targetPackageStruct->id); // write: target package id
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index ac75eb3bb98c..d1db13f53069 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -35,6 +35,7 @@ namespace android {
class LoadedIdmap;
class IdmapResMap;
struct Idmap_header;
+struct Idmap_constraint;
struct Idmap_data_header;
struct Idmap_target_entry;
struct Idmap_target_entry_inline;
@@ -203,6 +204,8 @@ class LoadedIdmap {
LoadedIdmap() = default;
const Idmap_header* header_;
+ const Idmap_constraint* constraints_;
+ uint32_t constraints_count_;
const Idmap_data_header* data_header_;
Idmap_target_entries target_entries_;
Idmap_target_inline_entries target_inline_entries_;
@@ -220,7 +223,10 @@ class LoadedIdmap {
DISALLOW_COPY_AND_ASSIGN(LoadedIdmap);
explicit LoadedIdmap(const std::string& idmap_path, const Idmap_header* header,
- const Idmap_data_header* data_header, Idmap_target_entries target_entries,
+ const Idmap_constraint* constraints,
+ uint32_t constraints_count,
+ const Idmap_data_header* data_header,
+ Idmap_target_entries target_entries,
Idmap_target_inline_entries target_inline_entries,
const Idmap_target_entry_inline_value* inline_entry_values_,
const ConfigDescription* configs, Idmap_overlay_entries overlay_entries,
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index e330410ed1a0..8b2871c21a1e 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -48,7 +48,7 @@
namespace android {
constexpr const uint32_t kIdmapMagic = 0x504D4449u;
-constexpr const uint32_t kIdmapCurrentVersion = 0x0000000Au;
+constexpr const uint32_t kIdmapCurrentVersion = 0x0000000Bu;
// This must never change.
constexpr const uint32_t kFabricatedOverlayMagic = 0x4f525246; // FRRO (big endian)
@@ -2267,7 +2267,7 @@ public:
void** outData, size_t* outSize) const;
static const size_t IDMAP_HEADER_SIZE_BYTES = 4 * sizeof(uint32_t) + 2 * 256;
- static const uint32_t IDMAP_CURRENT_VERSION = 0x00000001;
+ static const size_t IDMAP_CONSTRAINTS_COUNT_SIZE_BYTES = sizeof(uint32_t);
// Retrieve idmap meta-data.
//
diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap
index 7e4b261cf109..6bd57c8d517c 100644
--- a/libs/androidfw/tests/data/overlay/overlay.idmap
+++ b/libs/androidfw/tests/data/overlay/overlay.idmap
Binary files differ
diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java
index d33c860343c5..9e311511c24f 100644
--- a/services/core/java/com/android/server/om/IdmapDaemon.java
+++ b/services/core/java/com/android/server/om/IdmapDaemon.java
@@ -26,6 +26,7 @@ import android.os.FabricatedOverlayInfo;
import android.os.FabricatedOverlayInternal;
import android.os.IBinder;
import android.os.IIdmap2;
+import android.os.OverlayConstraint;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
@@ -135,8 +136,8 @@ class IdmapDaemon {
}
String createIdmap(@NonNull String targetPath, @NonNull String overlayPath,
- @Nullable String overlayName, int policies, boolean enforce, int userId)
- throws TimeoutException, RemoteException {
+ @Nullable String overlayName, int policies, boolean enforce, int userId,
+ @NonNull OverlayConstraint[] constraints) throws TimeoutException, RemoteException {
try (Connection c = connect()) {
final IIdmap2 idmap2 = c.getIdmap2();
if (idmap2 == null) {
@@ -147,7 +148,7 @@ class IdmapDaemon {
}
return idmap2.createIdmap(targetPath, overlayPath, TextUtils.emptyIfNull(overlayName),
- policies, enforce, userId);
+ policies, enforce, userId, constraints);
}
}
@@ -165,8 +166,8 @@ class IdmapDaemon {
}
boolean verifyIdmap(@NonNull String targetPath, @NonNull String overlayPath,
- @Nullable String overlayName, int policies, boolean enforce, int userId)
- throws Exception {
+ @Nullable String overlayName, int policies, boolean enforce, int userId,
+ @NonNull OverlayConstraint[] constraints) throws Exception {
try (Connection c = connect()) {
final IIdmap2 idmap2 = c.getIdmap2();
if (idmap2 == null) {
@@ -177,7 +178,7 @@ class IdmapDaemon {
}
return idmap2.verifyIdmap(targetPath, overlayPath, TextUtils.emptyIfNull(overlayName),
- policies, enforce, userId);
+ policies, enforce, userId, constraints);
}
}
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 86d05d92c95b..4e86aa00657d 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -22,6 +22,7 @@ import static com.android.server.om.OverlayManagerService.TAG;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
+import android.content.om.OverlayConstraint;
import android.content.om.OverlayInfo;
import android.content.om.OverlayableInfo;
import android.os.Build.VERSION_CODES;
@@ -102,7 +103,8 @@ final class IdmapManager {
*/
@IdmapStatus int createIdmap(@NonNull final AndroidPackage targetPackage,
@NonNull PackageState overlayPackageState, @NonNull final AndroidPackage overlayPackage,
- String overlayBasePath, String overlayName, @UserIdInt int userId) {
+ String overlayBasePath, String overlayName, @UserIdInt int userId,
+ @NonNull final List<OverlayConstraint> constraints) {
if (DEBUG) {
Slog.d(TAG, "create idmap for " + targetPackage.getPackageName() + " and "
+ overlayPackage.getPackageName());
@@ -112,12 +114,13 @@ final class IdmapManager {
int policies = calculateFulfilledPolicies(targetPackage, overlayPackageState,
overlayPackage, userId);
boolean enforce = enforceOverlayable(overlayPackageState, overlayPackage);
+ android.os.OverlayConstraint[] idmapConstraints = toIdmapConstraints(constraints);
if (mIdmapDaemon.verifyIdmap(targetPath, overlayBasePath, overlayName, policies,
- enforce, userId)) {
+ enforce, userId, idmapConstraints)) {
return IDMAP_IS_VERIFIED;
}
final boolean idmapCreated = mIdmapDaemon.createIdmap(targetPath, overlayBasePath,
- overlayName, policies, enforce, userId) != null;
+ overlayName, policies, enforce, userId, idmapConstraints) != null;
return (idmapCreated) ? IDMAP_IS_MODIFIED | IDMAP_IS_VERIFIED : IDMAP_NOT_EXIST;
} catch (Exception e) {
Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
@@ -275,4 +278,19 @@ final class IdmapManager {
return false;
}
+
+ @NonNull
+ private static android.os.OverlayConstraint[] toIdmapConstraints(
+ @NonNull final List<OverlayConstraint> constraints) {
+ android.os.OverlayConstraint[] idmapConstraints =
+ new android.os.OverlayConstraint[constraints.size()];
+ int index = 0;
+ for (OverlayConstraint constraint : constraints) {
+ android.os.OverlayConstraint idmapConstraint = new android.os.OverlayConstraint();
+ idmapConstraint.type = constraint.getType();
+ idmapConstraint.value = constraint.getValue();
+ idmapConstraints[index++] = idmapConstraint;
+ }
+ return idmapConstraints;
+ }
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 8710438d76b3..847da8642100 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -46,6 +46,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.om.IOverlayManager;
+import android.content.om.OverlayConstraint;
import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.content.om.OverlayManagerTransaction;
@@ -655,6 +656,18 @@ public final class OverlayManagerService extends SystemService {
@Override
public boolean setEnabled(@Nullable final String packageName, final boolean enable,
int userIdArg) {
+ return setEnabled(packageName, enable, userIdArg,
+ Collections.emptyList() /* constraints */);
+ }
+
+ @Override
+ public boolean enableWithConstraints(@Nullable final String packageName, int userIdArg,
+ @NonNull final List<OverlayConstraint> constraints) {
+ return setEnabled(packageName, true /* enable */, userIdArg, constraints);
+ }
+
+ private boolean setEnabled(@Nullable final String packageName, final boolean enable,
+ int userIdArg, @NonNull final List<OverlayConstraint> constraints) {
if (packageName == null) {
return false;
}
@@ -671,7 +684,7 @@ public final class OverlayManagerService extends SystemService {
synchronized (mLock) {
try {
updateTargetPackagesLocked(
- mImpl.setEnabled(overlay, enable, realUserId));
+ mImpl.setEnabled(overlay, enable, realUserId, constraints));
return true;
} catch (OperationFailedException e) {
return false;
@@ -967,13 +980,15 @@ public final class OverlayManagerService extends SystemService {
case TYPE_SET_ENABLED:
Set<UserPackage> result = null;
result = CollectionUtils.addAll(result,
- mImpl.setEnabled(request.overlay, true, realUserId));
+ mImpl.setEnabled(request.overlay, true /* enable */, realUserId,
+ request.constraints));
result = CollectionUtils.addAll(result,
mImpl.setHighestPriority(request.overlay, realUserId));
return CollectionUtils.emptyIfNull(result);
case TYPE_SET_DISABLED:
- return mImpl.setEnabled(request.overlay, false, realUserId);
+ return mImpl.setEnabled(request.overlay, false /* enable */, realUserId,
+ request.constraints);
case TYPE_REGISTER_FABRICATED:
final FabricatedOverlayInternal fabricated =
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 0e9ec4d71421..bcebe0b07d91 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -34,11 +34,13 @@ import static com.android.server.om.OverlayManagerService.TAG;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.om.CriticalOverlayInfo;
+import android.content.om.OverlayConstraint;
import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.content.pm.UserPackage;
import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.FrameworkParsingPackageUtils;
+import android.content.res.Flags;
import android.os.FabricatedOverlayInfo;
import android.os.FabricatedOverlayInternal;
import android.text.TextUtils;
@@ -246,7 +248,7 @@ final class OverlayManagerServiceImpl {
+ oi.targetPackageName + "' in category '" + oi.category + "' for user "
+ newUserId);
mSettings.setEnabled(overlay, newUserId, true);
- if (updateState(oi, newUserId, 0)) {
+ if (updateState(oi, newUserId, 0, oi.constraints)) {
CollectionUtils.add(updatedTargets,
UserPackage.of(oi.userId, oi.targetPackageName));
}
@@ -338,7 +340,7 @@ final class OverlayManagerServiceImpl {
for (int i = 0, n = overlays.size(); i < n; i++) {
final OverlayInfo oi = overlays.get(i);
try {
- modified |= updateState(oi, userId, flags);
+ modified |= updateState(oi, userId, flags, oi.constraints);
} catch (OverlayManagerSettings.BadKeyException e) {
Slog.e(TAG, "failed to update settings", e);
modified |= mSettings.remove(oi.getOverlayIdentifier(), userId);
@@ -386,7 +388,7 @@ final class OverlayManagerServiceImpl {
}
// Update the enabled state of the overlay.
- if (updateState(currentInfo, userId, flags)) {
+ if (updateState(currentInfo, userId, flags, currentInfo.constraints)) {
updatedTargets = CollectionUtils.add(updatedTargets,
UserPackage.of(userId, currentInfo.targetPackageName));
}
@@ -440,10 +442,22 @@ final class OverlayManagerServiceImpl {
@NonNull
Set<UserPackage> setEnabled(@NonNull final OverlayIdentifier overlay,
- final boolean enable, final int userId) throws OperationFailedException {
+ final boolean enable, final int userId,
+ @NonNull final List<OverlayConstraint> constraints)
+ throws OperationFailedException {
if (DEBUG) {
- Slog.d(TAG, String.format("setEnabled overlay=%s enable=%s userId=%d",
- overlay, enable, userId));
+ Slog.d(TAG, TextUtils.formatSimple(
+ "setEnabled overlay=%s enable=%s userId=%d constraints=%s",
+ overlay, enable, userId, OverlayConstraint.constraintsToString(constraints)));
+ }
+
+ boolean hasConstraints = constraints != null && !constraints.isEmpty();
+ if (!Flags.rroConstraints() && hasConstraints) {
+ throw new OperationFailedException("RRO constraints are not supported");
+ }
+ if (!enable && hasConstraints) {
+ throw new OperationFailedException(
+ "Constraints can only be set when enabling an overlay");
}
try {
@@ -455,7 +469,7 @@ final class OverlayManagerServiceImpl {
}
boolean modified = mSettings.setEnabled(overlay, userId, enable);
- modified |= updateState(oi, userId, 0);
+ modified |= updateState(oi, userId, 0, constraints);
if (modified) {
return Set.of(UserPackage.of(userId, oi.targetPackageName));
@@ -469,7 +483,7 @@ final class OverlayManagerServiceImpl {
Optional<UserPackage> setEnabledExclusive(@NonNull final OverlayIdentifier overlay,
boolean withinCategory, final int userId) throws OperationFailedException {
if (DEBUG) {
- Slog.d(TAG, String.format("setEnabledExclusive overlay=%s"
+ Slog.d(TAG, TextUtils.formatSimple("setEnabledExclusive overlay=%s"
+ " withinCategory=%s userId=%d", overlay, withinCategory, userId));
}
@@ -501,12 +515,16 @@ final class OverlayManagerServiceImpl {
// Disable the overlay.
modified |= mSettings.setEnabled(disabledOverlay, userId, false);
- modified |= updateState(disabledInfo, userId, 0);
+ modified |= updateState(disabledInfo, userId, 0 /* flags */,
+ Collections.emptyList() /* constraints */);
}
// Enable the selected overlay.
modified |= mSettings.setEnabled(overlay, userId, true);
- modified |= updateState(enabledInfo, userId, 0);
+ // No constraints should be applied when exclusively enabling an overlay within
+ // a category.
+ modified |= updateState(enabledInfo, userId, 0 /* flags */,
+ Collections.emptyList() /* constraints */);
if (modified) {
return Optional.of(UserPackage.of(userId, enabledInfo.targetPackageName));
@@ -569,7 +587,8 @@ final class OverlayManagerServiceImpl {
// overlay.
mSettings.setBaseCodePath(overlayIdentifier, userId, info.path);
}
- if (updateState(oi, userId, 0)) {
+ // No constraints should be applied when registering a fabricated overlay.
+ if (updateState(oi, userId, 0 /* flags */, Collections.emptyList() /* constraints */)) {
updatedTargets.add(UserPackage.of(userId, oi.targetPackageName));
}
} catch (OverlayManagerSettings.BadKeyException e) {
@@ -670,7 +689,7 @@ final class OverlayManagerServiceImpl {
Set<UserPackage> setHighestPriority(@NonNull final OverlayIdentifier overlay,
final int userId) throws OperationFailedException {
- try{
+ try {
if (DEBUG) {
Slog.d(TAG, "setHighestPriority overlay=" + overlay + " userId=" + userId);
}
@@ -693,7 +712,7 @@ final class OverlayManagerServiceImpl {
Optional<UserPackage> setLowestPriority(@NonNull final OverlayIdentifier overlay,
final int userId) throws OperationFailedException {
- try{
+ try {
if (DEBUG) {
Slog.d(TAG, "setLowestPriority packageName=" + overlay + " userId=" + userId);
}
@@ -793,7 +812,8 @@ final class OverlayManagerServiceImpl {
* Returns true if the settings/state was modified, false otherwise.
*/
private boolean updateState(@NonNull final CriticalOverlayInfo info,
- final int userId, final int flags) throws OverlayManagerSettings.BadKeyException {
+ final int userId, final int flags, @NonNull final List<OverlayConstraint> constraints)
+ throws OverlayManagerSettings.BadKeyException {
final OverlayIdentifier overlay = info.getOverlayIdentifier();
var targetPackageState =
mPackageManager.getPackageStateForUser(info.getTargetPackageName(), userId);
@@ -812,6 +832,7 @@ final class OverlayManagerServiceImpl {
}
modified |= mSettings.setCategory(overlay, userId, overlayPackage.getOverlayCategory());
+ modified |= mSettings.setConstraints(overlay, userId, constraints);
if (!info.isFabricated()) {
modified |= mSettings.setBaseCodePath(overlay, userId,
overlayPackage.getSplits().get(0).getPath());
@@ -826,7 +847,7 @@ final class OverlayManagerServiceImpl {
&& !isPackageConfiguredMutable(overlayPackage))) {
idmapStatus = mIdmapManager.createIdmap(targetPackage, overlayPackageState,
overlayPackage, updatedOverlayInfo.baseCodePath, overlay.getOverlayName(),
- userId);
+ userId, updatedOverlayInfo.constraints);
modified |= (idmapStatus & IDMAP_IS_MODIFIED) != 0;
}
@@ -835,7 +856,7 @@ final class OverlayManagerServiceImpl {
userId, flags, idmapStatus);
if (currentState != newState) {
if (DEBUG) {
- Slog.d(TAG, String.format("%s:%d: %s -> %s",
+ Slog.d(TAG, TextUtils.formatSimple("%s:%d: %s -> %s",
overlay, userId,
OverlayInfo.stateToString(currentState),
OverlayInfo.stateToString(newState)));
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index f9758fcd5d01..4e5c73209be2 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -21,6 +21,7 @@ import static com.android.server.om.OverlayManagerService.TAG;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.om.OverlayConstraint;
import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.os.UserHandle;
@@ -44,6 +45,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -78,7 +80,8 @@ final class OverlayManagerSettings {
remove(overlay, userId);
final SettingsItem item = new SettingsItem(overlay, userId, targetPackageName,
targetOverlayableName, baseCodePath, OverlayInfo.STATE_UNKNOWN, isEnabled,
- isMutable, priority, overlayCategory, isFabricated);
+ isMutable, priority, overlayCategory, isFabricated,
+ Collections.emptyList() /* constraints */);
insert(item);
return item.getOverlayInfo();
}
@@ -155,6 +158,15 @@ final class OverlayManagerSettings {
return mItems.get(idx).setEnabled(enable);
}
+ boolean setConstraints(@NonNull final OverlayIdentifier overlay, final int userId,
+ @NonNull final List<OverlayConstraint> constraints) throws BadKeyException {
+ final int idx = select(overlay, userId);
+ if (idx < 0) {
+ throw new BadKeyException(overlay, userId);
+ }
+ return mItems.get(idx).setConstraints(constraints);
+ }
+
@OverlayInfo.State int getState(@NonNull final OverlayIdentifier overlay, final int userId)
throws BadKeyException {
final int idx = select(overlay, userId);
@@ -400,6 +412,8 @@ final class OverlayManagerSettings {
pw.println("mPriority..............: " + item.mPriority);
pw.println("mCategory..............: " + item.mCategory);
pw.println("mIsFabricated..........: " + item.mIsFabricated);
+ pw.println("mConstraints...........: "
+ + OverlayConstraint.constraintsToString(item.mConstraints));
pw.decreaseIndent();
pw.println("}");
@@ -456,6 +470,7 @@ final class OverlayManagerSettings {
static final class Serializer {
private static final String TAG_OVERLAYS = "overlays";
private static final String TAG_ITEM = "item";
+ private static final String TAG_CONSTRAINT = "constraint";
private static final String ATTR_BASE_CODE_PATH = "baseCodePath";
private static final String ATTR_IS_ENABLED = "isEnabled";
@@ -471,8 +486,11 @@ final class OverlayManagerSettings {
private static final String ATTR_VERSION = "version";
private static final String ATTR_IS_FABRICATED = "fabricated";
+ private static final String ATTR_CONSTRAINT_TYPE = "type";
+ private static final String ATTR_CONSTRAINT_VALUE = "value";
+
@VisibleForTesting
- static final int CURRENT_VERSION = 4;
+ static final int CURRENT_VERSION = 5;
public static void restore(@NonNull final ArrayList<SettingsItem> table,
@NonNull final InputStream is) throws IOException, XmlPullParserException {
@@ -502,7 +520,7 @@ final class OverlayManagerSettings {
// and overwritten.
throw new XmlPullParserException("old version " + oldVersion + "; ignoring");
case 3:
- // Upgrading from version 3 to 4 is not a breaking change so do not ignore the
+ // Upgrading from version 3 to 5 is not a breaking change so do not ignore the
// overlay file.
return;
default:
@@ -529,12 +547,23 @@ final class OverlayManagerSettings {
final boolean isFabricated = parser.getAttributeBoolean(null, ATTR_IS_FABRICATED,
false);
+ final List<OverlayConstraint> constraints = new ArrayList<>();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (TAG_CONSTRAINT.equals(parser.getName())) {
+ final OverlayConstraint constraint = new OverlayConstraint(
+ parser.getAttributeInt(null, ATTR_CONSTRAINT_TYPE),
+ parser.getAttributeInt(null, ATTR_CONSTRAINT_VALUE));
+ constraints.add(constraint);
+ }
+ }
+
return new SettingsItem(overlay, userId, targetPackageName, targetOverlayableName,
- baseCodePath, state, isEnabled, !isStatic, priority, category, isFabricated);
+ baseCodePath, state, isEnabled, !isStatic, priority, category, isFabricated,
+ constraints);
}
public static void persist(@NonNull final ArrayList<SettingsItem> table,
- @NonNull final OutputStream os) throws IOException, XmlPullParserException {
+ @NonNull final OutputStream os) throws IOException {
final TypedXmlSerializer xml = Xml.resolveSerializer(os);
xml.startDocument(null, true);
xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
@@ -566,6 +595,14 @@ final class OverlayManagerSettings {
xml.attributeInt(null, ATTR_PRIORITY, item.mPriority);
XmlUtils.writeStringAttribute(xml, ATTR_CATEGORY, item.mCategory);
XmlUtils.writeBooleanAttribute(xml, ATTR_IS_FABRICATED, item.mIsFabricated);
+
+ for (OverlayConstraint constraint : item.mConstraints) {
+ xml.startTag(null, TAG_CONSTRAINT);
+ xml.attributeInt(null, ATTR_CONSTRAINT_TYPE, constraint.getType());
+ xml.attributeInt(null, ATTR_CONSTRAINT_VALUE, constraint.getValue());
+ xml.endTag(null, TAG_CONSTRAINT);
+ }
+
xml.endTag(null, TAG_ITEM);
}
}
@@ -579,17 +616,19 @@ final class OverlayManagerSettings {
private @OverlayInfo.State int mState;
private boolean mIsEnabled;
private OverlayInfo mCache;
- private boolean mIsMutable;
+ private final boolean mIsMutable;
private int mPriority;
private String mCategory;
- private boolean mIsFabricated;
+ private final boolean mIsFabricated;
+ @NonNull
+ private List<OverlayConstraint> mConstraints;
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,
- final boolean isFabricated) {
+ final boolean isFabricated, @NonNull final List<OverlayConstraint> constraints) {
mOverlay = overlay;
mUserId = userId;
mTargetPackageName = targetPackageName;
@@ -602,6 +641,8 @@ final class OverlayManagerSettings {
mIsMutable = isMutable;
mPriority = priority;
mIsFabricated = isFabricated;
+ Objects.requireNonNull(constraints);
+ mConstraints = constraints;
}
private String getTargetPackageName() {
@@ -668,11 +709,26 @@ final class OverlayManagerSettings {
return false;
}
+ private boolean setConstraints(@NonNull List<OverlayConstraint> constraints) {
+ Objects.requireNonNull(constraints);
+
+ if (!mIsMutable) {
+ return false;
+ }
+
+ if (!Objects.equals(mConstraints, constraints)) {
+ mConstraints = constraints;
+ invalidateCache();
+ return true;
+ }
+ return false;
+ }
+
private OverlayInfo getOverlayInfo() {
if (mCache == null) {
mCache = new OverlayInfo(mOverlay.getPackageName(), mOverlay.getOverlayName(),
mTargetPackageName, mTargetOverlayableName, mCategory, mBaseCodePath,
- mState, mUserId, mPriority, mIsMutable, mIsFabricated);
+ mState, mUserId, mPriority, mIsMutable, mIsFabricated, mConstraints);
}
return mCache;
}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayConstraintsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayConstraintsTests.java
new file mode 100644
index 000000000000..b2e296a36b93
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayConstraintsTests.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2025 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.server.om;
+
+import static android.content.Context.DEVICE_ID_DEFAULT;
+import static android.content.om.OverlayConstraint.TYPE_DEVICE_ID;
+import static android.content.om.OverlayConstraint.TYPE_DISPLAY_ID;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.om.FabricatedOverlay;
+import android.content.om.OverlayConstraint;
+import android.content.om.OverlayIdentifier;
+import android.content.om.OverlayInfo;
+import android.content.om.OverlayManager;
+import android.content.om.OverlayManagerTransaction;
+import android.content.res.Flags;
+import android.os.UserHandle;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.util.TypedValue;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(JUnitParamsRunner.class)
+public class OverlayConstraintsTests {
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private OverlayManager mOverlayManager;
+ private UserHandle mUserHandle;
+ private OverlayIdentifier mOverlayIdentifier = null;
+
+ @Before
+ public void setUp() throws Exception {
+ mOverlayManager = getApplicationContext().getSystemService(OverlayManager.class);
+ mUserHandle = UserHandle.of(UserHandle.myUserId());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mOverlayIdentifier != null) {
+ OverlayManagerTransaction transaction =
+ new OverlayManagerTransaction.Builder()
+ .unregisterFabricatedOverlay(mOverlayIdentifier)
+ .build();
+ mOverlayManager.commit(transaction);
+ mOverlayIdentifier = null;
+ }
+ }
+
+ @Test
+ public void createOverlayConstraint_withInvalidType_fails() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new OverlayConstraint(500 /* type */, 1 /* value */));
+ }
+
+ @Test
+ public void createOverlayConstraint_withInvalidValue_fails() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new OverlayConstraint(TYPE_DEVICE_ID, -1 /* value */));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS)
+ public void enableOverlayWithNullConstraints_fails() {
+ FabricatedOverlay fabricatedOverlay = createFabricatedOverlay();
+ assertThrows(NullPointerException.class,
+ () -> mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+ .registerFabricatedOverlay(fabricatedOverlay)
+ .setEnabled(fabricatedOverlay.getIdentifier(), true /* enable */,
+ null /* constraints */)
+ .build()));
+ }
+
+ @Test
+ @Parameters(method = "getAllConstraintLists")
+ @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS)
+ public void enableOverlayWithConstraints_writesConstraintsIntoOverlayInfo(
+ List<OverlayConstraint> constraints) throws Exception {
+ enableOverlay(constraints);
+
+ OverlayInfo overlayInfo = mOverlayManager.getOverlayInfo(mOverlayIdentifier, mUserHandle);
+ assertNotNull(overlayInfo);
+ assertEquals(constraints, overlayInfo.getConstraints());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS)
+ public void disableOverlayWithConstraints_fails() throws Exception {
+ FabricatedOverlay fabricatedOverlay = createFabricatedOverlay();
+ assertThrows(SecurityException.class,
+ () -> mOverlayManager.commit(new OverlayManagerTransaction.Builder()
+ .registerFabricatedOverlay(fabricatedOverlay)
+ .setEnabled(fabricatedOverlay.getIdentifier(), false /* enable */,
+ List.of(new OverlayConstraint(TYPE_DISPLAY_ID, DEFAULT_DISPLAY)))
+ .build()));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS)
+ public void enableOverlayWithNewConstraints_updatesConstraintsIntoOverlayInfo()
+ throws Exception {
+ List<OverlayConstraint> constraints1 =
+ List.of(new OverlayConstraint(TYPE_DISPLAY_ID, 1 /* value*/));
+ enableOverlay(constraints1);
+
+ OverlayInfo overlayInfo1 = mOverlayManager.getOverlayInfo(mOverlayIdentifier, mUserHandle);
+ assertNotNull(overlayInfo1);
+ assertEquals(constraints1, overlayInfo1.getConstraints());
+
+ List<OverlayConstraint> constraints2 = List.of(
+ new OverlayConstraint(TYPE_DISPLAY_ID, 2 /* value */));
+ enableOverlay(constraints2);
+
+ OverlayInfo overlayInfo2 = mOverlayManager.getOverlayInfo(mOverlayIdentifier, mUserHandle);
+ assertNotNull(overlayInfo2);
+ assertEquals(overlayInfo1.overlayName, overlayInfo2.overlayName);
+ assertEquals(overlayInfo1.targetPackageName, overlayInfo2.targetPackageName);
+ assertEquals(overlayInfo1.packageName, overlayInfo2.packageName);
+ assertEquals(constraints2, overlayInfo2.getConstraints());
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_RRO_CONSTRAINTS)
+ public void enableOverlayWithConstraints_fails() throws Exception {
+ assertThrows(SecurityException.class, () -> enableOverlay(
+ List.of(new OverlayConstraint(TYPE_DISPLAY_ID, DEFAULT_DISPLAY))));
+ }
+
+ private FabricatedOverlay createFabricatedOverlay() {
+ String packageName = getApplicationContext().getPackageName();
+ FabricatedOverlay fabricatedOverlay = new FabricatedOverlay.Builder(
+ packageName, "testOverlay" /* name */, packageName)
+ .build();
+ fabricatedOverlay.setResourceValue("string/module_2_name" /* resourceName */,
+ TypedValue.TYPE_STRING, "hello" /* value */, null /* configuration */);
+ return fabricatedOverlay;
+ }
+
+ private void enableOverlay(List<OverlayConstraint> constraints) {
+ FabricatedOverlay fabricatedOverlay = createFabricatedOverlay();
+ OverlayManagerTransaction transaction =
+ new OverlayManagerTransaction.Builder()
+ .registerFabricatedOverlay(fabricatedOverlay)
+ .setEnabled(fabricatedOverlay.getIdentifier(), true /* enable */,
+ constraints)
+ .build();
+ mOverlayManager.commit(transaction);
+ mOverlayIdentifier = fabricatedOverlay.getIdentifier();
+ }
+
+ private static List<OverlayConstraint>[] getAllConstraintLists() {
+ return new List[]{
+ Collections.emptyList(),
+ List.of(new OverlayConstraint(TYPE_DISPLAY_ID, DEFAULT_DISPLAY)),
+ List.of(new OverlayConstraint(TYPE_DEVICE_ID, DEVICE_ID_DEFAULT)),
+ List.of(new OverlayConstraint(TYPE_DEVICE_ID, DEVICE_ID_DEFAULT),
+ new OverlayConstraint(TYPE_DEVICE_ID, DEVICE_ID_DEFAULT))
+ };
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
index ec61b877a3e4..0818db1db3bc 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
@@ -30,6 +30,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Collections;
import java.util.Set;
import java.util.function.Consumer;
@@ -203,7 +204,7 @@ public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceI
// Overlay priority changing between reboots should not affect enable state of mutable
// overlays.
- impl.setEnabled(IDENTIFIER, true, USER);
+ impl.setEnabled(IDENTIFIER, true, USER, Collections.emptyList() /* constraints */);
// Reorder the overlays
configureSystemOverlay(OVERLAY, ConfigState.MUTABLE_DISABLED, 1 /* priority */);
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
index 578b888a6496..e46a806727c0 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
@@ -16,10 +16,14 @@
package com.android.server.om;
+import static android.content.Context.DEVICE_ID_DEFAULT;
+import static android.content.om.OverlayConstraint.TYPE_DEVICE_ID;
+import static android.content.om.OverlayConstraint.TYPE_DISPLAY_ID;
import static android.content.om.OverlayInfo.STATE_DISABLED;
import static android.content.om.OverlayInfo.STATE_ENABLED;
import static android.content.om.OverlayInfo.STATE_MISSING_TARGET;
import static android.os.OverlayablePolicy.CONFIG_SIGNATURE;
+import static android.view.Display.DEFAULT_DISPLAY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -27,21 +31,30 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.testng.Assert.assertThrows;
+import android.content.om.OverlayConstraint;
import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.content.pm.UserPackage;
+import android.content.res.Flags;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import androidx.test.runner.AndroidJUnit4;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
-@RunWith(AndroidJUnit4.class)
+@RunWith(JUnitParamsRunner.class)
public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
private static final String OVERLAY = "com.test.overlay";
@@ -62,6 +75,9 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
private static final String CERT_CONFIG_OK = "config_certificate_ok";
private static final String CERT_CONFIG_NOK = "config_certificate_nok";
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Test
public void testGetOverlayInfo() throws Exception {
installAndAssert(overlay(OVERLAY, TARGET), USER,
@@ -176,7 +192,8 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
Set.of(UserPackage.of(USER, TARGET)));
assertState(STATE_DISABLED, IDENTIFIER, USER);
- assertEquals(impl.setEnabled(IDENTIFIER, true, USER),
+ assertEquals(impl.setEnabled(IDENTIFIER, true /* enable */, USER,
+ Collections.emptyList() /* constraints */),
Set.of(UserPackage.of(USER, TARGET)));
assertState(STATE_ENABLED, IDENTIFIER, USER);
@@ -213,22 +230,17 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_RRO_CONSTRAINTS)
public void testSetEnabledAtVariousConditions() throws Exception {
- final OverlayManagerServiceImpl impl = getImpl();
- assertThrows(OverlayManagerServiceImpl.OperationFailedException.class,
- () -> impl.setEnabled(IDENTIFIER, true, USER));
-
- // request succeeded, and there was a change that needs to be
- // propagated to the rest of the system
- installAndAssert(target(TARGET), USER,
- Set.of(UserPackage.of(USER, TARGET)));
- installAndAssert(overlay(OVERLAY, TARGET), USER,
- Set.of(UserPackage.of(USER, OVERLAY), UserPackage.of(USER, TARGET)));
- assertEquals(Set.of(UserPackage.of(USER, TARGET)),
- impl.setEnabled(IDENTIFIER, true, USER));
+ testSetEnabledAtVariousConditions(Collections.emptyList());
+ }
- // request succeeded, but nothing changed
- assertEquals(Set.of(), impl.setEnabled(IDENTIFIER, true, USER));
+ @Test
+ @Parameters(method = "getConstraintLists")
+ @RequiresFlagsEnabled(Flags.FLAG_RRO_CONSTRAINTS)
+ public void testSetEnabledAtVariousConditionsWithConstraints(
+ List<OverlayConstraint> constraints) throws Exception {
+ testSetEnabledAtVariousConditions(constraints);
}
@Test
@@ -338,4 +350,33 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
Set.of(UserPackage.of(USER, TARGET)),
Set.of(UserPackage.of(USER, TARGET)));
}
+
+ private void testSetEnabledAtVariousConditions(final List<OverlayConstraint> constraints)
+ throws Exception {
+ final OverlayManagerServiceImpl impl = getImpl();
+ assertThrows(OverlayManagerServiceImpl.OperationFailedException.class,
+ () -> impl.setEnabled(IDENTIFIER, true /* enable */, USER, constraints));
+
+ // request succeeded, and there was a change that needs to be
+ // propagated to the rest of the system
+ installAndAssert(target(TARGET), USER,
+ Set.of(UserPackage.of(USER, TARGET)));
+ installAndAssert(overlay(OVERLAY, TARGET), USER,
+ Set.of(UserPackage.of(USER, OVERLAY), UserPackage.of(USER, TARGET)));
+ assertEquals(Set.of(UserPackage.of(USER, TARGET)),
+ impl.setEnabled(IDENTIFIER, true /* enable */, USER, constraints));
+
+ // request succeeded, but nothing changed
+ assertEquals(Set.of(), impl.setEnabled(IDENTIFIER, true /* enable */, USER, constraints));
+ }
+
+ private static List<OverlayConstraint>[] getConstraintLists() {
+ return new List[]{
+ Collections.emptyList(),
+ List.of(new OverlayConstraint(TYPE_DISPLAY_ID, DEFAULT_DISPLAY)),
+ List.of(new OverlayConstraint(TYPE_DEVICE_ID, DEVICE_ID_DEFAULT)),
+ List.of(new OverlayConstraint(TYPE_DEVICE_ID, DEVICE_ID_DEFAULT),
+ new OverlayConstraint(TYPE_DEVICE_ID, DEVICE_ID_DEFAULT))
+ };
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
index 3e82d45595d7..c69de8d2dbc4 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
@@ -32,6 +32,7 @@ import android.content.om.OverlayableInfo;
import android.content.pm.UserPackage;
import android.os.FabricatedOverlayInfo;
import android.os.FabricatedOverlayInternal;
+import android.os.OverlayConstraint;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -480,7 +481,7 @@ class OverlayManagerServiceImplTestsBase {
@Override
String createIdmap(String targetPath, String overlayPath, String overlayName,
- int policies, boolean enforce, int userId) {
+ int policies, boolean enforce, int userId, OverlayConstraint[] constraints) {
mIdmapFiles.put(overlayPath, new IdmapHeader(getCrc(targetPath),
getCrc(overlayPath), targetPath, overlayName, policies, enforce));
return overlayPath;
@@ -493,7 +494,7 @@ class OverlayManagerServiceImplTestsBase {
@Override
boolean verifyIdmap(String targetPath, String overlayPath, String overlayName, int policies,
- boolean enforce, int userId) {
+ boolean enforce, int userId, OverlayConstraint[] constraints) {
final IdmapHeader idmap = mIdmapFiles.get(overlayPath);
if (idmap == null) {
return false;
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
index 3f7eac798ccc..ad3855f4c28f 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
@@ -16,6 +16,8 @@
package com.android.server.om;
+import static android.content.om.OverlayConstraint.TYPE_DEVICE_ID;
+import static android.content.om.OverlayConstraint.TYPE_DISPLAY_ID;
import static android.content.om.OverlayInfo.STATE_DISABLED;
import static android.content.om.OverlayInfo.STATE_ENABLED;
@@ -26,6 +28,9 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.content.om.OverlayConstraint;
import android.content.om.OverlayIdentifier;
import android.content.om.OverlayInfo;
import android.text.TextUtils;
@@ -44,25 +49,32 @@ import org.xmlpull.v1.XmlPullParser;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
-import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.stream.IntStream;
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 int USER_0 = 0;
+ private static final int USER_1 = 1;
+
+ private static final int DISPLAY_ID = 1;
+ private static final int DEVICE_ID = 2;
+
+ private static final OverlayConstraint CONSTRAINT_0 =
+ new OverlayConstraint(TYPE_DISPLAY_ID, DISPLAY_ID);
+ private static final OverlayConstraint CONSTRAINT_1 =
+ new OverlayConstraint(TYPE_DEVICE_ID, DEVICE_ID);
- private static OverlayIdentifier OVERLAY_A = new OverlayIdentifier("com.test.overlay_a",
+ private static final OverlayIdentifier OVERLAY_A = new OverlayIdentifier("com.test.overlay_a",
null /* overlayName */);
- private static OverlayIdentifier OVERLAY_B = new OverlayIdentifier("com.test.overlay_b",
+ private static final OverlayIdentifier OVERLAY_B = new OverlayIdentifier("com.test.overlay_b",
null /* overlayName */);
- private static OverlayIdentifier OVERLAY_C = new OverlayIdentifier("com.test.overlay_c",
+ private static final OverlayIdentifier OVERLAY_C = new OverlayIdentifier("com.test.overlay_c",
null /* overlayName */);
private static final OverlayInfo OVERLAY_A_USER0 = createInfo(OVERLAY_A, USER_0);
@@ -72,6 +84,13 @@ public class OverlayManagerSettingsTests {
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_A_USER0_WITH_CONSTRAINTS =
+ createInfo(OVERLAY_A, USER_0, List.of(CONSTRAINT_0, CONSTRAINT_1));
+ private static final OverlayInfo OVERLAY_B_USER0_WITH_CONSTRAINTS =
+ createInfo(OVERLAY_B, USER_0, List.of(CONSTRAINT_1));
+ private static final OverlayInfo OVERLAY_B_USER1_WITH_CONSTRAINTS =
+ createInfo(OVERLAY_B, USER_1, List.of(CONSTRAINT_1));
+
private static final String TARGET_PACKAGE = "com.test.target";
@Before
@@ -228,6 +247,22 @@ public class OverlayManagerSettingsTests {
mSettings.getOverlaysForTarget(OVERLAY_A_USER0.targetPackageName, USER_0));
}
+ @Test
+ public void testSetConstraints() throws Exception {
+ insertSetting(OVERLAY_A_USER0);
+ insertSetting(OVERLAY_B_USER0);
+ assertListsAreEqual(List.of(OVERLAY_A_USER0, OVERLAY_B_USER0),
+ mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
+
+ assertTrue(mSettings.setConstraints(OVERLAY_A, USER_0,
+ List.of(CONSTRAINT_0, CONSTRAINT_1)));
+ assertTrue(mSettings.setConstraints(OVERLAY_B, USER_0, List.of(CONSTRAINT_1)));
+
+ assertListsAreEqual(
+ List.of(OVERLAY_A_USER0_WITH_CONSTRAINTS, OVERLAY_B_USER0_WITH_CONSTRAINTS),
+ mSettings.getOverlaysForTarget(TARGET_PACKAGE, USER_0));
+ }
+
// tests: persist and restore
@Test
@@ -291,12 +326,42 @@ public class OverlayManagerSettingsTests {
}
@Test
+ public void testPersistWithConstraints() throws Exception {
+ insertSetting(OVERLAY_A_USER0_WITH_CONSTRAINTS);
+ insertSetting(OVERLAY_B_USER1_WITH_CONSTRAINTS);
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ mSettings.persist(os);
+ ByteArrayInputStream xml = new ByteArrayInputStream(os.toByteArray());
+
+ assertEquals(1, countXmlTags(xml, "overlays"));
+ assertEquals(2, countXmlTags(xml, "item"));
+ assertEquals(3, countXmlTags(xml, "constraint"));
+ assertEquals(1, countXmlAttributesWhere(xml, "item", "packageName",
+ OVERLAY_A.getPackageName()));
+ assertEquals(1, countXmlAttributesWhere(xml, "item", "packageName",
+ OVERLAY_B.getPackageName()));
+ assertEquals(1, countXmlAttributesWhere(xml, "item", "userId",
+ Integer.toString(USER_0)));
+ assertEquals(1, countXmlAttributesWhere(xml, "item", "userId",
+ Integer.toString(USER_1)));
+ assertEquals(1, countXmlAttributesWhere(xml, "constraint", "type",
+ TYPE_DISPLAY_ID));
+ assertEquals(2, countXmlAttributesWhere(xml, "constraint", "type",
+ TYPE_DEVICE_ID));
+ assertEquals(1, countXmlAttributesWhere(xml, "constraint", "value",
+ DISPLAY_ID));
+ assertEquals(2, countXmlAttributesWhere(xml, "constraint", "value",
+ DEVICE_ID));
+ }
+
+ @Test
public void testRestoreEmpty() throws Exception {
final int version = OverlayManagerSettings.Serializer.CURRENT_VERSION;
final String xml =
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<overlays version=\"" + version + "\" />\n";
- ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
+ ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes(UTF_8));
mSettings.restore(is);
assertDoesNotContain(mSettings, new OverlayIdentifier("com.test.overlay"), 0);
@@ -319,7 +384,45 @@ public class OverlayManagerSettingsTests {
+ " isStatic='false'\n"
+ " priority='0' />\n"
+ "</overlays>\n";
- ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
+ ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes(UTF_8));
+
+ mSettings.restore(is);
+ 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(identifier, 1234));
+ assertTrue(oi.constraints.isEmpty());
+ }
+
+ @Test
+ public void testRestoreSingleUserSingleOverlayWithConstraints() throws Exception {
+ final int version = OverlayManagerSettings.Serializer.CURRENT_VERSION;
+ final String xml =
+ "<?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"
+ + " state='" + STATE_DISABLED + "'\n"
+ + " isEnabled='false'\n"
+ + " category='test-category'\n"
+ + " isStatic='false'\n"
+ + " priority='0' >\n"
+ + "<constraint type='" + TYPE_DISPLAY_ID + "'\n"
+ + " value = '" + DISPLAY_ID + "' />\n"
+ + "<constraint type='" + TYPE_DEVICE_ID + "'\n"
+ + " value = '" + DEVICE_ID + "' />\n"
+ + "</item>\n"
+ + "</overlays>\n";
+ ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes(UTF_8));
mSettings.restore(is);
final OverlayIdentifier identifier = new OverlayIdentifier("com.test.overlay", "test");
@@ -332,6 +435,7 @@ public class OverlayManagerSettingsTests {
assertEquals(1234, oi.userId);
assertEquals(STATE_DISABLED, oi.state);
assertFalse(mSettings.getEnabled(identifier, 1234));
+ assertListsAreEqual(List.of(CONSTRAINT_0, CONSTRAINT_1), oi.constraints);
}
@Test
@@ -352,6 +456,24 @@ public class OverlayManagerSettingsTests {
assertEquals(OVERLAY_B_USER1, b);
}
+ @Test
+ public void testPersistAndRestoreWithConstraints() throws Exception {
+ insertSetting(OVERLAY_A_USER0_WITH_CONSTRAINTS);
+ insertSetting(OVERLAY_B_USER1_WITH_CONSTRAINTS);
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ mSettings.persist(os);
+ ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
+ OverlayManagerSettings newSettings = new OverlayManagerSettings();
+ newSettings.restore(is);
+
+ OverlayInfo a = newSettings.getOverlayInfo(OVERLAY_A, USER_0);
+ assertEquals(OVERLAY_A_USER0_WITH_CONSTRAINTS, a);
+
+ OverlayInfo b = newSettings.getOverlayInfo(OVERLAY_B, USER_1);
+ assertEquals(OVERLAY_B_USER1_WITH_CONSTRAINTS, b);
+ }
+
private int countXmlTags(InputStream in, String tagToLookFor) throws Exception {
in.reset();
int count = 0;
@@ -384,11 +506,30 @@ public class OverlayManagerSettingsTests {
return count;
}
+ private int countXmlAttributesWhere(InputStream in, String tag, String attr, int value)
+ throws Exception {
+ in.reset();
+ int count = 0;
+ TypedXmlPullParser parser = Xml.resolvePullParser(in);
+ int event = parser.getEventType();
+ while (event != XmlPullParser.END_DOCUMENT) {
+ if (event == XmlPullParser.START_TAG && tag.equals(parser.getName())) {
+ int v = parser.getAttributeInt(null, attr);
+ if (value == v) {
+ count++;
+ }
+ }
+ event = parser.next();
+ }
+ return count;
+ }
+
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);
+ mSettings.setConstraints(oi.getOverlayIdentifier(), oi.userId, oi.constraints);
}
private static void assertContains(final OverlayManagerSettings settings,
@@ -417,42 +558,28 @@ public class OverlayManagerSettingsTests {
}
private static OverlayInfo createInfo(@NonNull OverlayIdentifier identifier, int userId) {
+ return createInfo(identifier, userId, Collections.emptyList());
+ }
+
+ private static OverlayInfo createInfo(@NonNull OverlayIdentifier identifier, int userId,
+ @NonNull List<OverlayConstraint> constraints) {
return new OverlayInfo(
identifier.getPackageName(),
identifier.getOverlayName(),
"com.test.target",
- null,
- "some-category",
- "/data/app/" + identifier + "/base.apk",
+ null /* targetOverlayableName */,
+ "some-category" /* category */,
+ "/data/app/" + identifier + "/base.apk" /* baseCodePath */,
STATE_DISABLED,
userId,
- 0,
- true,
- false);
- }
-
- private static void assertContains(int[] haystack, int needle) {
- List<Integer> list = IntStream.of(haystack)
- .boxed()
- .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
- if (!list.contains(needle)) {
- fail(String.format("integer array [%s] does not contain value %s",
- TextUtils.join(",", list), needle));
- }
- }
-
- private static void assertDoesNotContain(int[] haystack, int needle) {
- List<Integer> list = IntStream.of(haystack)
- .boxed()
- .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
- if (list.contains(needle)) {
- fail(String.format("integer array [%s] contains value %s",
- TextUtils.join(",", list), needle));
- }
+ 0 /* priority */,
+ true /* isMutable */,
+ false /* isFabricated */,
+ constraints);
}
- private static void assertListsAreEqual(
- @NonNull List<OverlayInfo> expected, @Nullable List<OverlayInfo> actual) {
+ private static <T> void assertListsAreEqual(
+ @NonNull List<T> expected, @Nullable List<T> actual) {
if (!expected.equals(actual)) {
fail(String.format("lists [%s] and [%s] differ",
TextUtils.join(",", expected), TextUtils.join(",", actual)));