• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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