1 /*
2 * Copyright (C) 2015 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 #include "compile/IdAssigner.h"
18
19 #include <map>
20
21 #include "android-base/logging.h"
22
23 #include "ResourceTable.h"
24 #include "process/IResourceTableConsumer.h"
25 #include "util/Util.h"
26
27 namespace aapt {
28
29 /**
30 * Assigns the intended ID to the ResourceTablePackage, ResourceTableType, and
31 * ResourceEntry,
32 * as long as there is no existing ID or the ID is the same.
33 */
AssignId(IDiagnostics * diag,const ResourceId & id,const ResourceName & name,ResourceTablePackage * pkg,ResourceTableType * type,ResourceEntry * entry)34 static bool AssignId(IDiagnostics* diag, const ResourceId& id,
35 const ResourceName& name, ResourceTablePackage* pkg,
36 ResourceTableType* type, ResourceEntry* entry) {
37 if (pkg->id.value() == id.package_id()) {
38 if (!type->id || type->id.value() == id.type_id()) {
39 type->id = id.type_id();
40
41 if (!entry->id || entry->id.value() == id.entry_id()) {
42 entry->id = id.entry_id();
43 return true;
44 }
45 }
46 }
47
48 const ResourceId existing_id(pkg->id.value(), type->id ? type->id.value() : 0,
49 entry->id ? entry->id.value() : 0);
50 diag->Error(DiagMessage() << "can't assign ID " << id << " to resource "
51 << name << " with conflicting ID " << existing_id);
52 return false;
53 }
54
Consume(IAaptContext * context,ResourceTable * table)55 bool IdAssigner::Consume(IAaptContext* context, ResourceTable* table) {
56 std::map<ResourceId, ResourceName> assigned_ids;
57
58 for (auto& package : table->packages) {
59 CHECK(bool(package->id)) << "packages must have manually assigned IDs";
60
61 for (auto& type : package->types) {
62 for (auto& entry : type->entries) {
63 const ResourceName name(package->name, type->type, entry->name);
64
65 if (assigned_id_map_) {
66 // Assign the pre-assigned stable ID meant for this resource.
67 const auto iter = assigned_id_map_->find(name);
68 if (iter != assigned_id_map_->end()) {
69 const ResourceId assigned_id = iter->second;
70 const bool result =
71 AssignId(context->GetDiagnostics(), assigned_id, name,
72 package.get(), type.get(), entry.get());
73 if (!result) {
74 return false;
75 }
76 }
77 }
78
79 if (package->id && type->id && entry->id) {
80 // If the ID is set for this resource, then reserve it.
81 ResourceId resource_id(package->id.value(), type->id.value(),
82 entry->id.value());
83 auto result = assigned_ids.insert({resource_id, name});
84 const ResourceName& existing_name = result.first->second;
85 if (!result.second) {
86 context->GetDiagnostics()->Error(
87 DiagMessage() << "resource " << name << " has same ID "
88 << resource_id << " as " << existing_name);
89 return false;
90 }
91 }
92 }
93 }
94 }
95
96 if (assigned_id_map_) {
97 // Reserve all the IDs mentioned in the stable ID map. That way we won't
98 // assign
99 // IDs that were listed in the map if they don't exist in the table.
100 for (const auto& stable_id_entry : *assigned_id_map_) {
101 const ResourceName& pre_assigned_name = stable_id_entry.first;
102 const ResourceId& pre_assigned_id = stable_id_entry.second;
103 auto result = assigned_ids.insert({pre_assigned_id, pre_assigned_name});
104 const ResourceName& existing_name = result.first->second;
105 if (!result.second && existing_name != pre_assigned_name) {
106 context->GetDiagnostics()->Error(
107 DiagMessage() << "stable ID " << pre_assigned_id << " for resource "
108 << pre_assigned_name
109 << " is already taken by resource " << existing_name);
110 return false;
111 }
112 }
113 }
114
115 // Assign any resources without IDs the next available ID. Gaps will be filled
116 // if possible,
117 // unless those IDs have been reserved.
118
119 const auto assigned_ids_iter_end = assigned_ids.end();
120 for (auto& package : table->packages) {
121 CHECK(bool(package->id)) << "packages must have manually assigned IDs";
122
123 // Build a half filled ResourceId object, which will be used to find the
124 // closest matching
125 // reserved ID in the assignedId map. From that point the next available
126 // type ID can be
127 // found.
128 ResourceId resource_id(package->id.value(), 0, 0);
129 uint8_t next_expected_type_id = 1;
130
131 // Find the closest matching ResourceId that is <= the one with only the
132 // package set.
133 auto next_type_iter = assigned_ids.lower_bound(resource_id);
134 for (auto& type : package->types) {
135 if (!type->id) {
136 // We need to assign a type ID. Iterate over the reserved IDs until we
137 // find
138 // some type ID that is a distance of 2 greater than the last one we've
139 // seen.
140 // That means there is an available type ID between these reserved IDs.
141 while (next_type_iter != assigned_ids_iter_end) {
142 if (next_type_iter->first.package_id() != package->id.value()) {
143 break;
144 }
145
146 const uint8_t type_id = next_type_iter->first.type_id();
147 if (type_id > next_expected_type_id) {
148 // There is a gap in the type IDs, so use the missing one.
149 type->id = next_expected_type_id++;
150 break;
151 }
152
153 // Set our expectation to be the next type ID after the reserved one
154 // we
155 // just saw.
156 next_expected_type_id = type_id + 1;
157
158 // Move to the next reserved ID.
159 ++next_type_iter;
160 }
161
162 if (!type->id) {
163 // We must have hit the end of the reserved IDs and not found a gap.
164 // That means the next ID is available.
165 type->id = next_expected_type_id++;
166 }
167 }
168
169 resource_id = ResourceId(package->id.value(), type->id.value(), 0);
170 uint16_t next_expected_entry_id = 0;
171
172 // Find the closest matching ResourceId that is <= the one with only the
173 // package
174 // and type set.
175 auto next_entry_iter = assigned_ids.lower_bound(resource_id);
176 for (auto& entry : type->entries) {
177 if (!entry->id) {
178 // We need to assign an entry ID. Iterate over the reserved IDs until
179 // we find
180 // some entry ID that is a distance of 2 greater than the last one
181 // we've seen.
182 // That means there is an available entry ID between these reserved
183 // IDs.
184 while (next_entry_iter != assigned_ids_iter_end) {
185 if (next_entry_iter->first.package_id() != package->id.value() ||
186 next_entry_iter->first.type_id() != type->id.value()) {
187 break;
188 }
189
190 const uint16_t entry_id = next_entry_iter->first.entry_id();
191 if (entry_id > next_expected_entry_id) {
192 // There is a gap in the entry IDs, so use the missing one.
193 entry->id = next_expected_entry_id++;
194 break;
195 }
196
197 // Set our expectation to be the next type ID after the reserved one
198 // we
199 // just saw.
200 next_expected_entry_id = entry_id + 1;
201
202 // Move to the next reserved entry ID.
203 ++next_entry_iter;
204 }
205
206 if (!entry->id) {
207 // We must have hit the end of the reserved IDs and not found a gap.
208 // That means the next ID is available.
209 entry->id = next_expected_entry_id++;
210 }
211 }
212 }
213 }
214 }
215 return true;
216 }
217
218 } // namespace aapt
219