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 "util/Util.h"
18
19 #include <algorithm>
20 #include <ostream>
21 #include <string>
22 #include <vector>
23
24 #include "android-base/parseint.h"
25 #include "android-base/stringprintf.h"
26 #include "android-base/strings.h"
27 #include "androidfw/BigBuffer.h"
28 #include "androidfw/StringPiece.h"
29 #include "androidfw/Util.h"
30 #include "build/version.h"
31 #include "text/Unicode.h"
32 #include "text/Utf8Iterator.h"
33 #include "utils/Unicode.h"
34
35 using ::aapt::text::Utf8Iterator;
36 using ::android::StringPiece;
37 using ::android::StringPiece16;
38
39 namespace aapt {
40 namespace util {
41
42 // Package name and shared user id would be used as a part of the file name.
43 // Limits size to 223 and reserves 32 for the OS.
44 // See frameworks/base/core/java/android/content/pm/parsing/ParsingPackageUtils.java
45 constexpr static const size_t kMaxPackageNameSize = 223;
46
SplitAndTransform(StringPiece str,char sep,char (* f)(char))47 static std::vector<std::string> SplitAndTransform(StringPiece str, char sep, char (*f)(char)) {
48 std::vector<std::string> parts;
49 const StringPiece::const_iterator end = std::end(str);
50 StringPiece::const_iterator start = std::begin(str);
51 StringPiece::const_iterator current;
52 do {
53 current = std::find(start, end, sep);
54 parts.emplace_back(start, current);
55 if (f) {
56 std::string& part = parts.back();
57 std::transform(part.begin(), part.end(), part.begin(), f);
58 }
59 start = current + 1;
60 } while (current != end);
61 return parts;
62 }
63
Split(StringPiece str,char sep)64 std::vector<std::string> Split(StringPiece str, char sep) {
65 return SplitAndTransform(str, sep, nullptr);
66 }
67
SplitAndLowercase(StringPiece str,char sep)68 std::vector<std::string> SplitAndLowercase(StringPiece str, char sep) {
69 return SplitAndTransform(str, sep, [](char c) -> char { return ::tolower(c); });
70 }
71
StartsWith(StringPiece str,StringPiece prefix)72 bool StartsWith(StringPiece str, StringPiece prefix) {
73 if (str.size() < prefix.size()) {
74 return false;
75 }
76 return str.substr(0, prefix.size()) == prefix;
77 }
78
EndsWith(StringPiece str,StringPiece suffix)79 bool EndsWith(StringPiece str, StringPiece suffix) {
80 if (str.size() < suffix.size()) {
81 return false;
82 }
83 return str.substr(str.size() - suffix.size(), suffix.size()) == suffix;
84 }
85
TrimLeadingWhitespace(StringPiece str)86 StringPiece TrimLeadingWhitespace(StringPiece str) {
87 if (str.size() == 0 || str.data() == nullptr) {
88 return str;
89 }
90
91 const char* start = str.data();
92 const char* end = start + str.length();
93
94 while (start != end && isspace(*start)) {
95 start++;
96 }
97 return StringPiece(start, end - start);
98 }
99
TrimTrailingWhitespace(StringPiece str)100 StringPiece TrimTrailingWhitespace(StringPiece str) {
101 if (str.size() == 0 || str.data() == nullptr) {
102 return str;
103 }
104
105 const char* start = str.data();
106 const char* end = start + str.length();
107
108 while (end != start && isspace(*(end - 1))) {
109 end--;
110 }
111 return StringPiece(start, end - start);
112 }
113
TrimWhitespace(StringPiece str)114 StringPiece TrimWhitespace(StringPiece str) {
115 if (str.size() == 0 || str.data() == nullptr) {
116 return str;
117 }
118
119 const char* start = str.data();
120 const char* end = str.data() + str.length();
121
122 while (start != end && isspace(*start)) {
123 start++;
124 }
125
126 while (end != start && isspace(*(end - 1))) {
127 end--;
128 }
129
130 return StringPiece(start, end - start);
131 }
132
IsJavaNameImpl(StringPiece str)133 static int IsJavaNameImpl(StringPiece str) {
134 int pieces = 0;
135 for (StringPiece piece : Tokenize(str, '.')) {
136 pieces++;
137 if (!text::IsJavaIdentifier(piece)) {
138 return -1;
139 }
140 }
141 return pieces;
142 }
143
IsJavaClassName(StringPiece str)144 bool IsJavaClassName(StringPiece str) {
145 return IsJavaNameImpl(str) >= 2;
146 }
147
IsJavaPackageName(StringPiece str)148 bool IsJavaPackageName(StringPiece str) {
149 return IsJavaNameImpl(str) >= 1;
150 }
151
IsAndroidNameImpl(StringPiece str)152 static int IsAndroidNameImpl(StringPiece str) {
153 int pieces = 0;
154 for (StringPiece piece : Tokenize(str, '.')) {
155 if (piece.empty()) {
156 return -1;
157 }
158
159 const char first_character = piece.data()[0];
160 if (!::isalpha(first_character)) {
161 return -1;
162 }
163
164 bool valid = std::all_of(piece.begin() + 1, piece.end(), [](const char c) -> bool {
165 return ::isalnum(c) || c == '_';
166 });
167
168 if (!valid) {
169 return -1;
170 }
171 pieces++;
172 }
173 return pieces;
174 }
175
IsAndroidPackageName(StringPiece str)176 bool IsAndroidPackageName(StringPiece str) {
177 if (str.size() > kMaxPackageNameSize) {
178 return false;
179 }
180 return IsAndroidNameImpl(str) > 1 || str == "android";
181 }
182
IsAndroidSharedUserId(android::StringPiece package_name,android::StringPiece shared_user_id)183 bool IsAndroidSharedUserId(android::StringPiece package_name, android::StringPiece shared_user_id) {
184 if (shared_user_id.size() > kMaxPackageNameSize) {
185 return false;
186 }
187 return shared_user_id.empty() || IsAndroidNameImpl(shared_user_id) > 1 ||
188 package_name == "android";
189 }
190
IsAndroidSplitName(StringPiece str)191 bool IsAndroidSplitName(StringPiece str) {
192 return IsAndroidNameImpl(str) > 0;
193 }
194
GetFullyQualifiedClassName(StringPiece package,StringPiece classname)195 std::optional<std::string> GetFullyQualifiedClassName(StringPiece package, StringPiece classname) {
196 if (classname.empty()) {
197 return {};
198 }
199
200 if (util::IsJavaClassName(classname)) {
201 return std::string(classname);
202 }
203
204 if (package.empty()) {
205 return {};
206 }
207
208 std::string result{package};
209 if (classname.data()[0] != '.') {
210 result += '.';
211 }
212
213 result.append(classname.data(), classname.size());
214 if (!IsJavaClassName(result)) {
215 return {};
216 }
217 return result;
218 }
219
GetToolName()220 const char* GetToolName() {
221 static const char* const sToolName = "Android Asset Packaging Tool (aapt)";
222 return sToolName;
223 }
224
GetToolFingerprint()225 std::string GetToolFingerprint() {
226 // DO NOT UPDATE, this is more of a marketing version.
227 static const char* const sMajorVersion = "2";
228
229 // Update minor version whenever a feature or flag is added.
230 static const char* const sMinorVersion = "19";
231
232 // The build id of aapt2 binary.
233 static const std::string sBuildId = [] {
234 std::string buildNumber = android::build::GetBuildNumber();
235
236 if (android::base::StartsWith(buildNumber, "eng.")) {
237 // android::build::GetBuildNumber() returns something like "eng.user.20230725.214219" where
238 // the latter two parts are "yyyyMMdd.HHmmss" at build time. Use "yyyyMM" in the fingerprint.
239 std::vector<std::string> parts = util::Split(buildNumber, '.');
240 int buildYear;
241 int buildMonth;
242 if (parts.size() < 3 || parts[2].length() < 6 ||
243 !android::base::ParseInt(parts[2].substr(0, 4), &buildYear) ||
244 !android::base::ParseInt(parts[2].substr(4, 2), &buildMonth)) {
245 // Fallback to localtime() if GetBuildNumber() returns an unexpected output.
246 time_t now = time(0);
247 tm* ltm = localtime(&now);
248 buildYear = 1900 + ltm->tm_year;
249 buildMonth = 1 + ltm->tm_mon;
250 }
251
252 buildNumber = android::base::StringPrintf("eng.%04d%02d", buildYear, buildMonth);
253 }
254 return buildNumber;
255 }();
256
257 return android::base::StringPrintf("%s.%s-%s", sMajorVersion, sMinorVersion, sBuildId.c_str());
258 }
259
ConsumeDigits(const char * start,const char * end)260 static size_t ConsumeDigits(const char* start, const char* end) {
261 const char* c = start;
262 for (; c != end && *c >= '0' && *c <= '9'; c++) {
263 }
264 return static_cast<size_t>(c - start);
265 }
266
VerifyJavaStringFormat(StringPiece str)267 bool VerifyJavaStringFormat(StringPiece str) {
268 const char* c = str.begin();
269 const char* const end = str.end();
270
271 size_t arg_count = 0;
272 bool nonpositional = false;
273 while (c != end) {
274 if (*c == '%' && c + 1 < end) {
275 c++;
276
277 if (*c == '%' || *c == 'n') {
278 c++;
279 continue;
280 }
281
282 arg_count++;
283
284 size_t num_digits = ConsumeDigits(c, end);
285 if (num_digits > 0) {
286 c += num_digits;
287 if (c != end && *c != '$') {
288 // The digits were a size, but not a positional argument.
289 nonpositional = true;
290 }
291 } else if (*c == '<') {
292 // Reusing last argument, bad idea since positions can be moved around
293 // during translation.
294 nonpositional = true;
295
296 c++;
297
298 // Optionally we can have a $ after
299 if (c != end && *c == '$') {
300 c++;
301 }
302 } else {
303 nonpositional = true;
304 }
305
306 // Ignore size, width, flags, etc.
307 while (c != end && (*c == '-' || *c == '#' || *c == '+' || *c == ' ' ||
308 *c == ',' || *c == '(' || (*c >= '0' && *c <= '9'))) {
309 c++;
310 }
311
312 /*
313 * This is a shortcut to detect strings that are going to Time.format()
314 * instead of String.format()
315 *
316 * Comparison of String.format() and Time.format() args:
317 *
318 * String: ABC E GH ST X abcdefgh nost x
319 * Time: DEFGHKMS W Za d hkm s w yz
320 *
321 * Therefore we know it's definitely Time if we have:
322 * DFKMWZkmwyz
323 */
324 if (c != end) {
325 switch (*c) {
326 case 'D':
327 case 'F':
328 case 'K':
329 case 'M':
330 case 'W':
331 case 'Z':
332 case 'k':
333 case 'm':
334 case 'w':
335 case 'y':
336 case 'z':
337 return true;
338 }
339 }
340 }
341
342 if (c != end) {
343 c++;
344 }
345 }
346
347 if (arg_count > 1 && nonpositional) {
348 // Multiple arguments were specified, but some or all were non positional.
349 // Translated
350 // strings may rearrange the order of the arguments, which will break the
351 // string.
352 return false;
353 }
354 return true;
355 }
356
Utf8ToUtf16(StringPiece utf8)357 std::u16string Utf8ToUtf16(StringPiece utf8) {
358 ssize_t utf16_length = utf8_to_utf16_length(
359 reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
360 if (utf16_length <= 0) {
361 return {};
362 }
363
364 std::u16string utf16;
365 utf16.resize(utf16_length);
366 utf8_to_utf16(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length(),
367 &*utf16.begin(), utf16_length + 1);
368 return utf16;
369 }
370
Utf16ToUtf8(const StringPiece16 & utf16)371 std::string Utf16ToUtf8(const StringPiece16& utf16) {
372 ssize_t utf8_length = utf16_to_utf8_length(utf16.data(), utf16.length());
373 if (utf8_length <= 0) {
374 return {};
375 }
376
377 std::string utf8;
378 utf8.resize(utf8_length);
379 utf16_to_utf8(utf16.data(), utf16.length(), &*utf8.begin(), utf8_length + 1);
380 return utf8;
381 }
382
WriteAll(std::ostream & out,const android::BigBuffer & buffer)383 bool WriteAll(std::ostream& out, const android::BigBuffer& buffer) {
384 for (const auto& b : buffer) {
385 if (!out.write(reinterpret_cast<const char*>(b.buffer.get()), b.size)) {
386 return false;
387 }
388 }
389 return true;
390 }
391
operator ++()392 typename Tokenizer::iterator& Tokenizer::iterator::operator++() {
393 const char* start = token_.end();
394 const char* end = str_.end();
395 if (start == end) {
396 end_ = true;
397 token_ = StringPiece(token_.end(), 0);
398 return *this;
399 }
400
401 start += 1;
402 const char* current = start;
403 while (current != end) {
404 if (*current == separator_) {
405 token_ = StringPiece(start, current - start);
406 return *this;
407 }
408 ++current;
409 }
410 token_ = StringPiece(start, end - start);
411 return *this;
412 }
413
operator ==(const iterator & rhs) const414 bool Tokenizer::iterator::operator==(const iterator& rhs) const {
415 // We check equality here a bit differently.
416 // We need to know that the addresses are the same.
417 return token_.begin() == rhs.token_.begin() &&
418 token_.end() == rhs.token_.end() && end_ == rhs.end_;
419 }
420
operator !=(const iterator & rhs) const421 bool Tokenizer::iterator::operator!=(const iterator& rhs) const {
422 return !(*this == rhs);
423 }
424
iterator(StringPiece s,char sep,StringPiece tok,bool end)425 Tokenizer::iterator::iterator(StringPiece s, char sep, StringPiece tok, bool end)
426 : str_(s), separator_(sep), token_(tok), end_(end) {
427 }
428
Tokenizer(StringPiece str,char sep)429 Tokenizer::Tokenizer(StringPiece str, char sep)
430 : begin_(++iterator(str, sep, StringPiece(str.begin() - 1, 0), false)),
431 end_(str, sep, StringPiece(str.end(), 0), true) {
432 }
433
ExtractResFilePathParts(StringPiece path,StringPiece * out_prefix,StringPiece * out_entry,StringPiece * out_suffix)434 bool ExtractResFilePathParts(StringPiece path, StringPiece* out_prefix, StringPiece* out_entry,
435 StringPiece* out_suffix) {
436 const StringPiece res_prefix("res/");
437 if (!StartsWith(path, res_prefix)) {
438 return false;
439 }
440
441 StringPiece::const_iterator last_occurence = path.end();
442 for (auto iter = path.begin() + res_prefix.size(); iter != path.end();
443 ++iter) {
444 if (*iter == '/') {
445 last_occurence = iter;
446 }
447 }
448
449 if (last_occurence == path.end()) {
450 return false;
451 }
452
453 auto iter = std::find(last_occurence, path.end(), '.');
454 *out_suffix = StringPiece(iter, path.end() - iter);
455 *out_entry = StringPiece(last_occurence + 1, iter - last_occurence - 1);
456 *out_prefix = StringPiece(path.begin(), last_occurence - path.begin() + 1);
457 return true;
458 }
459
460 } // namespace util
461 } // namespace aapt
462