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 
24 #include "android-base/macros.h"
25 
26 #include "androidfw/ByteBucketArray.h"
27 #include "androidfw/Chunk.h"
28 #include "androidfw/Idmap.h"
29 #include "androidfw/ResourceTypes.h"
30 #include "androidfw/Util.h"
31 
32 namespace android {
33 
34 class DynamicPackageEntry {
35  public:
36   DynamicPackageEntry() = default;
DynamicPackageEntry(std::string && package_name,int package_id)37   DynamicPackageEntry(std::string&& package_name, int package_id)
38       : package_name(std::move(package_name)), package_id(package_id) {}
39 
40   std::string package_name;
41   int package_id = 0;
42 };
43 
44 // TypeSpec is going to be immediately proceeded by
45 // an array of Type structs, all in the same block of memory.
46 struct TypeSpec {
47   // Pointer to the mmapped data where flags are kept.
48   // Flags denote whether the resource entry is public
49   // and under which configurations it varies.
50   const ResTable_typeSpec* type_spec;
51 
52   // Pointer to the mmapped data where the IDMAP mappings for this type
53   // exist. May be nullptr if no IDMAP exists.
54   const IdmapEntry_header* idmap_entries;
55 
56   // The number of types that follow this struct.
57   // There is a type for each configuration that entries are defined for.
58   size_t type_count;
59 
60   // Trick to easily access a variable number of Type structs
61   // proceeding this struct, and to ensure their alignment.
62   const ResTable_type* types[0];
63 
GetFlagsForEntryIndexTypeSpec64   inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const {
65     if (entry_index >= dtohl(type_spec->entryCount)) {
66       return 0u;
67     }
68 
69     const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec + 1);
70     return flags[entry_index];
71   }
72 };
73 
74 // TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of
75 // ResTable_type pointers.
76 // TypeSpecPtr is a managed pointer that knows how to delete itself.
77 using TypeSpecPtr = util::unique_cptr<TypeSpec>;
78 
79 class LoadedPackage {
80  public:
81   static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk,
82                                                    const LoadedIdmap* loaded_idmap, bool system,
83                                                    bool load_as_shared_library);
84 
85   ~LoadedPackage();
86 
87   // Finds the entry with the specified type name and entry name. The names are in UTF-16 because
88   // the underlying ResStringPool API expects this. For now this is acceptable, but since
89   // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change.
90   // Returns a partial resource ID, with the package ID left as 0x00. The caller is responsible
91   // for patching the correct package ID to the resource ID.
92   uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const;
93 
94   static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index);
95 
96   static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index);
97 
98   static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset);
99 
100   // Returns the string pool where type names are stored.
GetTypeStringPool()101   inline const ResStringPool* GetTypeStringPool() const {
102     return &type_string_pool_;
103   }
104 
105   // Returns the string pool where the names of resource entries are stored.
GetKeyStringPool()106   inline const ResStringPool* GetKeyStringPool() const {
107     return &key_string_pool_;
108   }
109 
GetPackageName()110   inline const std::string& GetPackageName() const {
111     return package_name_;
112   }
113 
GetPackageId()114   inline int GetPackageId() const {
115     return package_id_;
116   }
117 
118   // Returns true if this package is dynamic (shared library) and needs to have an ID assigned.
IsDynamic()119   inline bool IsDynamic() const {
120     return dynamic_;
121   }
122 
123   // Returns true if this package originates from a system provided resource.
IsSystem()124   inline bool IsSystem() const {
125     return system_;
126   }
127 
128   // Returns true if this package is from an overlay ApkAssets.
IsOverlay()129   inline bool IsOverlay() const {
130     return overlay_;
131   }
132 
133   // Returns the map of package name to package ID used in this LoadedPackage. At runtime, a
134   // package could have been assigned a different package ID than what this LoadedPackage was
135   // compiled with. AssetManager rewrites the package IDs so that they are compatible at runtime.
GetDynamicPackageMap()136   inline const std::vector<DynamicPackageEntry>& GetDynamicPackageMap() const {
137     return dynamic_package_map_;
138   }
139 
140   // Populates a set of ResTable_config structs, possibly excluding configurations defined for
141   // the mipmap type.
142   void CollectConfigurations(bool exclude_mipmap, std::set<ResTable_config>* out_configs) const;
143 
144   // Populates a set of strings representing locales.
145   // If `canonicalize` is set to true, each locale is transformed into its canonical format
146   // before being inserted into the set. This may cause some equivalent locales to de-dupe.
147   void CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const;
148 
149   // type_idx is TT - 1 from 0xPPTTEEEE.
GetTypeSpecByTypeIndex(uint8_t type_index)150   inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const {
151     // If the type IDs are offset in this package, we need to take that into account when searching
152     // for a type.
153     return type_specs_[type_index - type_id_offset_].get();
154   }
155 
156   template <typename Func>
ForEachTypeSpec(Func f)157   void ForEachTypeSpec(Func f) const {
158     for (size_t i = 0; i < type_specs_.size(); i++) {
159       const TypeSpecPtr& ptr = type_specs_[i];
160       if (ptr != nullptr) {
161         uint8_t type_id = ptr->type_spec->id;
162         if (ptr->idmap_entries != nullptr) {
163           type_id = ptr->idmap_entries->target_type_id;
164         }
165         f(ptr.get(), type_id - 1);
166       }
167     }
168   }
169 
170  private:
171   DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
172 
173   LoadedPackage();
174 
175   ResStringPool type_string_pool_;
176   ResStringPool key_string_pool_;
177   std::string package_name_;
178   int package_id_ = -1;
179   int type_id_offset_ = 0;
180   bool dynamic_ = false;
181   bool system_ = false;
182   bool overlay_ = false;
183 
184   ByteBucketArray<TypeSpecPtr> type_specs_;
185   std::vector<DynamicPackageEntry> dynamic_package_map_;
186 };
187 
188 // Read-only view into a resource table. This class validates all data
189 // when loading, including offsets and lengths.
190 class LoadedArsc {
191  public:
192   // Load a resource table from memory pointed to by `data` of size `len`.
193   // The lifetime of `data` must out-live the LoadedArsc returned from this method.
194   // If `system` is set to true, the LoadedArsc is considered as a system provided resource.
195   // If `load_as_shared_library` is set to true, the application package (0x7f) is treated
196   // as a shared library (0x00). When loaded into an AssetManager, the package will be assigned an
197   // ID.
198   static std::unique_ptr<const LoadedArsc> Load(const StringPiece& data,
199                                                 const LoadedIdmap* loaded_idmap = nullptr,
200                                                 bool system = false,
201                                                 bool load_as_shared_library = false);
202 
203   // Create an empty LoadedArsc. This is used when an APK has no resources.arsc.
204   static std::unique_ptr<const LoadedArsc> CreateEmpty();
205 
206   // Returns the string pool where all string resource values
207   // (Res_value::dataType == Res_value::TYPE_STRING) are indexed.
GetStringPool()208   inline const ResStringPool* GetStringPool() const {
209     return &global_string_pool_;
210   }
211 
212   // Gets a pointer to the package with the specified package ID, or nullptr if no such package
213   // exists.
214   const LoadedPackage* GetPackageById(uint8_t package_id) const;
215 
216   // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc.
GetPackages()217   inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const {
218     return packages_;
219   }
220 
221   // Returns true if this is a system provided resource.
IsSystem()222   inline bool IsSystem() const {
223     return system_;
224   }
225 
226  private:
227   DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
228 
229   LoadedArsc() = default;
230   bool LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library);
231 
232   ResStringPool global_string_pool_;
233   std::vector<std::unique_ptr<const LoadedPackage>> packages_;
234   bool system_ = false;
235 };
236 
237 }  // namespace android
238 
239 #endif /* LOADEDARSC_H_ */
240