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