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
32namespace android {
33
34class DynamicPackageEntry {
35 public:
36  DynamicPackageEntry() = default;
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.
46struct 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
64  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.
77using TypeSpecPtr = util::unique_cptr<TypeSpec>;
78
79class 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.
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.
106  inline const ResStringPool* GetKeyStringPool() const {
107    return &key_string_pool_;
108  }
109
110  inline const std::string& GetPackageName() const {
111    return package_name_;
112  }
113
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.
119  inline bool IsDynamic() const {
120    return dynamic_;
121  }
122
123  // Returns true if this package originates from a system provided resource.
124  inline bool IsSystem() const {
125    return system_;
126  }
127
128  // Returns true if this package is from an overlay ApkAssets.
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.
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.
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>
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.
190class 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.
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.
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.
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