Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 81e9ecfc authored by Adam Lesinski's avatar Adam Lesinski
Browse files

AAPT2: Differentiate between Android and Java package names

Android package names are more strict (ASCII only) than Java package names.
Also fixed an issue where trailing underscores were disallowed in Android
package names.

Bug: 68468089
Test: make aapt2_tests
Merged-In: I1052e9e82b6617db6065ce448d9bf7972bb68d59
Change-Id: I1052e9e82b6617db6065ce448d9bf7972bb68d59
parent 97ff9f9d
Loading
Loading
Loading
Loading
+10 −22
Original line number Diff line number Diff line
@@ -21,24 +21,21 @@
#include "Source.h"
#include "java/AnnotationProcessor.h"
#include "java/ClassDefinition.h"
#include "text/Unicode.h"
#include "util/Maybe.h"
#include "xml/XmlDom.h"

using android::StringPiece;
using ::aapt::text::IsJavaIdentifier;

namespace aapt {

static Maybe<StringPiece> ExtractJavaIdentifier(IDiagnostics* diag,
                                                const Source& source,
                                                const StringPiece& value) {
  const StringPiece sep = ".";
  auto iter = std::find_end(value.begin(), value.end(), sep.begin(), sep.end());

  StringPiece result;
  if (iter != value.end()) {
    result.assign(iter + sep.size(), value.end() - (iter + sep.size()));
  } else {
    result = value;
static Maybe<StringPiece> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source,
                                                const std::string& value) {
  StringPiece result = value;
  size_t pos = value.rfind('.');
  if (pos != std::string::npos) {
    result = result.substr(pos + 1);
  }

  if (result.empty()) {
@@ -46,19 +43,10 @@ static Maybe<StringPiece> ExtractJavaIdentifier(IDiagnostics* diag,
    return {};
  }

  iter = util::FindNonAlphaNumericAndNotInSet(result, "_");
  if (iter != result.end()) {
    diag->Error(DiagMessage(source) << "invalid character '"
                                    << StringPiece(iter, 1) << "' in '"
                                    << result << "'");
  if (!IsJavaIdentifier(result)) {
    diag->Error(DiagMessage(source) << "invalid Java identifier '" << result << "'");
    return {};
  }

  if (*result.begin() >= '0' && *result.begin() <= '9') {
    diag->Error(DiagMessage(source) << "symbol can not start with a digit");
    return {};
  }

  return result;
}

+2 −2
Original line number Diff line number Diff line
@@ -127,9 +127,9 @@ static bool VerifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
    diag->Error(DiagMessage(el->line_number)
                << "attribute 'package' in <manifest> tag must not be a reference");
    return false;
  } else if (!util::IsJavaPackageName(attr->value)) {
  } else if (!util::IsAndroidPackageName(attr->value)) {
    diag->Error(DiagMessage(el->line_number)
                << "attribute 'package' in <manifest> tag is not a valid Java package name: '"
                << "attribute 'package' in <manifest> tag is not a valid Android package name: '"
                << attr->value << "'");
    return false;
  }
+2 −1
Original line number Diff line number Diff line
@@ -85,7 +85,8 @@ bool IsJavaIdentifier(const StringPiece& str) {
    return false;
  }

  if (!IsXidStart(iter.Next())) {
  const char32_t first_codepoint = iter.Next();
  if (!IsXidStart(first_codepoint) && first_codepoint != U'_' && first_codepoint != U'$') {
    return false;
  }

+3 −2
Original line number Diff line number Diff line
@@ -44,10 +44,11 @@ TEST(UnicodeTest, IsXidContinue) {
TEST(UnicodeTest, IsJavaIdentifier) {
  EXPECT_TRUE(IsJavaIdentifier("FøøBar_12"));
  EXPECT_TRUE(IsJavaIdentifier("Føø$Bar"));
  EXPECT_TRUE(IsJavaIdentifier("_FøøBar"));
  EXPECT_TRUE(IsJavaIdentifier("$Føø$Bar"));

  EXPECT_FALSE(IsJavaIdentifier("12FøøBar"));
  EXPECT_FALSE(IsJavaIdentifier("_FøøBar"));
  EXPECT_FALSE(IsJavaIdentifier("$Føø$Bar"));
  EXPECT_FALSE(IsJavaIdentifier(".Hello"));
}

TEST(UnicodeTest, IsValidResourceEntryName) {
+34 −50
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include "androidfw/StringPiece.h"
#include "utils/Unicode.h"

#include "text/Unicode.h"
#include "text/Utf8Iterator.h"
#include "util/BigBuffer.h"
#include "util/Maybe.h"
@@ -94,72 +95,55 @@ StringPiece TrimWhitespace(const StringPiece& str) {
  return StringPiece(start, end - start);
}

StringPiece::const_iterator FindNonAlphaNumericAndNotInSet(
    const StringPiece& str, const StringPiece& allowed_chars) {
  const auto end_iter = str.end();
  for (auto iter = str.begin(); iter != end_iter; ++iter) {
    char c = *iter;
    if ((c >= u'a' && c <= u'z') || (c >= u'A' && c <= u'Z') ||
        (c >= u'0' && c <= u'9')) {
      continue;
static int IsJavaNameImpl(const StringPiece& str) {
  int pieces = 0;
  for (const StringPiece& piece : Tokenize(str, '.')) {
    pieces++;
    if (!text::IsJavaIdentifier(piece)) {
      return -1;
    }

    bool match = false;
    for (char i : allowed_chars) {
      if (c == i) {
        match = true;
        break;
  }
  return pieces;
}

    if (!match) {
      return iter;
    }
bool IsJavaClassName(const StringPiece& str) {
  return IsJavaNameImpl(str) >= 2;
}
  return end_iter;

bool IsJavaPackageName(const StringPiece& str) {
  return IsJavaNameImpl(str) >= 1;
}

bool IsJavaClassName(const StringPiece& str) {
  size_t pieces = 0;
static int IsAndroidNameImpl(const StringPiece& str) {
  int pieces = 0;
  for (const StringPiece& piece : Tokenize(str, '.')) {
    pieces++;
    if (piece.empty()) {
      return false;
      return -1;
    }

    // Can't have starting or trailing $ character.
    if (piece.data()[0] == '$' || piece.data()[piece.size() - 1] == '$') {
      return false;
    const char first_character = piece.data()[0];
    if (!::isalpha(first_character)) {
      return -1;
    }

    if (FindNonAlphaNumericAndNotInSet(piece, "$_") != piece.end()) {
      return false;
    }
  }
  return pieces >= 2;
}
    bool valid = std::all_of(piece.begin() + 1, piece.end(), [](const char c) -> bool {
      return ::isalnum(c) || c == '_';
    });

bool IsJavaPackageName(const StringPiece& str) {
  if (str.empty()) {
    return false;
    if (!valid) {
      return -1;
    }

  size_t pieces = 0;
  for (const StringPiece& piece : Tokenize(str, '.')) {
    pieces++;
    if (piece.empty()) {
      return false;
  }

    if (piece.data()[0] == '_' || piece.data()[piece.size() - 1] == '_') {
      return false;
  return pieces;
}

    if (FindNonAlphaNumericAndNotInSet(piece, "_") != piece.end()) {
      return false;
    }
bool IsAndroidPackageName(const StringPiece& str) {
  return IsAndroidNameImpl(str) > 1 || str == "android";
}
  return pieces >= 1;

bool IsAndroidSplitName(const StringPiece& str) {
  return IsAndroidNameImpl(str) > 0;
}

Maybe<std::string> GetFullyQualifiedClassName(const StringPiece& package,
@@ -176,7 +160,7 @@ Maybe<std::string> GetFullyQualifiedClassName(const StringPiece& package,
    return {};
  }

  std::string result(package.data(), package.size());
  std::string result = package.to_string();
  if (classname.data()[0] != '.') {
    result += '.';
  }
Loading