• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 #ifndef LOADEDARSC_H_
18 #define LOADEDARSC_H_
19 
20 #include <map>
21 #include <memory>
22 #include <set>
23 #include <vector>
24 #include <unordered_map>
25 #include <unordered_set>
26 
27 #include <android-base/macros.h>
28 #include <android-base/result.h>
29 
30 #include "androidfw/ByteBucketArray.h"
31 #include "androidfw/Chunk.h"
32 #include "androidfw/Idmap.h"
33 #include "androidfw/ResourceTypes.h"
34 #include "androidfw/Util.h"
35 
36 namespace android {
37 
38 class DynamicPackageEntry {
39  public:
40   DynamicPackageEntry() = default;
DynamicPackageEntry(std::string && package_name,int package_id)41   DynamicPackageEntry(std::string&& package_name, int package_id)
42       : package_name(std::move(package_name)), package_id(package_id) {}
43 
44   std::string package_name;
45   int package_id = 0;
46 };
47 
48 // TypeSpec is going to be immediately proceeded by
49 // an array of Type structs, all in the same block of memory.
50 struct TypeSpec {
51   struct TypeEntry {
52     incfs::verified_map_ptr<ResTable_type> type;
53 
54     // Type configurations are accessed frequently when setting up an AssetManager and querying
55     // resources. Access this cached configuration to minimize page faults.
56     ResTable_config config;
57   };
58 
59   // Pointer to the mmapped data where flags are kept. Flags denote whether the resource entry is
60   // public and under which configurations it varies.
61   incfs::verified_map_ptr<ResTable_typeSpec> type_spec;
62 
63   std::vector<TypeEntry> type_entries;
64 
GetFlagsForEntryIndexTypeSpec65   base::expected<uint32_t, NullOrIOError> GetFlagsForEntryIndex(uint16_t entry_index) const {
66     if (entry_index >= dtohl(type_spec->entryCount)) {
67       return 0U;
68     }
69     const auto entry_flags_ptr = ((type_spec + 1).convert<uint32_t>() + entry_index);
70     if (!entry_flags_ptr) {
71       return base::unexpected(IOError::PAGES_MISSING);
72     }
73     return entry_flags_ptr.value();
74   }
75 };
76 
77 // Flags that change the behavior of loaded packages.
78 // Keep in sync with f/b/android/content/res/ApkAssets.java
79 using package_property_t = uint32_t;
80 enum : package_property_t {
81   // The package contains framework resource values specified by the system.
82   // This allows some functions to filter out this package when computing
83   // what configurations/resources are available.
84   PROPERTY_SYSTEM = 1U << 0U,
85 
86   // The package is a shared library or has a package id of 7f and is loaded as a shared library by
87   // force.
88   PROPERTY_DYNAMIC = 1U << 1U,
89 
90   // The package has been loaded dynamically using a ResourcesProvider.
91   PROPERTY_LOADER = 1U << 2U,
92 
93   // The package is a RRO.
94   PROPERTY_OVERLAY = 1U << 3U,
95 
96   // The apk assets is owned by the application running in this process and incremental crash
97   // protections for this APK must be disabled.
98   PROPERTY_DISABLE_INCREMENTAL_HARDENING = 1U << 4U,
99 };
100 
101 struct OverlayableInfo {
102   std::string name;
103   std::string actor;
104   uint32_t policy_flags;
105 };
106 
107 class LoadedPackage {
108  public:
109   class iterator {
110    public:
111     iterator& operator=(const iterator& rhs) {
112       loadedPackage_ = rhs.loadedPackage_;
113       typeIndex_ = rhs.typeIndex_;
114       entryIndex_ = rhs.entryIndex_;
115       return *this;
116     }
117 
118     bool operator==(const iterator& rhs) const {
119       return loadedPackage_ == rhs.loadedPackage_ &&
120              typeIndex_ == rhs.typeIndex_ &&
121              entryIndex_ == rhs.entryIndex_;
122     }
123 
124     bool operator!=(const iterator& rhs) const {
125       return !(*this == rhs);
126     }
127 
128     iterator operator++(int) {
129       size_t prevTypeIndex_ = typeIndex_;
130       size_t prevEntryIndex_ = entryIndex_;
131       operator++();
132       return iterator(loadedPackage_, prevTypeIndex_, prevEntryIndex_);
133     }
134 
135     iterator& operator++();
136 
137     uint32_t operator*() const;
138 
139    private:
140     friend class LoadedPackage;
141 
142     iterator(const LoadedPackage* lp, size_t ti, size_t ei);
143 
144     const LoadedPackage* loadedPackage_;
145     size_t typeIndex_;
146     size_t entryIndex_;
147     const size_t typeIndexEnd_;  // STL style end, so one past the last element
148   };
149 
begin()150   iterator begin() const {
151     return iterator(this, 0, 0);
152   }
153 
end()154   iterator end() const {
155     return iterator(this, resource_ids_.size() + 1, 0);
156   }
157 
158   static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk,
159                                                    package_property_t property_flags);
160 
161   // Finds the entry with the specified type name and entry name. The names are in UTF-16 because
162   // the underlying ResStringPool API expects this. For now this is acceptable, but since
163   // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change.
164   // Returns a partial resource ID, with the package ID left as 0x00. The caller is responsible
165   // for patching the correct package ID to the resource ID.
166   base::expected<uint32_t, NullOrIOError> FindEntryByName(const std::u16string& type_name,
167                                                           const std::u16string& entry_name) const;
168 
169   static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntry(
170       incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index);
171 
172   static base::expected<uint32_t, NullOrIOError> GetEntryOffset(
173       incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index);
174 
175   static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntryFromOffset(
176       incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset);
177 
178   // Returns the string pool where type names are stored.
GetTypeStringPool()179   const ResStringPool* GetTypeStringPool() const {
180     return &type_string_pool_;
181   }
182 
183   // Returns the string pool where the names of resource entries are stored.
GetKeyStringPool()184   const ResStringPool* GetKeyStringPool() const {
185     return &key_string_pool_;
186   }
187 
GetPackageName()188   const std::string& GetPackageName() const {
189     return package_name_;
190   }
191 
GetPackageId()192   int GetPackageId() const {
193     return package_id_;
194   }
195 
196   // Returns true if this package is dynamic (shared library) and needs to have an ID assigned.
IsDynamic()197   bool IsDynamic() const {
198     return (property_flags_ & PROPERTY_DYNAMIC) != 0;
199   }
200 
201   // Returns true if this package is a Runtime Resource Overlay.
IsOverlay()202   bool IsOverlay() const {
203     return (property_flags_ & PROPERTY_OVERLAY) != 0;
204   }
205 
206   // Returns true if this package originates from a system provided resource.
IsSystem()207   bool IsSystem() const {
208     return (property_flags_ & PROPERTY_SYSTEM) != 0;
209   }
210 
211   // Returns true if this package is a custom loader and should behave like an overlay.
IsCustomLoader()212   bool IsCustomLoader() const {
213     return (property_flags_ & PROPERTY_LOADER) != 0;
214   }
215 
GetPropertyFlags()216   package_property_t GetPropertyFlags() const {
217     return property_flags_;
218   }
219 
220   // Returns the map of package name to package ID used in this LoadedPackage. At runtime, a
221   // package could have been assigned a different package ID than what this LoadedPackage was
222   // compiled with. AssetManager rewrites the package IDs so that they are compatible at runtime.
GetDynamicPackageMap()223   const std::vector<DynamicPackageEntry>& GetDynamicPackageMap() const {
224     return dynamic_package_map_;
225   }
226 
227   // Populates a set of ResTable_config structs, possibly excluding configurations defined for
228   // the mipmap type.
229   base::expected<std::monostate, IOError> CollectConfigurations(
230       bool exclude_mipmap, std::set<ResTable_config>* out_configs) const;
231 
232   // Populates a set of strings representing locales.
233   // If `canonicalize` is set to true, each locale is transformed into its canonical format
234   // before being inserted into the set. This may cause some equivalent locales to de-dupe.
235   void CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const;
236 
237   // type_idx is TT - 1 from 0xPPTTEEEE.
GetTypeSpecByTypeIndex(uint8_t type_index)238   inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const {
239     // If the type IDs are offset in this package, we need to take that into account when searching
240     // for a type.
241     const auto& type_spec = type_specs_.find(type_index + 1 - type_id_offset_);
242     if (type_spec == type_specs_.end()) {
243       return nullptr;
244     }
245     return &type_spec->second;
246   }
247 
248   template <typename Func>
ForEachTypeSpec(Func f)249   void ForEachTypeSpec(Func f) const {
250     for (const auto& type_spec : type_specs_) {
251       f(type_spec.second, type_spec.first);
252     }
253   }
254 
255   // Retrieves the overlayable properties of the specified resource. If the resource is not
256   // overlayable, this will return a null pointer.
GetOverlayableInfo(uint32_t resid)257   const OverlayableInfo* GetOverlayableInfo(uint32_t resid) const {
258     for (const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>& overlayable_info_ids
259         : overlayable_infos_) {
260       if (overlayable_info_ids.second.find(resid) != overlayable_info_ids.second.end()) {
261         return &overlayable_info_ids.first;
262       }
263     }
264     return nullptr;
265   }
266 
267   // Retrieves whether or not the package defines overlayable resources.
268   // TODO(123905379): Remove this when the enforcement of overlayable is turned on for all APK and
269   // not just those that defined overlayable resources.
DefinesOverlayable()270   bool DefinesOverlayable() const {
271     return defines_overlayable_;
272   }
273 
GetOverlayableMap()274   const std::unordered_map<std::string, std::string>& GetOverlayableMap() const {
275     return overlayable_map_;
276   }
277 
GetAliasResourceIdMap()278   const std::map<uint32_t, uint32_t>& GetAliasResourceIdMap() const {
279     return alias_id_map_;
280   }
281 
282  private:
283   DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
284 
285   LoadedPackage() = default;
286 
287   ResStringPool type_string_pool_;
288   ResStringPool key_string_pool_;
289   std::string package_name_;
290   bool defines_overlayable_ = false;
291   int package_id_ = -1;
292   int type_id_offset_ = 0;
293   package_property_t property_flags_ = 0U;
294 
295   std::unordered_map<uint8_t, TypeSpec> type_specs_;
296   ByteBucketArray<uint32_t> resource_ids_;
297   std::vector<DynamicPackageEntry> dynamic_package_map_;
298   std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;
299   std::map<uint32_t, uint32_t> alias_id_map_;
300 
301   // A map of overlayable name to actor
302   std::unordered_map<std::string, std::string> overlayable_map_;
303 };
304 
305 // Read-only view into a resource table. This class validates all data
306 // when loading, including offsets and lengths.
307 class LoadedArsc {
308  public:
309   // Load a resource table from memory pointed to by `data` of size `len`.
310   // The lifetime of `data` must out-live the LoadedArsc returned from this method.
311 
312   static std::unique_ptr<LoadedArsc> Load(incfs::map_ptr<void> data,
313                                           size_t length,
314                                           const LoadedIdmap* loaded_idmap = nullptr,
315                                           package_property_t property_flags = 0U);
316 
317   // Create an empty LoadedArsc. This is used when an APK has no resources.arsc.
318   static std::unique_ptr<LoadedArsc> CreateEmpty();
319 
320   // Returns the string pool where all string resource values
321   // (Res_value::dataType == Res_value::TYPE_STRING) are indexed.
GetStringPool()322   inline const ResStringPool* GetStringPool() const {
323     return global_string_pool_.get();
324   }
325 
326   // Gets a pointer to the package with the specified package ID, or nullptr if no such package
327   // exists.
328   const LoadedPackage* GetPackageById(uint8_t package_id) const;
329 
330   // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc.
GetPackages()331   inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const {
332     return packages_;
333   }
334 
335  private:
336   DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
337 
338   LoadedArsc() = default;
339   bool LoadTable(
340       const Chunk& chunk, const LoadedIdmap* loaded_idmap, package_property_t property_flags);
341 
342   std::unique_ptr<ResStringPool> global_string_pool_ = util::make_unique<ResStringPool>();
343   std::vector<std::unique_ptr<const LoadedPackage>> packages_;
344 };
345 
346 }  // namespace android
347 
348 #endif /* LOADEDARSC_H_ */
349