1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #pragma once
18
19 #include <memory>
20 #include <string>
21 #include <unordered_map>
22 #include <variant>
23
24 #include "android-base/macros.h"
25 #include "android-base/unique_fd.h"
26 #include "androidfw/ConfigDescription.h"
27 #include "androidfw/ResourceTypes.h"
28 #include "androidfw/StringPiece.h"
29 #include "androidfw/misc.h"
30 #include "utils/ByteOrder.h"
31
32 namespace android {
33
34 // An enum that tracks more states than just 'up to date' or 'not' for a resources container:
35 // there are several cases where we know for sure that the object can't change and won't get
36 // out of date. Reporting those states to the managed layer allows it to stop checking here
37 // completely, speeding up the cache lookups by dozens of milliseconds.
38 enum class UpToDate : int { False, True, Always };
39
40 // Combines two UpToDate values, and only accesses the second one if it matters to the result.
41 template <class Getter>
combine(UpToDate first,Getter secondGetter)42 UpToDate combine(UpToDate first, Getter secondGetter) {
43 switch (first) {
44 case UpToDate::False:
45 return UpToDate::False;
46 case UpToDate::True: {
47 const auto second = secondGetter();
48 return second == UpToDate::False ? UpToDate::False : UpToDate::True;
49 }
50 case UpToDate::Always:
51 return secondGetter();
52 }
53 }
54
fromBool(bool value)55 inline UpToDate fromBool(bool value) {
56 return value ? UpToDate::True : UpToDate::False;
57 }
58
59 class LoadedIdmap;
60 class IdmapResMap;
61 struct Idmap_header;
62 struct Idmap_constraints;
63 struct Idmap_data_header;
64 struct Idmap_target_entry_inline;
65 struct Idmap_target_entry_inline_value;
66
67 // LINT.IfChange
68 constexpr int32_t kOverlayConstraintTypeDisplayId = 0;
69 constexpr int32_t kOverlayConstraintTypeDeviceId = 1;
70 // LINT.ThenChange(../../../../core/java/android/content/om/OverlayConstraint.java)
71
72 struct Idmap_constraint {
73 // Constraint type can be kOverlayConstraintTypeDisplayId or kOverlayConstraintTypeDeviceId
74 const uint32_t constraint_type;
75 const uint32_t constraint_value;
76 };
77 struct Idmap_constraints {
78 const uint32_t constraint_count = 0;
79 const Idmap_constraint* constraint_entries = nullptr;
80 };
81 struct Idmap_target_entries {
82 const uint32_t* target_id = nullptr;
83 const uint32_t* overlay_id = nullptr;
84 };
85 struct Idmap_target_inline_entries {
86 const uint32_t* target_id = nullptr;
87 const Idmap_target_entry_inline* entry = nullptr;
88 };
89 struct Idmap_overlay_entries {
90 const uint32_t* overlay_id = nullptr;
91 const uint32_t* target_id = nullptr;
92 };
93
94 // A string pool for overlay apk assets. The string pool holds the strings of the overlay resources
95 // table and additionally allows for loading strings from the idmap string pool. The idmap string
96 // pool strings are offset after the end of the overlay resource table string pool entries so
97 // queries for strings defined inline in the idmap do not conflict with queries for overlay
98 // resource table strings.
99 class OverlayStringPool : public ResStringPool {
100 public:
101 virtual ~OverlayStringPool();
102 base::expected<StringPiece16, NullOrIOError> stringAt(size_t idx) const override;
103 base::expected<StringPiece, NullOrIOError> string8At(size_t idx) const override;
104 size_t size() const override;
105
106 explicit OverlayStringPool(const LoadedIdmap* loaded_idmap);
107 private:
108 const Idmap_data_header* data_header_;
109 const ResStringPool* idmap_string_pool_;
110 };
111
112 // A dynamic reference table for loaded overlay packages that rewrites the resource id of overlay
113 // resources to the resource id of corresponding target resources.
114 class OverlayDynamicRefTable : public DynamicRefTable {
115 public:
116 ~OverlayDynamicRefTable() override = default;
117 status_t lookupResourceId(uint32_t* resId) const override;
118
119 private:
120 explicit OverlayDynamicRefTable(const Idmap_data_header* data_header,
121 Idmap_overlay_entries entries,
122 uint8_t target_assigned_package_id);
123
124 // Rewrites a compile-time overlay resource id to the runtime resource id of corresponding target
125 // resource.
126 status_t lookupResourceIdNoRewrite(uint32_t* resId) const;
127
128 const Idmap_data_header* data_header_;
129 Idmap_overlay_entries entries_;
130 uint8_t target_assigned_package_id_;
131
132 friend LoadedIdmap;
133 friend IdmapResMap;
134 };
135
136 // A mapping of target resource ids to a values or resource ids that should overlay the target.
137 class IdmapResMap {
138 public:
139 // Represents the result of a idmap lookup. The result can be one of three possibilities:
140 // 1) The result is a resource id which represents the overlay resource that should act as an
141 // alias of the target resource.
142 // 2) The result is a table entry which overlays the type and value of the target resource.
143 // 3) The result is neither and the target resource is not overlaid.
144 class Result {
145 public:
146 Result() = default;
Result(uint32_t value)147 explicit Result(uint32_t value) : data_(value) {};
Result(std::map<ConfigDescription,Res_value> value)148 explicit Result(std::map<ConfigDescription, Res_value> value) : data_(std::move(value)) {
149 }
150
151 // Returns `true` if the resource is overlaid.
152 explicit operator bool() const {
153 return std::get_if<std::monostate>(&data_) == nullptr;
154 }
155
IsResourceId()156 bool IsResourceId() const {
157 return std::get_if<uint32_t>(&data_) != nullptr;
158 }
159
GetResourceId()160 uint32_t GetResourceId() const {
161 return std::get<uint32_t>(data_);
162 }
163
IsInlineValue()164 bool IsInlineValue() const {
165 return std::get_if<2>(&data_) != nullptr;
166 }
167
GetInlineValue()168 const std::map<ConfigDescription, Res_value>& GetInlineValue() const {
169 return std::get<2>(data_);
170 }
171
172 private:
173 std::variant<std::monostate, uint32_t,
174 std::map<ConfigDescription, Res_value> > data_;
175 };
176
177 // Looks up the value that overlays the target resource id.
178 Result Lookup(uint32_t target_res_id) const;
179
GetOverlayDynamicRefTable()180 inline const OverlayDynamicRefTable* GetOverlayDynamicRefTable() const {
181 return overlay_ref_table_;
182 }
183
GetConstraints()184 inline Idmap_constraints GetConstraints() const {
185 return constraints_;
186 }
187
188 private:
189 explicit IdmapResMap(const Idmap_data_header* data_header, const Idmap_constraints& constraints,
190 Idmap_target_entries entries, Idmap_target_inline_entries inline_entries,
191 const Idmap_target_entry_inline_value* inline_entry_values,
192 const ConfigDescription* configs, uint8_t target_assigned_package_id,
193 const OverlayDynamicRefTable* overlay_ref_table);
194
195 const Idmap_data_header* data_header_;
196 Idmap_constraints constraints_;
197 Idmap_target_entries entries_;
198 Idmap_target_inline_entries inline_entries_;
199 const Idmap_target_entry_inline_value* inline_entry_values_;
200 const ConfigDescription* configurations_;
201 const uint8_t target_assigned_package_id_;
202 const OverlayDynamicRefTable* overlay_ref_table_;
203
204 friend LoadedIdmap;
205 };
206
207 // Represents a loaded/parsed IDMAP for a Runtime Resource Overlay (RRO).
208 // An RRO and its target APK have different resource IDs assigned to their resources.
209 // An IDMAP is a generated mapping between the resource IDs of the RRO and the target APK.
210 // A LoadedIdmap can be set alongside the overlay's LoadedArsc to allow the overlay ApkAssets to
211 // masquerade as the target ApkAssets resources.
212 class LoadedIdmap {
213 public:
214 // Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed.
215 static std::unique_ptr<LoadedIdmap> Load(StringPiece idmap_path, StringPiece idmap_data);
216
217 // Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated.
OverlayApkPath()218 std::string_view OverlayApkPath() const {
219 return overlay_apk_path_;
220 }
221
222 // Returns the path to the RRO (Runtime Resource Overlay) APK for which this IDMAP was generated.
TargetApkPath()223 std::string_view TargetApkPath() const {
224 return target_apk_path_;
225 }
226
227 // Returns a mapping from target resource ids to overlay values.
GetTargetResourcesMap(uint8_t target_assigned_package_id,const OverlayDynamicRefTable * overlay_ref_table)228 IdmapResMap GetTargetResourcesMap(uint8_t target_assigned_package_id,
229 const OverlayDynamicRefTable* overlay_ref_table) const {
230 return IdmapResMap(data_header_, constraints_, target_entries_, target_inline_entries_,
231 inline_entry_values_, configurations_, target_assigned_package_id,
232 overlay_ref_table);
233 }
234
235 // Returns a dynamic reference table for a loaded overlay package.
GetOverlayDynamicRefTable(uint8_t target_assigned_package_id)236 OverlayDynamicRefTable GetOverlayDynamicRefTable(uint8_t target_assigned_package_id) const {
237 return OverlayDynamicRefTable(data_header_, overlay_entries_, target_assigned_package_id);
238 }
239
240 // Returns whether the idmap file on disk has not been modified since the construction of this
241 // LoadedIdmap.
242 UpToDate IsUpToDate() const;
243
GetConstraints()244 inline const Idmap_constraints GetConstraints() const {
245 return constraints_;
246 }
247
248 protected:
249 // Exposed as protected so that tests can subclass and mock this class out.
250 LoadedIdmap() = default;
251
252 const Idmap_header* header_;
253 const Idmap_data_header* data_header_;
254 Idmap_constraints constraints_;
255 Idmap_target_entries target_entries_;
256 Idmap_target_inline_entries target_inline_entries_;
257 const Idmap_target_entry_inline_value* inline_entry_values_;
258 const ConfigDescription* configurations_;
259 const Idmap_overlay_entries overlay_entries_;
260 const std::unique_ptr<ResStringPool> string_pool_;
261
262 android::base::unique_fd idmap_fd_;
263 std::string_view overlay_apk_path_;
264 std::string_view target_apk_path_;
265 ModDate idmap_last_mod_time_;
266
267 private:
268 DISALLOW_COPY_AND_ASSIGN(LoadedIdmap);
269
270 explicit LoadedIdmap(const std::string& idmap_path, const Idmap_header* header,
271 const Idmap_data_header* data_header, const Idmap_constraints& constraints,
272 Idmap_target_entries target_entries,
273 Idmap_target_inline_entries target_inline_entries,
274 const Idmap_target_entry_inline_value* inline_entry_values_,
275 const ConfigDescription* configs, Idmap_overlay_entries overlay_entries,
276 std::unique_ptr<ResStringPool>&& string_pool,
277 std::string_view overlay_apk_path, std::string_view target_apk_path);
278
279 friend OverlayStringPool;
280 };
281
282 } // namespace android
283