diff options
Diffstat (limited to 'tools')
99 files changed, 2128 insertions, 2477 deletions
diff --git a/tools/aapt/Android.bp b/tools/aapt/Android.bp index a594e5bf0ce1..f36739730775 100644 --- a/tools/aapt/Android.bp +++ b/tools/aapt/Android.bp @@ -71,6 +71,8 @@ cc_library_host_static { cflags: [ "-Wno-format-y2k", "-DSTATIC_ANDROIDFW_FOR_TOOLS", + // Allow implicit fallthroughs in AaptAssets.cpp until they are fixed. + "-Wno-error=implicit-fallthrough", ], srcs: [ diff --git a/tools/aapt/ConfigDescription.h b/tools/aapt/ConfigDescription.h index 09430f2532fa..b4ea624524b3 100644 --- a/tools/aapt/ConfigDescription.h +++ b/tools/aapt/ConfigDescription.h @@ -29,7 +29,7 @@ struct ConfigDescription : public android::ResTable_config { size = sizeof(android::ResTable_config); } - ConfigDescription(const android::ResTable_config&o) { // NOLINT(implicit) + ConfigDescription(const android::ResTable_config&o) { // NOLINT(google-explicit-constructor) *static_cast<android::ResTable_config*>(this) = o; size = sizeof(android::ResTable_config); } diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index 48cfc4453d8f..750fb56b2792 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -120,7 +120,6 @@ cc_library_host_static { "util/BigBuffer.cpp", "util/Files.cpp", "util/Util.cpp", - "ConfigDescription.cpp", "Debug.cpp", "DominatorTree.cpp", "Flags.cpp", @@ -130,7 +129,6 @@ cc_library_host_static { "java/ManifestClassGenerator.cpp", "java/ProguardRules.cpp", "LoadedApk.cpp", - "Locale.cpp", "Resource.cpp", "ResourceParser.cpp", "ResourceTable.cpp", diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk index c9987b86cc5c..b165c6bed220 100644 --- a/tools/aapt2/Android.mk +++ b/tools/aapt2/Android.mk @@ -2,10 +2,19 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) +aapt2_results := $(call intermediates-dir-for,PACKAGING,aapt2_run_host_unit_tests)/result.xml + # Target for running host unit tests on post/pre-submit. .PHONY: aapt2_run_host_unit_tests -aapt2_run_host_unit_tests: PRIVATE_GTEST_OPTIONS := --gtest_output=xml:$(DIST_DIR)/gtest/aapt2_host_unit_tests_result.xml -aapt2_run_host_unit_tests: $(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests - -$(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests $(PRIVATE_GTEST_OPTIONS) > /dev/null 2>&1 +aapt2_run_host_unit_tests: $(aapt2_results) + +$(call dist-for-goals,aapt2_run_host_unit_tests,$(aapt2_results):gtest/aapt2_host_unit_tests_result.xml) + +# Always run the tests again, even if they haven't changed +$(aapt2_results): .KATI_IMPLICIT_OUTPUTS := $(aapt2_results)-nocache +$(aapt2_results): $(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests + -$(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests --gtest_output=xml:$@ > /dev/null 2>&1 + +aapt2_results := include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp deleted file mode 100644 index f621660cb8ad..000000000000 --- a/tools/aapt2/ConfigDescription.cpp +++ /dev/null @@ -1,999 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ConfigDescription.h" - -#include <string> -#include <vector> - -#include "androidfw/ResourceTypes.h" -#include "androidfw/StringPiece.h" - -#include "Locale.h" -#include "SdkConstants.h" -#include "util/Util.h" - -using android::ResTable_config; -using android::StringPiece; - -namespace aapt { - -static const char* kWildcardName = "any"; - -const ConfigDescription& ConfigDescription::DefaultConfig() { - static ConfigDescription config = {}; - return config; -} - -static bool parseMcc(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) out->mcc = 0; - return true; - } - const char* c = name; - if (tolower(*c) != 'm') return false; - c++; - if (tolower(*c) != 'c') return false; - c++; - if (tolower(*c) != 'c') return false; - c++; - - const char* val = c; - - while (*c >= '0' && *c <= '9') { - c++; - } - if (*c != 0) return false; - if (c - val != 3) return false; - - int d = atoi(val); - if (d != 0) { - if (out) out->mcc = d; - return true; - } - - return false; -} - -static bool parseMnc(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) out->mnc = 0; - return true; - } - const char* c = name; - if (tolower(*c) != 'm') return false; - c++; - if (tolower(*c) != 'n') return false; - c++; - if (tolower(*c) != 'c') return false; - c++; - - const char* val = c; - - while (*c >= '0' && *c <= '9') { - c++; - } - if (*c != 0) return false; - if (c - val == 0 || c - val > 3) return false; - - if (out) { - out->mnc = atoi(val); - if (out->mnc == 0) { - out->mnc = ACONFIGURATION_MNC_ZERO; - } - } - - return true; -} - -static bool parseLayoutDirection(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) - out->screenLayout = - (out->screenLayout & ~ResTable_config::MASK_LAYOUTDIR) | - ResTable_config::LAYOUTDIR_ANY; - return true; - } else if (strcmp(name, "ldltr") == 0) { - if (out) - out->screenLayout = - (out->screenLayout & ~ResTable_config::MASK_LAYOUTDIR) | - ResTable_config::LAYOUTDIR_LTR; - return true; - } else if (strcmp(name, "ldrtl") == 0) { - if (out) - out->screenLayout = - (out->screenLayout & ~ResTable_config::MASK_LAYOUTDIR) | - ResTable_config::LAYOUTDIR_RTL; - return true; - } - - return false; -} - -static bool parseScreenLayoutSize(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) - out->screenLayout = - (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) | - ResTable_config::SCREENSIZE_ANY; - return true; - } else if (strcmp(name, "small") == 0) { - if (out) - out->screenLayout = - (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) | - ResTable_config::SCREENSIZE_SMALL; - return true; - } else if (strcmp(name, "normal") == 0) { - if (out) - out->screenLayout = - (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) | - ResTable_config::SCREENSIZE_NORMAL; - return true; - } else if (strcmp(name, "large") == 0) { - if (out) - out->screenLayout = - (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) | - ResTable_config::SCREENSIZE_LARGE; - return true; - } else if (strcmp(name, "xlarge") == 0) { - if (out) - out->screenLayout = - (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) | - ResTable_config::SCREENSIZE_XLARGE; - return true; - } - - return false; -} - -static bool parseScreenLayoutLong(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) - out->screenLayout = - (out->screenLayout & ~ResTable_config::MASK_SCREENLONG) | - ResTable_config::SCREENLONG_ANY; - return true; - } else if (strcmp(name, "long") == 0) { - if (out) - out->screenLayout = - (out->screenLayout & ~ResTable_config::MASK_SCREENLONG) | - ResTable_config::SCREENLONG_YES; - return true; - } else if (strcmp(name, "notlong") == 0) { - if (out) - out->screenLayout = - (out->screenLayout & ~ResTable_config::MASK_SCREENLONG) | - ResTable_config::SCREENLONG_NO; - return true; - } - - return false; -} - -static bool parseScreenRound(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) - out->screenLayout2 = - (out->screenLayout2 & ~ResTable_config::MASK_SCREENROUND) | - ResTable_config::SCREENROUND_ANY; - return true; - } else if (strcmp(name, "round") == 0) { - if (out) - out->screenLayout2 = - (out->screenLayout2 & ~ResTable_config::MASK_SCREENROUND) | - ResTable_config::SCREENROUND_YES; - return true; - } else if (strcmp(name, "notround") == 0) { - if (out) - out->screenLayout2 = - (out->screenLayout2 & ~ResTable_config::MASK_SCREENROUND) | - ResTable_config::SCREENROUND_NO; - return true; - } - return false; -} - -static bool parseWideColorGamut(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) - out->colorMode = - (out->colorMode & ~ResTable_config::MASK_WIDE_COLOR_GAMUT) | - ResTable_config::WIDE_COLOR_GAMUT_ANY; - return true; - } else if (strcmp(name, "widecg") == 0) { - if (out) - out->colorMode = - (out->colorMode & ~ResTable_config::MASK_WIDE_COLOR_GAMUT) | - ResTable_config::WIDE_COLOR_GAMUT_YES; - return true; - } else if (strcmp(name, "nowidecg") == 0) { - if (out) - out->colorMode = - (out->colorMode & ~ResTable_config::MASK_WIDE_COLOR_GAMUT) | - ResTable_config::WIDE_COLOR_GAMUT_NO; - return true; - } - return false; -} - -static bool parseHdr(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) - out->colorMode = - (out->colorMode & ~ResTable_config::MASK_HDR) | - ResTable_config::HDR_ANY; - return true; - } else if (strcmp(name, "highdr") == 0) { - if (out) - out->colorMode = - (out->colorMode & ~ResTable_config::MASK_HDR) | - ResTable_config::HDR_YES; - return true; - } else if (strcmp(name, "lowdr") == 0) { - if (out) - out->colorMode = - (out->colorMode & ~ResTable_config::MASK_HDR) | - ResTable_config::HDR_NO; - return true; - } - return false; -} - -static bool parseOrientation(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) out->orientation = out->ORIENTATION_ANY; - return true; - } else if (strcmp(name, "port") == 0) { - if (out) out->orientation = out->ORIENTATION_PORT; - return true; - } else if (strcmp(name, "land") == 0) { - if (out) out->orientation = out->ORIENTATION_LAND; - return true; - } else if (strcmp(name, "square") == 0) { - if (out) out->orientation = out->ORIENTATION_SQUARE; - return true; - } - - return false; -} - -static bool parseUiModeType(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) - out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) | - ResTable_config::UI_MODE_TYPE_ANY; - return true; - } else if (strcmp(name, "desk") == 0) { - if (out) - out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) | - ResTable_config::UI_MODE_TYPE_DESK; - return true; - } else if (strcmp(name, "car") == 0) { - if (out) - out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) | - ResTable_config::UI_MODE_TYPE_CAR; - return true; - } else if (strcmp(name, "television") == 0) { - if (out) - out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) | - ResTable_config::UI_MODE_TYPE_TELEVISION; - return true; - } else if (strcmp(name, "appliance") == 0) { - if (out) - out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) | - ResTable_config::UI_MODE_TYPE_APPLIANCE; - return true; - } else if (strcmp(name, "watch") == 0) { - if (out) - out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) | - ResTable_config::UI_MODE_TYPE_WATCH; - return true; - } else if (strcmp(name, "vrheadset") == 0) { - if (out) - out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) | - ResTable_config::UI_MODE_TYPE_VR_HEADSET; - return true; - } - - return false; -} - -static bool parseUiModeNight(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) - out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_NIGHT) | - ResTable_config::UI_MODE_NIGHT_ANY; - return true; - } else if (strcmp(name, "night") == 0) { - if (out) - out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_NIGHT) | - ResTable_config::UI_MODE_NIGHT_YES; - return true; - } else if (strcmp(name, "notnight") == 0) { - if (out) - out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_NIGHT) | - ResTable_config::UI_MODE_NIGHT_NO; - return true; - } - - return false; -} - -static bool parseDensity(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) out->density = ResTable_config::DENSITY_DEFAULT; - return true; - } - - if (strcmp(name, "anydpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_ANY; - return true; - } - - if (strcmp(name, "nodpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_NONE; - return true; - } - - if (strcmp(name, "ldpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_LOW; - return true; - } - - if (strcmp(name, "mdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_MEDIUM; - return true; - } - - if (strcmp(name, "tvdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_TV; - return true; - } - - if (strcmp(name, "hdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_HIGH; - return true; - } - - if (strcmp(name, "xhdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_XHIGH; - return true; - } - - if (strcmp(name, "xxhdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_XXHIGH; - return true; - } - - if (strcmp(name, "xxxhdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_XXXHIGH; - return true; - } - - char* c = (char*)name; - while (*c >= '0' && *c <= '9') { - c++; - } - - // check that we have 'dpi' after the last digit. - if (toupper(c[0]) != 'D' || toupper(c[1]) != 'P' || toupper(c[2]) != 'I' || - c[3] != 0) { - return false; - } - - // temporarily replace the first letter with \0 to - // use atoi. - char tmp = c[0]; - c[0] = '\0'; - - int d = atoi(name); - c[0] = tmp; - - if (d != 0) { - if (out) out->density = d; - return true; - } - - return false; -} - -static bool parseTouchscreen(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) out->touchscreen = out->TOUCHSCREEN_ANY; - return true; - } else if (strcmp(name, "notouch") == 0) { - if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH; - return true; - } else if (strcmp(name, "stylus") == 0) { - if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS; - return true; - } else if (strcmp(name, "finger") == 0) { - if (out) out->touchscreen = out->TOUCHSCREEN_FINGER; - return true; - } - - return false; -} - -static bool parseKeysHidden(const char* name, ResTable_config* out) { - uint8_t mask = 0; - uint8_t value = 0; - if (strcmp(name, kWildcardName) == 0) { - mask = ResTable_config::MASK_KEYSHIDDEN; - value = ResTable_config::KEYSHIDDEN_ANY; - } else if (strcmp(name, "keysexposed") == 0) { - mask = ResTable_config::MASK_KEYSHIDDEN; - value = ResTable_config::KEYSHIDDEN_NO; - } else if (strcmp(name, "keyshidden") == 0) { - mask = ResTable_config::MASK_KEYSHIDDEN; - value = ResTable_config::KEYSHIDDEN_YES; - } else if (strcmp(name, "keyssoft") == 0) { - mask = ResTable_config::MASK_KEYSHIDDEN; - value = ResTable_config::KEYSHIDDEN_SOFT; - } - - if (mask != 0) { - if (out) out->inputFlags = (out->inputFlags & ~mask) | value; - return true; - } - - return false; -} - -static bool parseKeyboard(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) out->keyboard = out->KEYBOARD_ANY; - return true; - } else if (strcmp(name, "nokeys") == 0) { - if (out) out->keyboard = out->KEYBOARD_NOKEYS; - return true; - } else if (strcmp(name, "qwerty") == 0) { - if (out) out->keyboard = out->KEYBOARD_QWERTY; - return true; - } else if (strcmp(name, "12key") == 0) { - if (out) out->keyboard = out->KEYBOARD_12KEY; - return true; - } - - return false; -} - -static bool parseNavHidden(const char* name, ResTable_config* out) { - uint8_t mask = 0; - uint8_t value = 0; - if (strcmp(name, kWildcardName) == 0) { - mask = ResTable_config::MASK_NAVHIDDEN; - value = ResTable_config::NAVHIDDEN_ANY; - } else if (strcmp(name, "navexposed") == 0) { - mask = ResTable_config::MASK_NAVHIDDEN; - value = ResTable_config::NAVHIDDEN_NO; - } else if (strcmp(name, "navhidden") == 0) { - mask = ResTable_config::MASK_NAVHIDDEN; - value = ResTable_config::NAVHIDDEN_YES; - } - - if (mask != 0) { - if (out) out->inputFlags = (out->inputFlags & ~mask) | value; - return true; - } - - return false; -} - -static bool parseNavigation(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) out->navigation = out->NAVIGATION_ANY; - return true; - } else if (strcmp(name, "nonav") == 0) { - if (out) out->navigation = out->NAVIGATION_NONAV; - return true; - } else if (strcmp(name, "dpad") == 0) { - if (out) out->navigation = out->NAVIGATION_DPAD; - return true; - } else if (strcmp(name, "trackball") == 0) { - if (out) out->navigation = out->NAVIGATION_TRACKBALL; - return true; - } else if (strcmp(name, "wheel") == 0) { - if (out) out->navigation = out->NAVIGATION_WHEEL; - return true; - } - - return false; -} - -static bool parseScreenSize(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) { - out->screenWidth = out->SCREENWIDTH_ANY; - out->screenHeight = out->SCREENHEIGHT_ANY; - } - return true; - } - - const char* x = name; - while (*x >= '0' && *x <= '9') x++; - if (x == name || *x != 'x') return false; - std::string xName(name, x - name); - x++; - - const char* y = x; - while (*y >= '0' && *y <= '9') y++; - if (y == name || *y != 0) return false; - std::string yName(x, y - x); - - uint16_t w = (uint16_t)atoi(xName.c_str()); - uint16_t h = (uint16_t)atoi(yName.c_str()); - if (w < h) { - return false; - } - - if (out) { - out->screenWidth = w; - out->screenHeight = h; - } - - return true; -} - -static bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) { - out->smallestScreenWidthDp = out->SCREENWIDTH_ANY; - } - return true; - } - - if (*name != 's') return false; - name++; - if (*name != 'w') return false; - name++; - const char* x = name; - while (*x >= '0' && *x <= '9') x++; - if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; - std::string xName(name, x - name); - - if (out) { - out->smallestScreenWidthDp = (uint16_t)atoi(xName.c_str()); - } - - return true; -} - -static bool parseScreenWidthDp(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) { - out->screenWidthDp = out->SCREENWIDTH_ANY; - } - return true; - } - - if (*name != 'w') return false; - name++; - const char* x = name; - while (*x >= '0' && *x <= '9') x++; - if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; - std::string xName(name, x - name); - - if (out) { - out->screenWidthDp = (uint16_t)atoi(xName.c_str()); - } - - return true; -} - -static bool parseScreenHeightDp(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) { - out->screenHeightDp = out->SCREENWIDTH_ANY; - } - return true; - } - - if (*name != 'h') return false; - name++; - const char* x = name; - while (*x >= '0' && *x <= '9') x++; - if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; - std::string xName(name, x - name); - - if (out) { - out->screenHeightDp = (uint16_t)atoi(xName.c_str()); - } - - return true; -} - -static bool parseVersion(const char* name, ResTable_config* out) { - if (strcmp(name, kWildcardName) == 0) { - if (out) { - out->sdkVersion = out->SDKVERSION_ANY; - out->minorVersion = out->MINORVERSION_ANY; - } - return true; - } - - if (*name != 'v') { - return false; - } - - name++; - const char* s = name; - while (*s >= '0' && *s <= '9') s++; - if (s == name || *s != 0) return false; - std::string sdkName(name, s - name); - - if (out) { - out->sdkVersion = (uint16_t)atoi(sdkName.c_str()); - out->minorVersion = 0; - } - - return true; -} - -bool ConfigDescription::Parse(const StringPiece& str, ConfigDescription* out) { - std::vector<std::string> parts = util::SplitAndLowercase(str, '-'); - - ConfigDescription config; - ssize_t parts_consumed = 0; - LocaleValue locale; - - const auto parts_end = parts.end(); - auto part_iter = parts.begin(); - - if (str.size() == 0) { - goto success; - } - - if (parseMcc(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseMnc(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - // Locale spans a few '-' separators, so we let it - // control the index. - parts_consumed = locale.InitFromParts(part_iter, parts_end); - if (parts_consumed < 0) { - return false; - } else { - locale.WriteTo(&config); - part_iter += parts_consumed; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseLayoutDirection(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseSmallestScreenWidthDp(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseScreenWidthDp(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseScreenHeightDp(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseScreenLayoutSize(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseScreenLayoutLong(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseScreenRound(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseWideColorGamut(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseHdr(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseOrientation(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseUiModeType(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseUiModeNight(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseDensity(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseTouchscreen(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseKeysHidden(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseKeyboard(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseNavHidden(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseNavigation(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseScreenSize(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - if (parseVersion(part_iter->c_str(), &config)) { - ++part_iter; - if (part_iter == parts_end) { - goto success; - } - } - - // Unrecognized. - return false; - -success: - if (out != NULL) { - ApplyVersionForCompatibility(&config); - *out = config; - } - return true; -} - -void ConfigDescription::ApplyVersionForCompatibility( - ConfigDescription* config) { - uint16_t min_sdk = 0; - if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE) - == ResTable_config::UI_MODE_TYPE_VR_HEADSET || - config->colorMode & ResTable_config::MASK_WIDE_COLOR_GAMUT || - config->colorMode & ResTable_config::MASK_HDR) { - min_sdk = SDK_O; - } else if (config->screenLayout2 & ResTable_config::MASK_SCREENROUND) { - min_sdk = SDK_MARSHMALLOW; - } else if (config->density == ResTable_config::DENSITY_ANY) { - min_sdk = SDK_LOLLIPOP; - } else if (config->smallestScreenWidthDp != - ResTable_config::SCREENWIDTH_ANY || - config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY || - config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) { - min_sdk = SDK_HONEYCOMB_MR2; - } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE) != - ResTable_config::UI_MODE_TYPE_ANY || - (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT) != - ResTable_config::UI_MODE_NIGHT_ANY) { - min_sdk = SDK_FROYO; - } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE) != - ResTable_config::SCREENSIZE_ANY || - (config->screenLayout & ResTable_config::MASK_SCREENLONG) != - ResTable_config::SCREENLONG_ANY || - config->density != ResTable_config::DENSITY_DEFAULT) { - min_sdk = SDK_DONUT; - } - - if (min_sdk > config->sdkVersion) { - config->sdkVersion = min_sdk; - } -} - -ConfigDescription ConfigDescription::CopyWithoutSdkVersion() const { - ConfigDescription copy = *this; - copy.sdkVersion = 0; - return copy; -} - -std::string ConfigDescription::GetBcp47LanguageTag(bool canonicalize) const { - char locale[RESTABLE_MAX_LOCALE_LEN]; - getBcp47Locale(locale, canonicalize); - return std::string(locale); -} - -std::string ConfigDescription::to_string() const { - const android::String8 str = toString(); - return std::string(str.string(), str.size()); -} - -bool ConfigDescription::Dominates(const ConfigDescription& o) const { - if (*this == o) { - return true; - } - - // Locale de-duping is not-trivial, disable for now (b/62409213). - if (diff(o) & CONFIG_LOCALE) { - return false; - } - - if (*this == DefaultConfig()) { - return true; - } - return MatchWithDensity(o) && !o.MatchWithDensity(*this) && - !isMoreSpecificThan(o) && !o.HasHigherPrecedenceThan(*this); -} - -bool ConfigDescription::HasHigherPrecedenceThan( - const ConfigDescription& o) const { - // The order of the following tests defines the importance of one - // configuration parameter over another. Those tests first are more - // important, trumping any values in those following them. - // The ordering should be the same as ResTable_config#isBetterThan. - if (mcc || o.mcc) return (!o.mcc); - if (mnc || o.mnc) return (!o.mnc); - if (language[0] || o.language[0]) return (!o.language[0]); - if (country[0] || o.country[0]) return (!o.country[0]); - // Script and variant require either a language or country, both of which - // have higher precedence. - if ((screenLayout | o.screenLayout) & MASK_LAYOUTDIR) { - return !(o.screenLayout & MASK_LAYOUTDIR); - } - if (smallestScreenWidthDp || o.smallestScreenWidthDp) - return (!o.smallestScreenWidthDp); - if (screenWidthDp || o.screenWidthDp) return (!o.screenWidthDp); - if (screenHeightDp || o.screenHeightDp) return (!o.screenHeightDp); - if ((screenLayout | o.screenLayout) & MASK_SCREENSIZE) { - return !(o.screenLayout & MASK_SCREENSIZE); - } - if ((screenLayout | o.screenLayout) & MASK_SCREENLONG) { - return !(o.screenLayout & MASK_SCREENLONG); - } - if ((screenLayout2 | o.screenLayout2) & MASK_SCREENROUND) { - return !(o.screenLayout2 & MASK_SCREENROUND); - } - if ((colorMode | o.colorMode) & MASK_HDR) { - return !(o.colorMode & MASK_HDR); - } - if ((colorMode | o.colorMode) & MASK_WIDE_COLOR_GAMUT) { - return !(o.colorMode & MASK_WIDE_COLOR_GAMUT); - } - if (orientation || o.orientation) return (!o.orientation); - if ((uiMode | o.uiMode) & MASK_UI_MODE_TYPE) { - return !(o.uiMode & MASK_UI_MODE_TYPE); - } - if ((uiMode | o.uiMode) & MASK_UI_MODE_NIGHT) { - return !(o.uiMode & MASK_UI_MODE_NIGHT); - } - if (density || o.density) return (!o.density); - if (touchscreen || o.touchscreen) return (!o.touchscreen); - if ((inputFlags | o.inputFlags) & MASK_KEYSHIDDEN) { - return !(o.inputFlags & MASK_KEYSHIDDEN); - } - if ((inputFlags | o.inputFlags) & MASK_NAVHIDDEN) { - return !(o.inputFlags & MASK_NAVHIDDEN); - } - if (keyboard || o.keyboard) return (!o.keyboard); - if (navigation || o.navigation) return (!o.navigation); - if (screenWidth || o.screenWidth) return (!o.screenWidth); - if (screenHeight || o.screenHeight) return (!o.screenHeight); - if (sdkVersion || o.sdkVersion) return (!o.sdkVersion); - if (minorVersion || o.minorVersion) return (!o.minorVersion); - // Both configurations have nothing defined except some possible future - // value. Returning the comparison of the two configurations is a - // "best effort" at this point to protect against incorrect dominations. - return *this != o; -} - -bool ConfigDescription::ConflictsWith(const ConfigDescription& o) const { - // This method should be updated as new configuration parameters are - // introduced (e.g. screenConfig2). - auto pred = [](const uint32_t a, const uint32_t b) -> bool { - return a == 0 || b == 0 || a == b; - }; - // The values here can be found in ResTable_config#match. Density and range - // values can't lead to conflicts, and are ignored. - return !pred(mcc, o.mcc) || !pred(mnc, o.mnc) || !pred(locale, o.locale) || - !pred(screenLayout & MASK_LAYOUTDIR, - o.screenLayout & MASK_LAYOUTDIR) || - !pred(screenLayout & MASK_SCREENLONG, - o.screenLayout & MASK_SCREENLONG) || - !pred(uiMode & MASK_UI_MODE_TYPE, o.uiMode & MASK_UI_MODE_TYPE) || - !pred(uiMode & MASK_UI_MODE_NIGHT, o.uiMode & MASK_UI_MODE_NIGHT) || - !pred(screenLayout2 & MASK_SCREENROUND, - o.screenLayout2 & MASK_SCREENROUND) || - !pred(colorMode & MASK_HDR, o.colorMode & MASK_HDR) || - !pred(colorMode & MASK_WIDE_COLOR_GAMUT, - o.colorMode & MASK_WIDE_COLOR_GAMUT) || - !pred(orientation, o.orientation) || - !pred(touchscreen, o.touchscreen) || - !pred(inputFlags & MASK_KEYSHIDDEN, o.inputFlags & MASK_KEYSHIDDEN) || - !pred(inputFlags & MASK_NAVHIDDEN, o.inputFlags & MASK_NAVHIDDEN) || - !pred(keyboard, o.keyboard) || !pred(navigation, o.navigation); -} - -bool ConfigDescription::IsCompatibleWith(const ConfigDescription& o) const { - return !ConflictsWith(o) && !Dominates(o) && !o.Dominates(*this); -} - -} // namespace aapt diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h deleted file mode 100644 index b46a50398217..000000000000 --- a/tools/aapt2/ConfigDescription.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AAPT_CONFIG_DESCRIPTION_H -#define AAPT_CONFIG_DESCRIPTION_H - -#include <ostream> - -#include "androidfw/ResourceTypes.h" -#include "androidfw/StringPiece.h" - -namespace aapt { - -/* - * Subclass of ResTable_config that adds convenient - * initialization and comparison methods. - */ -struct ConfigDescription : public android::ResTable_config { - /** - * Returns an immutable default config. - */ - static const ConfigDescription& DefaultConfig(); - - /* - * Parse a string of the form 'fr-sw600dp-land' and fill in the - * given ResTable_config with resulting configuration parameters. - * - * The resulting configuration has the appropriate sdkVersion defined - * for backwards compatibility. - */ - static bool Parse(const android::StringPiece& str, ConfigDescription* out = nullptr); - - /** - * If the configuration uses an axis that was added after - * the original Android release, make sure the SDK version - * is set accordingly. - */ - static void ApplyVersionForCompatibility(ConfigDescription* config); - - ConfigDescription(); - ConfigDescription(const android::ResTable_config& o); // NOLINT(implicit) - ConfigDescription(const ConfigDescription& o); - ConfigDescription(ConfigDescription&& o) noexcept; - - ConfigDescription& operator=(const android::ResTable_config& o); - ConfigDescription& operator=(const ConfigDescription& o); - ConfigDescription& operator=(ConfigDescription&& o) noexcept; - - ConfigDescription CopyWithoutSdkVersion() const; - - // Returns the BCP-47 language tag of this configuration's locale. - std::string GetBcp47LanguageTag(bool canonicalize = false) const; - - std::string to_string() const; - - /** - * A configuration X dominates another configuration Y, if X has at least the - * precedence of Y and X is strictly more general than Y: for any type defined - * by X, the same type is defined by Y with a value equal to or, in the case - * of ranges, more specific than that of X. - * - * For example, the configuration 'en-w800dp' dominates 'en-rGB-w1024dp'. It - * does not dominate 'fr', 'en-w720dp', or 'mcc001-en-w800dp'. - */ - bool Dominates(const ConfigDescription& o) const; - - /** - * Returns true if this configuration defines a more important configuration - * parameter than o. For example, "en" has higher precedence than "v23", - * whereas "en" has the same precedence as "en-v23". - */ - bool HasHigherPrecedenceThan(const ConfigDescription& o) const; - - /** - * A configuration conflicts with another configuration if both - * configurations define an incompatible configuration parameter. An - * incompatible configuration parameter is a non-range, non-density parameter - * that is defined in both configurations as a different, non-default value. - */ - bool ConflictsWith(const ConfigDescription& o) const; - - /** - * A configuration is compatible with another configuration if both - * configurations can match a common concrete device configuration and are - * unrelated by domination. For example, land-v11 conflicts with port-v21 - * but is compatible with v21 (both land-v11 and v21 would match en-land-v23). - */ - bool IsCompatibleWith(const ConfigDescription& o) const; - - bool MatchWithDensity(const ConfigDescription& o) const; - - bool operator<(const ConfigDescription& o) const; - bool operator<=(const ConfigDescription& o) const; - bool operator==(const ConfigDescription& o) const; - bool operator!=(const ConfigDescription& o) const; - bool operator>=(const ConfigDescription& o) const; - bool operator>(const ConfigDescription& o) const; -}; - -inline ConfigDescription::ConfigDescription() { - memset(this, 0, sizeof(*this)); - size = sizeof(android::ResTable_config); -} - -inline ConfigDescription::ConfigDescription(const android::ResTable_config& o) { - *static_cast<android::ResTable_config*>(this) = o; - size = sizeof(android::ResTable_config); -} - -inline ConfigDescription::ConfigDescription(const ConfigDescription& o) { - *static_cast<android::ResTable_config*>(this) = o; -} - -inline ConfigDescription::ConfigDescription(ConfigDescription&& o) noexcept { - *this = o; -} - -inline ConfigDescription& ConfigDescription::operator=( - const android::ResTable_config& o) { - *static_cast<android::ResTable_config*>(this) = o; - size = sizeof(android::ResTable_config); - return *this; -} - -inline ConfigDescription& ConfigDescription::operator=( - const ConfigDescription& o) { - *static_cast<android::ResTable_config*>(this) = o; - return *this; -} - -inline ConfigDescription& ConfigDescription::operator=(ConfigDescription&& o) noexcept { - *this = o; - return *this; -} - -inline bool ConfigDescription::MatchWithDensity( - const ConfigDescription& o) const { - return match(o) && (density == 0 || density == o.density); -} - -inline bool ConfigDescription::operator<(const ConfigDescription& o) const { - return compare(o) < 0; -} - -inline bool ConfigDescription::operator<=(const ConfigDescription& o) const { - return compare(o) <= 0; -} - -inline bool ConfigDescription::operator==(const ConfigDescription& o) const { - return compare(o) == 0; -} - -inline bool ConfigDescription::operator!=(const ConfigDescription& o) const { - return compare(o) != 0; -} - -inline bool ConfigDescription::operator>=(const ConfigDescription& o) const { - return compare(o) >= 0; -} - -inline bool ConfigDescription::operator>(const ConfigDescription& o) const { - return compare(o) > 0; -} - -inline ::std::ostream& operator<<(::std::ostream& out, - const ConfigDescription& o) { - return out << o.toString().string(); -} - -} // namespace aapt - -#endif // AAPT_CONFIG_DESCRIPTION_H diff --git a/tools/aapt2/ConfigDescription_test.cpp b/tools/aapt2/ConfigDescription_test.cpp deleted file mode 100644 index 1f351bf7481d..000000000000 --- a/tools/aapt2/ConfigDescription_test.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ConfigDescription.h" - -#include <string> - -#include "androidfw/StringPiece.h" - -#include "SdkConstants.h" -#include "test/Test.h" - -using android::StringPiece; - -namespace aapt { - -static ::testing::AssertionResult TestParse( - const StringPiece& input, ConfigDescription* config = nullptr) { - if (ConfigDescription::Parse(input, config)) { - return ::testing::AssertionSuccess() << input << " was successfully parsed"; - } - return ::testing::AssertionFailure() << input << " could not be parsed"; -} - -TEST(ConfigDescriptionTest, ParseFailWhenQualifiersAreOutOfOrder) { - EXPECT_FALSE(TestParse("en-sw600dp-ldrtl")); - EXPECT_FALSE(TestParse("land-en")); - EXPECT_FALSE(TestParse("hdpi-320dpi")); -} - -TEST(ConfigDescriptionTest, ParseFailWhenQualifiersAreNotMatched) { - EXPECT_FALSE(TestParse("en-sw600dp-ILLEGAL")); -} - -TEST(ConfigDescriptionTest, ParseFailWhenQualifiersHaveTrailingDash) { - EXPECT_FALSE(TestParse("en-sw600dp-land-")); -} - -TEST(ConfigDescriptionTest, ParseBasicQualifiers) { - ConfigDescription config; - EXPECT_TRUE(TestParse("", &config)); - EXPECT_EQ(std::string(""), config.toString().string()); - - EXPECT_TRUE(TestParse("fr-land", &config)); - EXPECT_EQ(std::string("fr-land"), config.toString().string()); - - EXPECT_TRUE( - TestParse("mcc310-pl-sw720dp-normal-long-port-night-" - "xhdpi-keyssoft-qwerty-navexposed-nonav", - &config)); - EXPECT_EQ(std::string("mcc310-pl-sw720dp-normal-long-port-night-" - "xhdpi-keyssoft-qwerty-navexposed-nonav-v13"), - config.toString().string()); -} - -TEST(ConfigDescriptionTest, ParseLocales) { - ConfigDescription config; - EXPECT_TRUE(TestParse("en-rUS", &config)); - EXPECT_EQ(std::string("en-rUS"), config.toString().string()); -} - -TEST(ConfigDescriptionTest, ParseQualifierAddedInApi13) { - ConfigDescription config; - EXPECT_TRUE(TestParse("sw600dp", &config)); - EXPECT_EQ(std::string("sw600dp-v13"), config.toString().string()); - - EXPECT_TRUE(TestParse("sw600dp-v8", &config)); - EXPECT_EQ(std::string("sw600dp-v13"), config.toString().string()); -} - -TEST(ConfigDescriptionTest, ParseCarAttribute) { - ConfigDescription config; - EXPECT_TRUE(TestParse("car", &config)); - EXPECT_EQ(android::ResTable_config::UI_MODE_TYPE_CAR, config.uiMode); -} - -TEST(ConfigDescriptionTest, TestParsingRoundQualifier) { - ConfigDescription config; - EXPECT_TRUE(TestParse("round", &config)); - EXPECT_EQ(android::ResTable_config::SCREENROUND_YES, - config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND); - EXPECT_EQ(SDK_MARSHMALLOW, config.sdkVersion); - EXPECT_EQ(std::string("round-v23"), config.toString().string()); - - EXPECT_TRUE(TestParse("notround", &config)); - EXPECT_EQ(android::ResTable_config::SCREENROUND_NO, - config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND); - EXPECT_EQ(SDK_MARSHMALLOW, config.sdkVersion); - EXPECT_EQ(std::string("notround-v23"), config.toString().string()); -} - -TEST(ConfigDescriptionTest, TestWideColorGamutQualifier) { - ConfigDescription config; - EXPECT_TRUE(TestParse("widecg", &config)); - EXPECT_EQ(android::ResTable_config::WIDE_COLOR_GAMUT_YES, - config.colorMode & android::ResTable_config::MASK_WIDE_COLOR_GAMUT); - EXPECT_EQ(SDK_O, config.sdkVersion); - EXPECT_EQ(std::string("widecg-v26"), config.toString().string()); - - EXPECT_TRUE(TestParse("nowidecg", &config)); - EXPECT_EQ(android::ResTable_config::WIDE_COLOR_GAMUT_NO, - config.colorMode & android::ResTable_config::MASK_WIDE_COLOR_GAMUT); - EXPECT_EQ(SDK_O, config.sdkVersion); - EXPECT_EQ(std::string("nowidecg-v26"), config.toString().string()); -} - -TEST(ConfigDescriptionTest, TestHdrQualifier) { - ConfigDescription config; - EXPECT_TRUE(TestParse("highdr", &config)); - EXPECT_EQ(android::ResTable_config::HDR_YES, - config.colorMode & android::ResTable_config::MASK_HDR); - EXPECT_EQ(SDK_O, config.sdkVersion); - EXPECT_EQ(std::string("highdr-v26"), config.toString().string()); - - EXPECT_TRUE(TestParse("lowdr", &config)); - EXPECT_EQ(android::ResTable_config::HDR_NO, - config.colorMode & android::ResTable_config::MASK_HDR); - EXPECT_EQ(SDK_O, config.sdkVersion); - EXPECT_EQ(std::string("lowdr-v26"), config.toString().string()); -} - -TEST(ConfigDescriptionTest, ParseVrAttribute) { - ConfigDescription config; - EXPECT_TRUE(TestParse("vrheadset", &config)); - EXPECT_EQ(android::ResTable_config::UI_MODE_TYPE_VR_HEADSET, config.uiMode); - EXPECT_EQ(SDK_O, config.sdkVersion); - EXPECT_EQ(std::string("vrheadset-v26"), config.toString().string()); -} - -TEST(ConfigDescriptionTest, RangeQualifiersDoNotConflict) { - using test::ParseConfigOrDie; - - EXPECT_FALSE(ParseConfigOrDie("large").ConflictsWith(ParseConfigOrDie("normal-land"))); - EXPECT_FALSE(ParseConfigOrDie("long-hdpi").ConflictsWith(ParseConfigOrDie("xhdpi"))); - EXPECT_FALSE(ParseConfigOrDie("sw600dp").ConflictsWith(ParseConfigOrDie("sw700dp"))); - EXPECT_FALSE(ParseConfigOrDie("v11").ConflictsWith(ParseConfigOrDie("v21"))); - EXPECT_FALSE(ParseConfigOrDie("h600dp").ConflictsWith(ParseConfigOrDie("h300dp"))); - EXPECT_FALSE(ParseConfigOrDie("w400dp").ConflictsWith(ParseConfigOrDie("w300dp"))); - EXPECT_FALSE(ParseConfigOrDie("600x400").ConflictsWith(ParseConfigOrDie("300x200"))); -} - -} // namespace aapt diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp index f064cb14248f..e0d7abe4cf43 100644 --- a/tools/aapt2/Debug.cpp +++ b/tools/aapt2/Debug.cpp @@ -414,7 +414,7 @@ class XmlPrinter : public xml::ConstVisitor { public: using xml::ConstVisitor::Visit; - XmlPrinter(Printer* printer) : printer_(printer) { + explicit XmlPrinter(Printer* printer) : printer_(printer) { } void Visit(const xml::Element* el) override { diff --git a/tools/aapt2/DominatorTree.cpp b/tools/aapt2/DominatorTree.cpp index 118a385e2253..ff18033c641a 100644 --- a/tools/aapt2/DominatorTree.cpp +++ b/tools/aapt2/DominatorTree.cpp @@ -19,8 +19,9 @@ #include <algorithm> #include "android-base/logging.h" +#include "androidfw/ConfigDescription.h" -#include "ConfigDescription.h" +using ::android::ConfigDescription; namespace aapt { diff --git a/tools/aapt2/DominatorTree_test.cpp b/tools/aapt2/DominatorTree_test.cpp index efc523fcf59b..fe4f951a5cd0 100644 --- a/tools/aapt2/DominatorTree_test.cpp +++ b/tools/aapt2/DominatorTree_test.cpp @@ -23,6 +23,8 @@ #include "test/Test.h" #include "util/Util.h" +using ::android::ConfigDescription; + namespace aapt { namespace { diff --git a/tools/aapt2/Locale.cpp b/tools/aapt2/Locale.cpp deleted file mode 100644 index d81921f23904..000000000000 --- a/tools/aapt2/Locale.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Locale.h" - -#include <ctype.h> - -#include <algorithm> -#include <string> -#include <vector> - -#include "util/Util.h" - -using ::android::ResTable_config; -using ::android::StringPiece; - -namespace aapt { - -void LocaleValue::set_language(const char* language_chars) { - size_t i = 0; - while ((*language_chars) != '\0') { - language[i++] = ::tolower(*language_chars); - language_chars++; - } -} - -void LocaleValue::set_region(const char* region_chars) { - size_t i = 0; - while ((*region_chars) != '\0') { - region[i++] = ::toupper(*region_chars); - region_chars++; - } -} - -void LocaleValue::set_script(const char* script_chars) { - size_t i = 0; - while ((*script_chars) != '\0') { - if (i == 0) { - script[i++] = ::toupper(*script_chars); - } else { - script[i++] = ::tolower(*script_chars); - } - script_chars++; - } -} - -void LocaleValue::set_variant(const char* variant_chars) { - size_t i = 0; - while ((*variant_chars) != '\0') { - variant[i++] = *variant_chars; - variant_chars++; - } -} - -static inline bool is_alpha(const std::string& str) { - return std::all_of(std::begin(str), std::end(str), ::isalpha); -} - -static inline bool is_number(const std::string& str) { - return std::all_of(std::begin(str), std::end(str), ::isdigit); -} - -bool LocaleValue::InitFromFilterString(const StringPiece& str) { - // A locale (as specified in the filter) is an underscore separated name such - // as "en_US", "en_Latn_US", or "en_US_POSIX". - std::vector<std::string> parts = util::SplitAndLowercase(str, '_'); - - const int num_tags = parts.size(); - bool valid = false; - if (num_tags >= 1) { - const std::string& lang = parts[0]; - if (is_alpha(lang) && (lang.length() == 2 || lang.length() == 3)) { - set_language(lang.c_str()); - valid = true; - } - } - - if (!valid || num_tags == 1) { - return valid; - } - - // At this point, valid == true && numTags > 1. - const std::string& part2 = parts[1]; - if ((part2.length() == 2 && is_alpha(part2)) || - (part2.length() == 3 && is_number(part2))) { - set_region(part2.c_str()); - } else if (part2.length() == 4 && is_alpha(part2)) { - set_script(part2.c_str()); - } else if (part2.length() >= 4 && part2.length() <= 8) { - set_variant(part2.c_str()); - } else { - valid = false; - } - - if (!valid || num_tags == 2) { - return valid; - } - - // At this point, valid == true && numTags > 1. - const std::string& part3 = parts[2]; - if (((part3.length() == 2 && is_alpha(part3)) || - (part3.length() == 3 && is_number(part3))) && - script[0]) { - set_region(part3.c_str()); - } else if (part3.length() >= 4 && part3.length() <= 8) { - set_variant(part3.c_str()); - } else { - valid = false; - } - - if (!valid || num_tags == 3) { - return valid; - } - - const std::string& part4 = parts[3]; - if (part4.length() >= 4 && part4.length() <= 8) { - set_variant(part4.c_str()); - } else { - valid = false; - } - - if (!valid || num_tags > 4) { - return false; - } - - return true; -} - -bool LocaleValue::InitFromBcp47Tag(const StringPiece& bcp47tag) { - return InitFromBcp47TagImpl(bcp47tag, '-'); -} - -bool LocaleValue::InitFromBcp47TagImpl(const StringPiece& bcp47tag, const char separator) { - std::vector<std::string> subtags = util::SplitAndLowercase(bcp47tag, separator); - if (subtags.size() == 1) { - set_language(subtags[0].c_str()); - } else if (subtags.size() == 2) { - set_language(subtags[0].c_str()); - - // The second tag can either be a region, a variant or a script. - switch (subtags[1].size()) { - case 2: - case 3: - set_region(subtags[1].c_str()); - break; - case 4: - if ('0' <= subtags[1][0] && subtags[1][0] <= '9') { - // This is a variant: fall through - } else { - set_script(subtags[1].c_str()); - break; - } - case 5: - case 6: - case 7: - case 8: - set_variant(subtags[1].c_str()); - break; - default: - return false; - } - } else if (subtags.size() == 3) { - // The language is always the first subtag. - set_language(subtags[0].c_str()); - - // The second subtag can either be a script or a region code. - // If its size is 4, it's a script code, else it's a region code. - if (subtags[1].size() == 4) { - set_script(subtags[1].c_str()); - } else if (subtags[1].size() == 2 || subtags[1].size() == 3) { - set_region(subtags[1].c_str()); - } else { - return false; - } - - // The third tag can either be a region code (if the second tag was - // a script), else a variant code. - if (subtags[2].size() >= 4) { - set_variant(subtags[2].c_str()); - } else { - set_region(subtags[2].c_str()); - } - } else if (subtags.size() == 4) { - set_language(subtags[0].c_str()); - set_script(subtags[1].c_str()); - set_region(subtags[2].c_str()); - set_variant(subtags[3].c_str()); - } else { - return false; - } - return true; -} - -ssize_t LocaleValue::InitFromParts(std::vector<std::string>::iterator iter, - std::vector<std::string>::iterator end) { - const std::vector<std::string>::iterator start_iter = iter; - - std::string& part = *iter; - if (part[0] == 'b' && part[1] == '+') { - // This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags, - // except that the separator is "+" and not "-". Skip the prefix 'b+'. - if (!InitFromBcp47TagImpl(StringPiece(part).substr(2), '+')) { - return -1; - } - ++iter; - } else { - if ((part.length() == 2 || part.length() == 3) && is_alpha(part) && part != "car") { - set_language(part.c_str()); - ++iter; - - if (iter != end) { - const std::string& region_part = *iter; - if (region_part.c_str()[0] == 'r' && region_part.length() == 3) { - set_region(region_part.c_str() + 1); - ++iter; - } - } - } - } - return static_cast<ssize_t>(iter - start_iter); -} - -void LocaleValue::InitFromResTable(const ResTable_config& config) { - config.unpackLanguage(language); - config.unpackRegion(region); - if (config.localeScript[0] && !config.localeScriptWasComputed) { - memcpy(script, config.localeScript, sizeof(config.localeScript)); - } - - if (config.localeVariant[0]) { - memcpy(variant, config.localeVariant, sizeof(config.localeVariant)); - } -} - -void LocaleValue::WriteTo(ResTable_config* out) const { - out->packLanguage(language); - out->packRegion(region); - - if (script[0]) { - memcpy(out->localeScript, script, sizeof(out->localeScript)); - } - - if (variant[0]) { - memcpy(out->localeVariant, variant, sizeof(out->localeVariant)); - } -} - -} // namespace aapt diff --git a/tools/aapt2/Locale.h b/tools/aapt2/Locale.h deleted file mode 100644 index 6d8b598415cc..000000000000 --- a/tools/aapt2/Locale.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AAPT_LOCALE_VALUE_H -#define AAPT_LOCALE_VALUE_H - -#include <string> -#include <vector> - -#include "androidfw/ResourceTypes.h" -#include "androidfw/StringPiece.h" - -namespace aapt { - -/** - * A convenience class to build and parse locales. - */ -struct LocaleValue { - char language[4]; - char region[4]; - char script[4]; - char variant[8]; - - inline LocaleValue(); - - /** - * Initialize this LocaleValue from a config string. - */ - bool InitFromFilterString(const android::StringPiece& config); - - // Initializes this LocaleValue from a BCP-47 locale tag. - bool InitFromBcp47Tag(const android::StringPiece& bcp47tag); - - /** - * Initialize this LocaleValue from parts of a vector. - */ - ssize_t InitFromParts(std::vector<std::string>::iterator iter, - std::vector<std::string>::iterator end); - - /** - * Initialize this LocaleValue from a ResTable_config. - */ - void InitFromResTable(const android::ResTable_config& config); - - /** - * Set the locale in a ResTable_config from this LocaleValue. - */ - void WriteTo(android::ResTable_config* out) const; - - inline int compare(const LocaleValue& other) const; - - inline bool operator<(const LocaleValue& o) const; - inline bool operator<=(const LocaleValue& o) const; - inline bool operator==(const LocaleValue& o) const; - inline bool operator!=(const LocaleValue& o) const; - inline bool operator>=(const LocaleValue& o) const; - inline bool operator>(const LocaleValue& o) const; - - private: - bool InitFromBcp47TagImpl(const android::StringPiece& bcp47tag, const char separator); - - void set_language(const char* language); - void set_region(const char* language); - void set_script(const char* script); - void set_variant(const char* variant); -}; - -// -// Implementation -// - -LocaleValue::LocaleValue() { memset(this, 0, sizeof(LocaleValue)); } - -int LocaleValue::compare(const LocaleValue& other) const { - return memcmp(this, &other, sizeof(LocaleValue)); -} - -bool LocaleValue::operator<(const LocaleValue& o) const { - return compare(o) < 0; -} - -bool LocaleValue::operator<=(const LocaleValue& o) const { - return compare(o) <= 0; -} - -bool LocaleValue::operator==(const LocaleValue& o) const { - return compare(o) == 0; -} - -bool LocaleValue::operator!=(const LocaleValue& o) const { - return compare(o) != 0; -} - -bool LocaleValue::operator>=(const LocaleValue& o) const { - return compare(o) >= 0; -} - -bool LocaleValue::operator>(const LocaleValue& o) const { - return compare(o) > 0; -} - -} // namespace aapt - -#endif // AAPT_LOCALE_VALUE_H diff --git a/tools/aapt2/Locale_test.cpp b/tools/aapt2/Locale_test.cpp deleted file mode 100644 index 68b4cae44e15..000000000000 --- a/tools/aapt2/Locale_test.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Locale.h" - -#include <string> - -#include "gtest/gtest.h" - -#include "util/Util.h" - -namespace aapt { - -static ::testing::AssertionResult TestLanguage(const char* input, - const char* lang) { - std::vector<std::string> parts = util::SplitAndLowercase(input, '-'); - LocaleValue lv; - ssize_t count = lv.InitFromParts(std::begin(parts), std::end(parts)); - if (count < 0) { - return ::testing::AssertionFailure() << " failed to parse '" << input - << "'."; - } - - if (count != 1) { - return ::testing::AssertionFailure() - << count << " parts were consumed parsing '" << input - << "' but expected 1."; - } - - if (memcmp(lv.language, lang, std::min(strlen(lang), sizeof(lv.language))) != - 0) { - return ::testing::AssertionFailure() - << "expected " << lang << " but got " - << std::string(lv.language, sizeof(lv.language)) << "."; - } - - return ::testing::AssertionSuccess(); -} - -static ::testing::AssertionResult TestLanguageRegion(const char* input, - const char* lang, - const char* region) { - std::vector<std::string> parts = util::SplitAndLowercase(input, '-'); - LocaleValue lv; - ssize_t count = lv.InitFromParts(std::begin(parts), std::end(parts)); - if (count < 0) { - return ::testing::AssertionFailure() << " failed to parse '" << input - << "'."; - } - - if (count != 2) { - return ::testing::AssertionFailure() - << count << " parts were consumed parsing '" << input - << "' but expected 2."; - } - - if (memcmp(lv.language, lang, std::min(strlen(lang), sizeof(lv.language))) != - 0) { - return ::testing::AssertionFailure() - << "expected " << input << " but got " - << std::string(lv.language, sizeof(lv.language)) << "."; - } - - if (memcmp(lv.region, region, std::min(strlen(region), sizeof(lv.region))) != - 0) { - return ::testing::AssertionFailure() - << "expected " << region << " but got " - << std::string(lv.region, sizeof(lv.region)) << "."; - } - - return ::testing::AssertionSuccess(); -} - -TEST(ConfigDescriptionTest, ParseLanguage) { - EXPECT_TRUE(TestLanguage("en", "en")); - EXPECT_TRUE(TestLanguage("fr", "fr")); - EXPECT_FALSE(TestLanguage("land", "")); - EXPECT_TRUE(TestLanguage("fr-land", "fr")); - - EXPECT_TRUE(TestLanguageRegion("fr-rCA", "fr", "CA")); -} - -} // namespace aapt diff --git a/tools/aapt2/OWNERS b/tools/aapt2/OWNERS index 23ec5ab0d1f3..f1903a5a54a7 100644 --- a/tools/aapt2/OWNERS +++ b/tools/aapt2/OWNERS @@ -1,2 +1,3 @@ set noparent toddke@google.com +rtmitchell@google.com
\ No newline at end of file diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h index 6fcf0f6d19cd..a8ba202d08f7 100644 --- a/tools/aapt2/Resource.h +++ b/tools/aapt2/Resource.h @@ -24,10 +24,10 @@ #include <tuple> #include <vector> +#include "androidfw/ConfigDescription.h" #include "androidfw/StringPiece.h" #include "utils/JenkinsHash.h" -#include "ConfigDescription.h" #include "Source.h" namespace aapt { @@ -108,7 +108,7 @@ struct ResourceNameRef { ResourceNameRef() = default; ResourceNameRef(const ResourceNameRef&) = default; ResourceNameRef(ResourceNameRef&&) = default; - ResourceNameRef(const ResourceName& rhs); // NOLINT(implicit) + ResourceNameRef(const ResourceName& rhs); // NOLINT(google-explicit-constructor) ResourceNameRef(const android::StringPiece& p, ResourceType t, const android::StringPiece& e); ResourceNameRef& operator=(const ResourceNameRef& rhs) = default; ResourceNameRef& operator=(ResourceNameRef&& rhs) = default; @@ -139,7 +139,7 @@ struct ResourceId { ResourceId(); ResourceId(const ResourceId& rhs); - ResourceId(uint32_t res_id); // NOLINT(implicit) + ResourceId(uint32_t res_id); // NOLINT(google-explicit-constructor) ResourceId(uint8_t p, uint8_t t, uint16_t e); bool is_valid() const; @@ -171,7 +171,7 @@ struct ResourceFile { ResourceName name; // Configuration - ConfigDescription config; + android::ConfigDescription config; // Type Type type; @@ -189,7 +189,7 @@ struct ResourceFile { */ struct ResourceKey { ResourceName name; - ConfigDescription config; + android::ConfigDescription config; }; bool operator<(const ResourceKey& a, const ResourceKey& b); @@ -201,16 +201,16 @@ bool operator<(const ResourceKey& a, const ResourceKey& b); */ struct ResourceKeyRef { ResourceNameRef name; - ConfigDescription config; + android::ConfigDescription config; ResourceKeyRef() = default; - ResourceKeyRef(const ResourceNameRef& n, const ConfigDescription& c) + ResourceKeyRef(const ResourceNameRef& n, const android::ConfigDescription& c) : name(n), config(c) {} /** * Prevent taking a reference to a temporary. This is bad. */ - ResourceKeyRef(ResourceName&& n, const ConfigDescription& c) = delete; + ResourceKeyRef(ResourceName&& n, const android::ConfigDescription& c) = delete; }; bool operator<(const ResourceKeyRef& a, const ResourceKeyRef& b); diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 7f48544c0ae4..968376b8229b 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -34,6 +34,7 @@ using ::aapt::ResourceUtils::StringBuilder; using ::aapt::text::Utf8Iterator; +using ::android::ConfigDescription; using ::android::StringPiece; namespace aapt { @@ -77,7 +78,7 @@ static uint32_t ParseFormatType(const StringPiece& piece) { static uint32_t ParseFormatAttribute(const StringPiece& str) { uint32_t mask = 0; - for (StringPiece part : util::Tokenize(str, '|')) { + for (const StringPiece& part : util::Tokenize(str, '|')) { StringPiece trimmed_part = util::TrimWhitespace(part); uint32_t type = ParseFormatType(trimmed_part); if (type == 0) { diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h index fb9dbd0cd0fd..6cc7b76c1ad1 100644 --- a/tools/aapt2/ResourceParser.h +++ b/tools/aapt2/ResourceParser.h @@ -20,9 +20,9 @@ #include <memory> #include "android-base/macros.h" +#include "androidfw/ConfigDescription.h" #include "androidfw/StringPiece.h" -#include "ConfigDescription.h" #include "Diagnostics.h" #include "ResourceTable.h" #include "ResourceValues.h" @@ -53,7 +53,7 @@ struct ResourceParserOptions { class ResourceParser { public: ResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source, - const ConfigDescription& config, + const android::ConfigDescription& config, const ResourceParserOptions& options = {}); bool Parse(xml::XmlPullParser* parser); @@ -110,7 +110,7 @@ class ResourceParser { IDiagnostics* diag_; ResourceTable* table_; Source source_; - ConfigDescription config_; + android::ConfigDescription config_; ResourceParserOptions options_; }; diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp index 41b4041efb7a..9de43c02fdca 100644 --- a/tools/aapt2/ResourceParser_test.cpp +++ b/tools/aapt2/ResourceParser_test.cpp @@ -29,6 +29,7 @@ using ::aapt::io::StringInputStream; using ::aapt::test::StrValueEq; using ::aapt::test::ValueEq; +using ::android::ConfigDescription; using ::android::Res_value; using ::android::ResTable_map; using ::android::StringPiece; diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index d0faac30425a..c2274d04cc8c 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -23,9 +23,9 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" +#include "androidfw/ConfigDescription.h" #include "androidfw/ResourceTypes.h" -#include "ConfigDescription.h" #include "NameMangler.h" #include "ResourceValues.h" #include "ValueVisitor.h" @@ -33,6 +33,7 @@ #include "util/Util.h" using ::aapt::text::IsValidResourceEntryName; +using ::android::ConfigDescription; using ::android::StringPiece; using ::android::base::StringPrintf; diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h index 8534eaaf0366..7b19a3188a76 100644 --- a/tools/aapt2/ResourceTable.h +++ b/tools/aapt2/ResourceTable.h @@ -17,7 +17,6 @@ #ifndef AAPT_RESOURCE_TABLE_H #define AAPT_RESOURCE_TABLE_H -#include "ConfigDescription.h" #include "Diagnostics.h" #include "Resource.h" #include "ResourceValues.h" @@ -26,6 +25,7 @@ #include "io/File.h" #include "android-base/macros.h" +#include "androidfw/ConfigDescription.h" #include "androidfw/StringPiece.h" #include <functional> @@ -66,7 +66,7 @@ struct Overlayable { class ResourceConfigValue { public: // The configuration for which this value is defined. - const ConfigDescription config; + const android::ConfigDescription config; // The product for which this value is defined. const std::string product; @@ -74,7 +74,7 @@ class ResourceConfigValue { // The actual Value. std::unique_ptr<Value> value; - ResourceConfigValue(const ConfigDescription& config, const android::StringPiece& product) + ResourceConfigValue(const android::ConfigDescription& config, const android::StringPiece& product) : config(config), product(product.to_string()) {} private: @@ -103,14 +103,14 @@ class ResourceEntry { explicit ResourceEntry(const android::StringPiece& name) : name(name.to_string()) {} - ResourceConfigValue* FindValue(const ConfigDescription& config); + ResourceConfigValue* FindValue(const android::ConfigDescription& config); - ResourceConfigValue* FindValue(const ConfigDescription& config, + ResourceConfigValue* FindValue(const android::ConfigDescription& config, const android::StringPiece& product); - ResourceConfigValue* FindOrCreateValue(const ConfigDescription& config, + ResourceConfigValue* FindOrCreateValue(const android::ConfigDescription& config, const android::StringPiece& product); - std::vector<ResourceConfigValue*> FindAllValues(const ConfigDescription& config); + std::vector<ResourceConfigValue*> FindAllValues(const android::ConfigDescription& config); template <typename Func> std::vector<ResourceConfigValue*> FindValuesIf(Func f) { @@ -182,29 +182,30 @@ class ResourceTable { // When a collision of resources occurs, this method decides which value to keep. static CollisionResult ResolveValueCollision(Value* existing, Value* incoming); - bool AddResource(const ResourceNameRef& name, const ConfigDescription& config, + bool AddResource(const ResourceNameRef& name, const android::ConfigDescription& config, const android::StringPiece& product, std::unique_ptr<Value> value, IDiagnostics* diag); bool AddResourceWithId(const ResourceNameRef& name, const ResourceId& res_id, - const ConfigDescription& config, const android::StringPiece& product, - std::unique_ptr<Value> value, IDiagnostics* diag); + const android::ConfigDescription& config, + const android::StringPiece& product, std::unique_ptr<Value> value, + IDiagnostics* diag); - bool AddFileReference(const ResourceNameRef& name, const ConfigDescription& config, + bool AddFileReference(const ResourceNameRef& name, const android::ConfigDescription& config, const Source& source, const android::StringPiece& path, IDiagnostics* diag); - bool AddFileReferenceMangled(const ResourceNameRef& name, const ConfigDescription& config, + bool AddFileReferenceMangled(const ResourceNameRef& name, const android::ConfigDescription& config, const Source& source, const android::StringPiece& path, io::IFile* file, IDiagnostics* diag); // Same as AddResource, but doesn't verify the validity of the name. This is used // when loading resources from an existing binary resource table that may have mangled names. - bool AddResourceMangled(const ResourceNameRef& name, const ConfigDescription& config, + bool AddResourceMangled(const ResourceNameRef& name, const android::ConfigDescription& config, const android::StringPiece& product, std::unique_ptr<Value> value, IDiagnostics* diag); bool AddResourceWithIdMangled(const ResourceNameRef& name, const ResourceId& id, - const ConfigDescription& config, + const android::ConfigDescription& config, const android::StringPiece& product, std::unique_ptr<Value> value, IDiagnostics* diag); @@ -277,11 +278,12 @@ class ResourceTable { IDiagnostics* diag); bool AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id, - const ConfigDescription& config, const android::StringPiece& product, - std::unique_ptr<Value> value, NameValidator name_validator, - const CollisionResolverFunc& conflict_resolver, IDiagnostics* diag); + const android::ConfigDescription& config, + const android::StringPiece& product, std::unique_ptr<Value> value, + NameValidator name_validator, const CollisionResolverFunc& conflict_resolver, + IDiagnostics* diag); - bool AddFileReferenceImpl(const ResourceNameRef& name, const ConfigDescription& config, + bool AddFileReferenceImpl(const ResourceNameRef& name, const android::ConfigDescription& config, const Source& source, const android::StringPiece& path, io::IFile* file, NameValidator name_validator, IDiagnostics* diag); diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp index 7fa8ea2f7f94..1a1f73fa4bb9 100644 --- a/tools/aapt2/ResourceTable_test.cpp +++ b/tools/aapt2/ResourceTable_test.cpp @@ -24,6 +24,7 @@ #include <ostream> #include <string> +using ::android::ConfigDescription; using ::android::StringPiece; using ::testing::Eq; using ::testing::NotNull; diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index 560077cc322c..99420de47fca 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -31,6 +31,7 @@ #include "util/Util.h" using ::aapt::text::Utf8Iterator; +using ::android::ConfigDescription; using ::android::StringPiece; using ::android::StringPiece16; using ::android::base::StringPrintf; @@ -359,7 +360,7 @@ std::unique_ptr<BinaryPrimitive> TryParseFlagSymbol(const Attribute* flag_attr, return util::make_unique<BinaryPrimitive>(flags); } - for (StringPiece part : util::Tokenize(str, '|')) { + for (const StringPiece& part : util::Tokenize(str, '|')) { StringPiece trimmed_part = util::TrimWhitespace(part); bool flag_set = false; diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h index 7af2fe06b908..e2f1c89bfd4c 100644 --- a/tools/aapt2/ResourceUtils.h +++ b/tools/aapt2/ResourceUtils.h @@ -20,6 +20,7 @@ #include <functional> #include <memory> +#include "androidfw/ConfigDescription.h" #include "androidfw/ResourceTypes.h" #include "androidfw/StringPiece.h" @@ -219,7 +220,8 @@ std::string BuildResourceFileName(const ResourceFile& res_file, // Parses the binary form of a resource value. `type` is used as a hint to know when a value is // an ID versus a False boolean value, etc. `config` is for sorting strings in the string pool. -std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const ConfigDescription& config, +std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, + const android::ConfigDescription& config, const android::ResStringPool& src_pool, const android::Res_value& res_value, StringPool* dst_pool); diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h index f5b464de2ea5..1006ca970dc5 100644 --- a/tools/aapt2/StringPool.h +++ b/tools/aapt2/StringPool.h @@ -24,9 +24,9 @@ #include <vector> #include "android-base/macros.h" +#include "androidfw/ConfigDescription.h" #include "androidfw/StringPiece.h" -#include "ConfigDescription.h" #include "Diagnostics.h" #include "util/BigBuffer.h" @@ -60,12 +60,12 @@ class StringPool { kLowPriority = 0xffffffffu, }; uint32_t priority = kNormalPriority; - ConfigDescription config; + android::ConfigDescription config; Context() = default; - Context(uint32_t p, const ConfigDescription& c) : priority(p), config(c) {} + Context(uint32_t p, const android::ConfigDescription& c) : priority(p), config(c) {} explicit Context(uint32_t p) : priority(p) {} - explicit Context(const ConfigDescription& c) : priority(kNormalPriority), config(c) { + explicit Context(const android::ConfigDescription& c) : priority(kNormalPriority), config(c) { } }; diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp index ab8a4b77a89d..7144985e80dc 100644 --- a/tools/aapt2/cmd/Compile.cpp +++ b/tools/aapt2/cmd/Compile.cpp @@ -21,11 +21,11 @@ #include "android-base/errors.h" #include "android-base/file.h" #include "android-base/utf8.h" +#include "androidfw/ConfigDescription.h" #include "androidfw/StringPiece.h" #include "google/protobuf/io/coded_stream.h" #include "google/protobuf/io/zero_copy_stream_impl_lite.h" -#include "ConfigDescription.h" #include "Diagnostics.h" #include "Flags.h" #include "ResourceParser.h" @@ -50,6 +50,7 @@ using ::aapt::io::FileInputStream; using ::aapt::text::Printer; +using ::android::ConfigDescription; using ::android::StringPiece; using ::android::base::SystemErrorCodeToString; using ::google::protobuf::io::CopyingOutputStreamAdaptor; @@ -485,7 +486,7 @@ static bool CompileXml(IAaptContext* context, const CompileOptions& options, } Printer r_txt_printer(&fout_text); - for (const auto res : xmlres->file.exported_symbols) { + for (const auto& res : xmlres->file.exported_symbols) { r_txt_printer.Print("default int id "); r_txt_printer.Println(res.name.entry); } @@ -644,7 +645,7 @@ static bool CompileFile(IAaptContext* context, const CompileOptions& options, class CompileContext : public IAaptContext { public: - CompileContext(IDiagnostics* diagnostics) : diagnostics_(diagnostics) { + explicit CompileContext(IDiagnostics* diagnostics) : diagnostics_(diagnostics) { } PackageType GetPackageType() override { diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index db42e7cb3e02..623ba120903d 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -24,13 +24,13 @@ #include "android-base/errors.h" #include "android-base/file.h" #include "android-base/stringprintf.h" +#include "androidfw/Locale.h" #include "androidfw/StringPiece.h" #include "AppInfo.h" #include "Debug.h" #include "Flags.h" #include "LoadedApk.h" -#include "Locale.h" #include "NameMangler.h" #include "ResourceUtils.h" #include "ResourceValues.h" @@ -68,6 +68,7 @@ #include "xml/XmlDom.h" using ::aapt::io::FileInputStream; +using ::android::ConfigDescription; using ::android::StringPiece; using ::android::base::StringPrintf; @@ -105,6 +106,7 @@ struct LinkOptions { bool no_version_vectors = false; bool no_version_transitions = false; bool no_resource_deduping = false; + bool no_resource_removal = false; bool no_xml_namespaces = false; bool do_not_compress_anything = false; std::unordered_set<std::string> extensions_to_not_compress; @@ -139,7 +141,7 @@ struct LinkOptions { class LinkContext : public IAaptContext { public: - LinkContext(IDiagnostics* diagnostics) + explicit LinkContext(IDiagnostics* diagnostics) : diagnostics_(diagnostics), name_mangler_({}), symbols_(&name_mangler_) { } @@ -224,7 +226,7 @@ class LinkContext : public IAaptContext { // See b/37498913. class FeatureSplitSymbolTableDelegate : public DefaultSymbolTableDelegate { public: - FeatureSplitSymbolTableDelegate(IAaptContext* context) : context_(context) { + explicit FeatureSplitSymbolTableDelegate(IAaptContext* context) : context_(context) { } virtual ~FeatureSplitSymbolTableDelegate() = default; @@ -1806,10 +1808,12 @@ class LinkCommand { // Before we process anything, remove the resources whose default values don't exist. // We want to force any references to these to fail the build. - if (!NoDefaultResourceRemover{}.Consume(context_, &final_table_)) { - context_->GetDiagnostics()->Error(DiagMessage() - << "failed removing resources with no defaults"); - return 1; + if (!options_.no_resource_removal) { + if (!NoDefaultResourceRemover{}.Consume(context_, &final_table_)) { + context_->GetDiagnostics()->Error(DiagMessage() + << "failed removing resources with no defaults"); + return 1; + } } ReferenceLinker linker; @@ -2084,6 +2088,10 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { "Disables automatic deduping of resources with\n" "identical values across compatible configurations.", &options.no_resource_deduping) + .OptionalSwitch("--no-resource-removal", + "Disables automatic removal of resources without defaults. Use this only\n" + "when building runtime resource overlay packages.", + &options.no_resource_removal) .OptionalSwitch("--enable-sparse-encoding", "Enables encoding sparse entries using a binary search tree.\n" "This decreases APK size at the cost of resource retrieval performance.", diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp index 9c76119f9504..45297a7f8997 100644 --- a/tools/aapt2/cmd/Optimize.cpp +++ b/tools/aapt2/cmd/Optimize.cpp @@ -20,6 +20,7 @@ #include "android-base/file.h" #include "android-base/stringprintf.h" +#include "androidfw/ConfigDescription.h" #include "androidfw/ResourceTypes.h" #include "androidfw/StringPiece.h" @@ -45,6 +46,7 @@ using ::aapt::configuration::Abi; using ::aapt::configuration::OutputArtifact; +using ::android::ConfigDescription; using ::android::ResTable_config; using ::android::StringPiece; using ::android::base::ReadFileToString; diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp index 8b3a6701b409..25010c52235c 100644 --- a/tools/aapt2/cmd/Util.cpp +++ b/tools/aapt2/cmd/Util.cpp @@ -19,15 +19,17 @@ #include <vector> #include "android-base/logging.h" +#include "androidfw/ConfigDescription.h" +#include "androidfw/Locale.h" -#include "ConfigDescription.h" -#include "Locale.h" #include "ResourceUtils.h" #include "ValueVisitor.h" #include "split/TableSplitter.h" #include "util/Maybe.h" #include "util/Util.h" +using ::android::ConfigDescription; +using ::android::LocaleValue; using ::android::StringPiece; namespace aapt { diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp index 36c24bc4a0fd..c5de9e058907 100644 --- a/tools/aapt2/compile/PseudolocaleGenerator.cpp +++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp @@ -24,6 +24,7 @@ #include "compile/Pseudolocalizer.h" #include "util/Util.h" +using ::android::ConfigDescription; using ::android::StringPiece; using ::android::StringPiece16; diff --git a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp index 711558aa51c1..31358020ab60 100644 --- a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp +++ b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp @@ -19,6 +19,8 @@ #include "test/Test.h" #include "util/Util.h" +using ::android::ConfigDescription; + namespace aapt { TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) { diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp index 902334b98d00..dd06b38f6c01 100644 --- a/tools/aapt2/configuration/ConfigurationParser.cpp +++ b/tools/aapt2/configuration/ConfigurationParser.cpp @@ -25,8 +25,8 @@ #include "android-base/file.h" #include "android-base/logging.h" +#include "androidfw/ConfigDescription.h" -#include "ConfigDescription.h" #include "Diagnostics.h" #include "ResourceUtils.h" #include "configuration/ConfigurationParser.internal.h" @@ -40,6 +40,8 @@ #include "xml/XmlDom.h" #include "xml/XmlUtil.h" +using ::android::ConfigDescription; + namespace aapt { namespace { diff --git a/tools/aapt2/configuration/ConfigurationParser.h b/tools/aapt2/configuration/ConfigurationParser.h index 7f1d4453f9b3..b9e3be9393c9 100644 --- a/tools/aapt2/configuration/ConfigurationParser.h +++ b/tools/aapt2/configuration/ConfigurationParser.h @@ -22,7 +22,8 @@ #include <unordered_map> #include <vector> -#include "ConfigDescription.h" +#include "androidfw/ConfigDescription.h" + #include "Diagnostics.h" #include "util/Maybe.h" @@ -109,8 +110,8 @@ struct OutputArtifact { std::string name; int version; std::vector<Abi> abis; - std::vector<ConfigDescription> screen_densities; - std::vector<ConfigDescription> locales; + std::vector<android::ConfigDescription> screen_densities; + std::vector<android::ConfigDescription> locales; Maybe<AndroidSdk> android_sdk; std::vector<DeviceFeature> features; std::vector<GlTexture> textures; diff --git a/tools/aapt2/configuration/ConfigurationParser.internal.h b/tools/aapt2/configuration/ConfigurationParser.internal.h index f071a69fc9e3..c541688bc018 100644 --- a/tools/aapt2/configuration/ConfigurationParser.internal.h +++ b/tools/aapt2/configuration/ConfigurationParser.internal.h @@ -17,6 +17,8 @@ #ifndef AAPT2_CONFIGURATIONPARSER_INTERNAL_H #define AAPT2_CONFIGURATIONPARSER_INTERNAL_H +#include "androidfw/ConfigDescription.h" + #include "configuration/ConfigurationParser.h" #include <algorithm> @@ -148,8 +150,8 @@ struct PostProcessingConfiguration { Maybe<std::string> artifact_format; Group<Abi> abi_groups; - Group<ConfigDescription> screen_density_groups; - Group<ConfigDescription> locale_groups; + Group<android::ConfigDescription> screen_density_groups; + Group<android::ConfigDescription> locale_groups; Group<DeviceFeature> device_feature_groups; Group<GlTexture> gl_texture_groups; Entry<AndroidSdk> android_sdks; diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp index febbb2ed11a0..4c23bd3d6e3e 100644 --- a/tools/aapt2/configuration/ConfigurationParser_test.cpp +++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp @@ -26,6 +26,8 @@ #include "test/Test.h" #include "xml/XmlDom.h" +using ::android::ConfigDescription; + namespace aapt { namespace configuration { @@ -228,7 +230,7 @@ TEST_F(ConfigurationParserTest, ValidateFile) { test::ParseConfigOrDie("fr"), test::ParseConfigOrDie("de"))); ASSERT_TRUE(a1.android_sdk); ASSERT_TRUE(a1.android_sdk.value().min_sdk_version); - EXPECT_EQ(a1.android_sdk.value().min_sdk_version, 19l); + EXPECT_EQ(a1.android_sdk.value().min_sdk_version, 19L); EXPECT_THAT(a1.textures, SizeIs(1ul)); EXPECT_THAT(a1.features, SizeIs(1ul)); @@ -248,7 +250,7 @@ TEST_F(ConfigurationParserTest, ValidateFile) { test::ParseConfigOrDie("fr-rCA"))); ASSERT_TRUE(a2.android_sdk); ASSERT_TRUE(a2.android_sdk.value().min_sdk_version); - EXPECT_EQ(a2.android_sdk.value().min_sdk_version, 19l); + EXPECT_EQ(a2.android_sdk.value().min_sdk_version, 19L); EXPECT_THAT(a2.textures, SizeIs(1ul)); EXPECT_THAT(a2.features, SizeIs(1ul)); } diff --git a/tools/aapt2/filter/ConfigFilter.cpp b/tools/aapt2/filter/ConfigFilter.cpp index 5fbe77e98144..9d10d595882a 100644 --- a/tools/aapt2/filter/ConfigFilter.cpp +++ b/tools/aapt2/filter/ConfigFilter.cpp @@ -16,9 +16,10 @@ #include "filter/ConfigFilter.h" +#include "androidfw/ConfigDescription.h" #include "androidfw/ResourceTypes.h" -#include "ConfigDescription.h" +using ::android::ConfigDescription; namespace aapt { diff --git a/tools/aapt2/filter/ConfigFilter.h b/tools/aapt2/filter/ConfigFilter.h index ebb81519dceb..c4b7e4356df9 100644 --- a/tools/aapt2/filter/ConfigFilter.h +++ b/tools/aapt2/filter/ConfigFilter.h @@ -20,7 +20,7 @@ #include <set> #include <utility> -#include "ConfigDescription.h" +#include "androidfw/ConfigDescription.h" namespace aapt { @@ -34,7 +34,7 @@ class IConfigFilter { /** * Returns true if the filter matches the configuration, false otherwise. */ - virtual bool Match(const ConfigDescription& config) const = 0; + virtual bool Match(const android::ConfigDescription& config) const = 0; }; /** @@ -46,12 +46,12 @@ class IConfigFilter { */ class AxisConfigFilter : public IConfigFilter { public: - void AddConfig(ConfigDescription config); + void AddConfig(android::ConfigDescription config); - bool Match(const ConfigDescription& config) const override; + bool Match(const android::ConfigDescription& config) const override; private: - std::set<std::pair<ConfigDescription, uint32_t>> configs_; + std::set<std::pair<android::ConfigDescription, uint32_t>> configs_; uint32_t config_mask_ = 0; }; diff --git a/tools/aapt2/format/binary/BinaryResourceParser.h b/tools/aapt2/format/binary/BinaryResourceParser.h index a1f9f83edfb6..2bdc051f4e29 100644 --- a/tools/aapt2/format/binary/BinaryResourceParser.h +++ b/tools/aapt2/format/binary/BinaryResourceParser.h @@ -20,6 +20,7 @@ #include <string> #include "android-base/macros.h" +#include "androidfw/ConfigDescription.h" #include "androidfw/ResourceTypes.h" #include "ResourceTable.h" @@ -54,22 +55,28 @@ class BinaryResourceParser { bool ParseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk); bool ParseLibrary(const android::ResChunk_header* chunk); - std::unique_ptr<Item> ParseValue(const ResourceNameRef& name, const ConfigDescription& config, + std::unique_ptr<Item> ParseValue(const ResourceNameRef& name, + const android::ConfigDescription& config, const android::Res_value& value); - std::unique_ptr<Value> ParseMapEntry(const ResourceNameRef& name, const ConfigDescription& config, + std::unique_ptr<Value> ParseMapEntry(const ResourceNameRef& name, + const android::ConfigDescription& config, const android::ResTable_map_entry* map); - std::unique_ptr<Style> ParseStyle(const ResourceNameRef& name, const ConfigDescription& config, + std::unique_ptr<Style> ParseStyle(const ResourceNameRef& name, + const android::ConfigDescription& config, const android::ResTable_map_entry* map); - std::unique_ptr<Attribute> ParseAttr(const ResourceNameRef& name, const ConfigDescription& config, + std::unique_ptr<Attribute> ParseAttr(const ResourceNameRef& name, + const android::ConfigDescription& config, const android::ResTable_map_entry* map); - std::unique_ptr<Array> ParseArray(const ResourceNameRef& name, const ConfigDescription& config, + std::unique_ptr<Array> ParseArray(const ResourceNameRef& name, + const android::ConfigDescription& config, const android::ResTable_map_entry* map); - std::unique_ptr<Plural> ParsePlural(const ResourceNameRef& name, const ConfigDescription& config, + std::unique_ptr<Plural> ParsePlural(const ResourceNameRef& name, + const android::ConfigDescription& config, const android::ResTable_map_entry* map); /** diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp index 3b101b7152be..d1b2fdb84afc 100644 --- a/tools/aapt2/format/proto/ProtoDeserialize.cpp +++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp @@ -19,13 +19,15 @@ #include "android-base/logging.h" #include "android-base/macros.h" #include "androidfw/ResourceTypes.h" +#include "androidfw/Locale.h" -#include "Locale.h" #include "ResourceTable.h" #include "ResourceUtils.h" #include "ResourceValues.h" #include "ValueVisitor.h" +using ::android::ConfigDescription; +using ::android::LocaleValue; using ::android::ResStringPool; namespace aapt { diff --git a/tools/aapt2/format/proto/ProtoDeserialize.h b/tools/aapt2/format/proto/ProtoDeserialize.h index 0c581a17fa2e..723a1c095a50 100644 --- a/tools/aapt2/format/proto/ProtoDeserialize.h +++ b/tools/aapt2/format/proto/ProtoDeserialize.h @@ -18,9 +18,9 @@ #define AAPT_FORMAT_PROTO_PROTODESERIALIZE_H #include "android-base/macros.h" +#include "androidfw/ConfigDescription.h" #include "androidfw/ResourceTypes.h" -#include "ConfigDescription.h" #include "Configuration.pb.h" #include "ResourceTable.h" #include "ResourceValues.h" @@ -34,14 +34,15 @@ namespace aapt { std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value, const android::ResStringPool& src_pool, - const ConfigDescription& config, + const android::ConfigDescription& config, StringPool* value_pool, io::IFileCollection* files, std::string* out_error); std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item, const android::ResStringPool& src_pool, - const ConfigDescription& config, StringPool* value_pool, - io::IFileCollection* files, std::string* out_error); + const android::ConfigDescription& config, + StringPool* value_pool, io::IFileCollection* files, + std::string* out_error); std::unique_ptr<xml::XmlResource> DeserializeXmlResourceFromPb(const pb::XmlNode& pb_node, std::string* out_error); @@ -49,8 +50,8 @@ std::unique_ptr<xml::XmlResource> DeserializeXmlResourceFromPb(const pb::XmlNode bool DeserializeXmlFromPb(const pb::XmlNode& pb_node, xml::Element* out_el, StringPool* value_pool, std::string* out_error); -bool DeserializeConfigFromPb(const pb::Configuration& pb_config, ConfigDescription* out_config, - std::string* out_error); +bool DeserializeConfigFromPb(const pb::Configuration& pb_config, + android::ConfigDescription* out_config, std::string* out_error); // Optional io::IFileCollection used to lookup references to files in the ResourceTable. bool DeserializeTableFromPb(const pb::ResourceTable& pb_table, io::IFileCollection* files, diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp index 411cc29630fe..7e35ea7bb7a3 100644 --- a/tools/aapt2/format/proto/ProtoSerialize.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize.cpp @@ -19,6 +19,8 @@ #include "ValueVisitor.h" #include "util/BigBuffer.h" +using android::ConfigDescription; + namespace aapt { void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool, IDiagnostics* diag) { diff --git a/tools/aapt2/format/proto/ProtoSerialize.h b/tools/aapt2/format/proto/ProtoSerialize.h index 951494ca8a0d..c40e5dd51da6 100644 --- a/tools/aapt2/format/proto/ProtoSerialize.h +++ b/tools/aapt2/format/proto/ProtoSerialize.h @@ -18,8 +18,8 @@ #define AAPT_FORMAT_PROTO_PROTOSERIALIZE_H #include "android-base/macros.h" +#include "androidfw/ConfigDescription.h" -#include "ConfigDescription.h" #include "Configuration.pb.h" #include "ResourceTable.h" #include "ResourceValues.h" @@ -49,7 +49,7 @@ void SerializeXmlResourceToPb(const xml::XmlResource& resource, pb::XmlNode* out void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool, IDiagnostics* diag); // Serializes a ConfigDescription into its protobuf representation. -void SerializeConfig(const ConfigDescription& config, pb::Configuration* out_pb_config); +void SerializeConfig(const android::ConfigDescription& config, pb::Configuration* out_pb_config); // Serializes a ResourceTable into its protobuf representation. void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table, IDiagnostics* diag); diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp index 21fdbd8a4c1e..a0d92f7b9308 100644 --- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp @@ -20,6 +20,7 @@ #include "format/proto/ProtoDeserialize.h" #include "test/Test.h" +using ::android::ConfigDescription; using ::android::StringPiece; using ::testing::Eq; using ::testing::IsEmpty; diff --git a/tools/aapt2/io/FileStream_test.cpp b/tools/aapt2/io/FileStream_test.cpp index 7872738320c3..cc9cd2808a0c 100644 --- a/tools/aapt2/io/FileStream_test.cpp +++ b/tools/aapt2/io/FileStream_test.cpp @@ -18,7 +18,6 @@ #include "android-base/file.h" #include "android-base/macros.h" -#include "android-base/test_utils.h" #include "test/Test.h" diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp index 8d91b0098c1f..a4610b2575b9 100644 --- a/tools/aapt2/java/AnnotationProcessor.cpp +++ b/tools/aapt2/java/AnnotationProcessor.cpp @@ -113,7 +113,7 @@ void AnnotationProcessor::AppendNewLine() { void AnnotationProcessor::Print(Printer* printer) const { if (has_comments_) { std::string result = comment_.str(); - for (StringPiece line : util::Tokenize(result, '\n')) { + for (const StringPiece& line : util::Tokenize(result, '\n')) { printer->Println(line); } printer->Println(" */"); diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h index 46827ee7cf93..c53f3076d85e 100644 --- a/tools/aapt2/java/ProguardRules.h +++ b/tools/aapt2/java/ProguardRules.h @@ -44,7 +44,7 @@ class KeepSet { public: KeepSet() = default; - KeepSet(bool conditional_keep_rules) : conditional_keep_rules_(conditional_keep_rules) { + explicit KeepSet(bool conditional_keep_rules) : conditional_keep_rules_(conditional_keep_rules) { } inline void AddManifestClass(const UsageLocation& file, const std::string& class_name) { diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp index 37d1a5fbaeb8..f774e3a1245a 100644 --- a/tools/aapt2/java/ProguardRules_test.cpp +++ b/tools/aapt2/java/ProguardRules_test.cpp @@ -21,6 +21,7 @@ #include "test/Test.h" using ::aapt::io::StringOutputStream; +using ::android::ConfigDescription; using ::testing::HasSubstr; using ::testing::Not; diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp index f80c6e9b34d3..960c7d46cc98 100644 --- a/tools/aapt2/link/AutoVersioner.cpp +++ b/tools/aapt2/link/AutoVersioner.cpp @@ -20,14 +20,16 @@ #include "android-base/logging.h" -#include "ConfigDescription.h" #include "ResourceTable.h" #include "SdkConstants.h" #include "ValueVisitor.h" +using android::ConfigDescription; + namespace aapt { -bool ShouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config, +bool ShouldGenerateVersionedResource(const ResourceEntry* entry, + const ConfigDescription& config, const ApiVersion sdk_version_to_generate) { // We assume the caller is trying to generate a version greater than the current configuration. CHECK(sdk_version_to_generate > config.sdkVersion); diff --git a/tools/aapt2/link/AutoVersioner_test.cpp b/tools/aapt2/link/AutoVersioner_test.cpp index 49639f8ad549..1117472f104b 100644 --- a/tools/aapt2/link/AutoVersioner_test.cpp +++ b/tools/aapt2/link/AutoVersioner_test.cpp @@ -16,9 +16,11 @@ #include "link/Linkers.h" -#include "ConfigDescription.h" +#include "androidfw/ConfigDescription.h" + #include "test/Test.h" +using ::android::ConfigDescription; using ::testing::NotNull; namespace aapt { diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h index 3c9c4767b3d1..c9b8d3993959 100644 --- a/tools/aapt2/link/Linkers.h +++ b/tools/aapt2/link/Linkers.h @@ -21,6 +21,7 @@ #include <unordered_set> #include "android-base/macros.h" +#include "androidfw/ConfigDescription.h" #include "androidfw/StringPiece.h" #include "Resource.h" @@ -32,7 +33,6 @@ namespace aapt { class ResourceTable; class ResourceEntry; -struct ConfigDescription; // Defines the context in which a resource value is defined. Most resources are defined with the // implicit package name of their compilation context. Understanding the package name of a resource @@ -43,12 +43,14 @@ struct CallSite { // Determines whether a versioned resource should be created. If a versioned resource already // exists, it takes precedence. -bool ShouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config, +bool ShouldGenerateVersionedResource(const ResourceEntry* entry, + const android::ConfigDescription& config, const ApiVersion sdk_version_to_generate); // Finds the next largest ApiVersion of the config which is identical to the given config except // for sdkVersion. -ApiVersion FindNextApiVersionForConfig(const ResourceEntry* entry, const ConfigDescription& config); +ApiVersion FindNextApiVersionForConfig(const ResourceEntry* entry, + const android::ConfigDescription& config); class AutoVersioner : public IResourceTableConsumer { public: diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index ee4e70288994..7180a9918abe 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -252,6 +252,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, xml::XmlNodeAction component_action; component_action.Action(RequiredNameIsJavaClassName); component_action["intent-filter"] = intent_filter_action; + component_action["preferred"] = intent_filter_action; component_action["meta-data"] = meta_data_action; // Manifest actions. @@ -346,6 +347,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, manifest_action["uses-permission"]; manifest_action["uses-permission-sdk-23"]; manifest_action["permission"]; + manifest_action["permission"]["meta-data"] = meta_data_action; manifest_action["permission-tree"]; manifest_action["permission-group"]; manifest_action["uses-configuration"]; @@ -355,6 +357,8 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, manifest_action["compatible-screens"]; manifest_action["compatible-screens"]["screen"]; manifest_action["supports-gl-texture"]; + manifest_action["restrict-update"]; + manifest_action["package-verifier"]; manifest_action["meta-data"] = meta_data_action; manifest_action["uses-split"].Action(RequiredNameIsJavaPackage); @@ -376,6 +380,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, uses_static_library_action.Action(RequiredNameIsJavaPackage); uses_static_library_action.Action(RequiredAndroidAttribute("version")); uses_static_library_action.Action(RequiredAndroidAttribute("certDigest")); + uses_static_library_action["additional-certificate"]; if (options_.debug_mode) { application_action.Action([&](xml::Element* el) -> bool { @@ -399,6 +404,8 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, application_action["provider"]["grant-uri-permission"]; application_action["provider"]["path-permission"]; + manifest_action["package"] = manifest_action; + return true; } diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp index 5bc004d62ff2..cf85726105df 100644 --- a/tools/aapt2/link/ManifestFixer_test.cpp +++ b/tools/aapt2/link/ManifestFixer_test.cpp @@ -350,133 +350,133 @@ TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) { } TEST_F(ManifestFixerTest, DontUseDefaultVersionNameAndCode) { -ManifestFixerOptions options; -options.version_name_default = std::string("Beta"); -options.version_code_default = std::string("0x10000000"); + ManifestFixerOptions options; + options.version_name_default = std::string("Beta"); + options.version_code_default = std::string("0x10000000"); -std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF( + std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" android:versionCode="0x20000000" android:versionName="Alpha" />)EOF", - options); -ASSERT_THAT(doc, NotNull()); + options); + ASSERT_THAT(doc, NotNull()); -xml::Element* manifest_el = doc->root.get(); -ASSERT_THAT(manifest_el, NotNull()); + xml::Element* manifest_el = doc->root.get(); + ASSERT_THAT(manifest_el, NotNull()); -xml::Attribute* attr = - manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName"); -ASSERT_THAT(attr, NotNull()); -EXPECT_THAT(attr->value, StrEq("Alpha")); + xml::Attribute* attr = + manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("Alpha")); -attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode"); -ASSERT_THAT(attr, NotNull()); -EXPECT_THAT(attr->value, StrEq("0x20000000")); + attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("0x20000000")); } TEST_F(ManifestFixerTest, ReplaceVersionNameAndCode) { -ManifestFixerOptions options; -options.replace_version = true; -options.version_name_default = std::string("Beta"); -options.version_code_default = std::string("0x10000000"); + ManifestFixerOptions options; + options.replace_version = true; + options.version_name_default = std::string("Beta"); + options.version_code_default = std::string("0x10000000"); -std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF( + std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" android:versionCode="0x20000000" android:versionName="Alpha" />)EOF", - options); -ASSERT_THAT(doc, NotNull()); + options); + ASSERT_THAT(doc, NotNull()); -xml::Element* manifest_el = doc->root.get(); -ASSERT_THAT(manifest_el, NotNull()); + xml::Element* manifest_el = doc->root.get(); + ASSERT_THAT(manifest_el, NotNull()); -xml::Attribute* attr = - manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName"); -ASSERT_THAT(attr, NotNull()); -EXPECT_THAT(attr->value, StrEq("Beta")); + xml::Attribute* attr = + manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("Beta")); -attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode"); -ASSERT_THAT(attr, NotNull()); -EXPECT_THAT(attr->value, StrEq("0x10000000")); + attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("0x10000000")); } TEST_F(ManifestFixerTest, ReplaceVersionName) { -ManifestFixerOptions options; -options.replace_version = true; -options.version_name_default = std::string("Beta"); + ManifestFixerOptions options; + options.replace_version = true; + options.version_name_default = std::string("Beta"); -std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF( + std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" android:versionCode="0x20000000" android:versionName="Alpha" />)EOF", - options); -ASSERT_THAT(doc, NotNull()); + options); + ASSERT_THAT(doc, NotNull()); -xml::Element* manifest_el = doc->root.get(); -ASSERT_THAT(manifest_el, NotNull()); + xml::Element* manifest_el = doc->root.get(); + ASSERT_THAT(manifest_el, NotNull()); -xml::Attribute* attr = - manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName"); -ASSERT_THAT(attr, NotNull()); -EXPECT_THAT(attr->value, StrEq("Beta")); + xml::Attribute* attr = + manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("Beta")); -attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode"); -ASSERT_THAT(attr, NotNull()); -EXPECT_THAT(attr->value, StrEq("0x20000000")); + attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("0x20000000")); } TEST_F(ManifestFixerTest, ReplaceVersionCode) { -ManifestFixerOptions options; -options.replace_version = true; -options.version_code_default = std::string("0x10000000"); + ManifestFixerOptions options; + options.replace_version = true; + options.version_code_default = std::string("0x10000000"); -std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF( + std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" android:versionCode="0x20000000" android:versionName="Alpha" />)EOF", - options); -ASSERT_THAT(doc, NotNull()); + options); + ASSERT_THAT(doc, NotNull()); -xml::Element* manifest_el = doc->root.get(); -ASSERT_THAT(manifest_el, NotNull()); + xml::Element* manifest_el = doc->root.get(); + ASSERT_THAT(manifest_el, NotNull()); -xml::Attribute* attr = - manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName"); -ASSERT_THAT(attr, NotNull()); -EXPECT_THAT(attr->value, StrEq("Alpha")); + xml::Attribute* attr = + manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("Alpha")); -attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode"); -ASSERT_THAT(attr, NotNull()); -EXPECT_THAT(attr->value, StrEq("0x10000000")); + attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("0x10000000")); } TEST_F(ManifestFixerTest, DontReplaceVersionNameOrCode) { -ManifestFixerOptions options; -options.replace_version = true; + ManifestFixerOptions options; + options.replace_version = true; -std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF( + std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" android:versionCode="0x20000000" android:versionName="Alpha" />)EOF", - options); -ASSERT_THAT(doc, NotNull()); + options); + ASSERT_THAT(doc, NotNull()); -xml::Element* manifest_el = doc->root.get(); -ASSERT_THAT(manifest_el, NotNull()); + xml::Element* manifest_el = doc->root.get(); + ASSERT_THAT(manifest_el, NotNull()); -xml::Attribute* attr = - manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName"); -ASSERT_THAT(attr, NotNull()); -EXPECT_THAT(attr->value, StrEq("Alpha")); + xml::Attribute* attr = + manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("Alpha")); -attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode"); -ASSERT_THAT(attr, NotNull()); -EXPECT_THAT(attr->value, StrEq("0x20000000")); + attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("0x20000000")); } TEST_F(ManifestFixerTest, EnsureManifestAttributesAreTyped) { @@ -673,7 +673,8 @@ TEST_F(ManifestFixerTest, UnexpectedElementsInManifest) { options.warn_validation = true; // Unexpected element should result in a warning if the flag is set to 'true'. - std::unique_ptr<xml::XmlResource> manifest = VerifyWithOptions(input, options); + std::unique_ptr<xml::XmlResource> manifest = + VerifyWithOptions(input, options); ASSERT_THAT(manifest, NotNull()); // Unexpected element should result in an error if the flag is set to 'false'. diff --git a/tools/aapt2/link/NoDefaultResourceRemover.cpp b/tools/aapt2/link/NoDefaultResourceRemover.cpp index cfb4b26aba02..13054bf14c78 100644 --- a/tools/aapt2/link/NoDefaultResourceRemover.cpp +++ b/tools/aapt2/link/NoDefaultResourceRemover.cpp @@ -14,12 +14,16 @@ * limitations under the License. */ +#include "androidfw/Locale.h" + #include "link/NoDefaultResourceRemover.h" #include <algorithm> #include "ResourceTable.h" +using android::ConfigDescription; + namespace aapt { static bool IsDefaultConfigRequired(const ConfigDescription& config) { diff --git a/tools/aapt2/link/ProductFilter_test.cpp b/tools/aapt2/link/ProductFilter_test.cpp index 86dd56ad8f5b..dd4767463711 100644 --- a/tools/aapt2/link/ProductFilter_test.cpp +++ b/tools/aapt2/link/ProductFilter_test.cpp @@ -18,6 +18,8 @@ #include "test/Test.h" +using ::android::ConfigDescription; + namespace aapt { TEST(ProductFilterTest, SelectTwoProducts) { diff --git a/tools/aapt2/link/XmlCompatVersioner.h b/tools/aapt2/link/XmlCompatVersioner.h index 099e23c9e479..998061806279 100644 --- a/tools/aapt2/link/XmlCompatVersioner.h +++ b/tools/aapt2/link/XmlCompatVersioner.h @@ -55,7 +55,7 @@ class XmlCompatVersioner { public: using Rules = std::unordered_map<ResourceId, std::unique_ptr<IDegradeRule>>; - XmlCompatVersioner(const Rules* rules); + explicit XmlCompatVersioner(const Rules* rules); std::vector<std::unique_ptr<xml::XmlResource>> Process(IAaptContext* context, xml::XmlResource* doc, @@ -83,7 +83,7 @@ struct ReplacementAttr { class DegradeToManyRule : public IDegradeRule { public: - DegradeToManyRule(std::vector<ReplacementAttr> attrs); + explicit DegradeToManyRule(std::vector<ReplacementAttr> attrs); virtual ~DegradeToManyRule() = default; std::vector<DegradeResult> Degrade(const xml::Element& src_el, const xml::Attribute& src_attr, diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp index 588b3316e6fa..aa9f9ab4798f 100644 --- a/tools/aapt2/optimize/MultiApkGenerator.cpp +++ b/tools/aapt2/optimize/MultiApkGenerator.cpp @@ -20,6 +20,7 @@ #include <regex> #include <string> +#include "androidfw/ConfigDescription.h" #include "androidfw/StringPiece.h" #include "LoadedApk.h" @@ -43,6 +44,7 @@ using ::aapt::configuration::AndroidSdk; using ::aapt::configuration::OutputArtifact; using ::aapt::xml::kSchemaAndroid; using ::aapt::xml::XmlResource; +using ::android::ConfigDescription; using ::android::StringPiece; /** diff --git a/tools/aapt2/optimize/MultiApkGenerator.h b/tools/aapt2/optimize/MultiApkGenerator.h index c8588791662a..4a5a6c3d5915 100644 --- a/tools/aapt2/optimize/MultiApkGenerator.h +++ b/tools/aapt2/optimize/MultiApkGenerator.h @@ -22,6 +22,8 @@ #include <unordered_set> #include <vector> +#include "androidfw/ConfigDescription.h" + #include "Diagnostics.h" #include "LoadedApk.h" #include "configuration/ConfigurationParser.h" @@ -66,7 +68,7 @@ class MultiApkGenerator { /** * Adds the <screen> elements to the parent node for the provided density configuration. */ - void AddScreens(const ConfigDescription& config, xml::Element* parent); + void AddScreens(const android::ConfigDescription& config, xml::Element* parent); LoadedApk* apk_; IAaptContext* context_; diff --git a/tools/aapt2/optimize/MultiApkGenerator_test.cpp b/tools/aapt2/optimize/MultiApkGenerator_test.cpp index 80eb737fa682..7d87eb8831e8 100644 --- a/tools/aapt2/optimize/MultiApkGenerator_test.cpp +++ b/tools/aapt2/optimize/MultiApkGenerator_test.cpp @@ -31,6 +31,8 @@ #include "test/Context.h" #include "test/Test.h" +using ::android::ConfigDescription; + namespace aapt { namespace { diff --git a/tools/aapt2/optimize/ResourceDeduper.cpp b/tools/aapt2/optimize/ResourceDeduper.cpp index 9d16268a276e..ee2dfbce7c06 100644 --- a/tools/aapt2/optimize/ResourceDeduper.cpp +++ b/tools/aapt2/optimize/ResourceDeduper.cpp @@ -21,6 +21,8 @@ #include "DominatorTree.h" #include "ResourceTable.h" +using android::ConfigDescription; + namespace aapt { namespace { diff --git a/tools/aapt2/optimize/ResourceDeduper_test.cpp b/tools/aapt2/optimize/ResourceDeduper_test.cpp index d9f384c095ca..2e098aec4f8d 100644 --- a/tools/aapt2/optimize/ResourceDeduper_test.cpp +++ b/tools/aapt2/optimize/ResourceDeduper_test.cpp @@ -20,6 +20,7 @@ #include "test/Test.h" using ::aapt::test::HasValue; +using ::android::ConfigDescription; using ::testing::Not; namespace aapt { diff --git a/tools/aapt2/optimize/VersionCollapser.cpp b/tools/aapt2/optimize/VersionCollapser.cpp index cc1fc1e6910b..f9856047ac40 100644 --- a/tools/aapt2/optimize/VersionCollapser.cpp +++ b/tools/aapt2/optimize/VersionCollapser.cpp @@ -21,6 +21,8 @@ #include "ResourceTable.h" +using android::ConfigDescription; + namespace aapt { template <typename Iterator, typename Pred> diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp index 70efbf5d17b2..a844a43698e5 100644 --- a/tools/aapt2/process/SymbolTable.cpp +++ b/tools/aapt2/process/SymbolTable.cpp @@ -21,15 +21,16 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" #include "androidfw/AssetManager.h" +#include "androidfw/ConfigDescription.h" #include "androidfw/ResourceTypes.h" -#include "ConfigDescription.h" #include "NameMangler.h" #include "Resource.h" #include "ResourceUtils.h" #include "ValueVisitor.h" #include "util/Util.h" +using ::android::ConfigDescription; using ::android::StringPiece; using ::android::StringPiece16; diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h index 51a2e373596a..2d8bd02a3799 100644 --- a/tools/aapt2/process/SymbolTable.h +++ b/tools/aapt2/process/SymbolTable.h @@ -71,7 +71,7 @@ class SymbolTable { bool is_dynamic = false; }; - SymbolTable(NameMangler* mangler); + explicit SymbolTable(NameMangler* mangler); // Overrides the default ISymbolTableDelegate, which allows a custom defined strategy for // looking up resources from a set of sources. diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp index e99174302d26..1dd5502202b2 100644 --- a/tools/aapt2/split/TableSplitter.cpp +++ b/tools/aapt2/split/TableSplitter.cpp @@ -24,11 +24,13 @@ #include <vector> #include "android-base/logging.h" +#include "androidfw/ConfigDescription.h" -#include "ConfigDescription.h" #include "ResourceTable.h" #include "util/Util.h" +using ::android::ConfigDescription; + namespace aapt { using ConfigClaimedMap = std::unordered_map<ResourceConfigValue*, bool>; diff --git a/tools/aapt2/split/TableSplitter.h b/tools/aapt2/split/TableSplitter.h index 6aec2576261e..91afaa37e2d4 100644 --- a/tools/aapt2/split/TableSplitter.h +++ b/tools/aapt2/split/TableSplitter.h @@ -20,8 +20,8 @@ #include <set> #include <vector> #include "android-base/macros.h" +#include "androidfw/ConfigDescription.h" -#include "ConfigDescription.h" #include "ResourceTable.h" #include "filter/ConfigFilter.h" #include "process/IResourceTableConsumer.h" @@ -29,7 +29,7 @@ namespace aapt { struct SplitConstraints { - std::set<ConfigDescription> configs; + std::set<android::ConfigDescription> configs; }; struct TableSplitterOptions { diff --git a/tools/aapt2/split/TableSplitter_test.cpp b/tools/aapt2/split/TableSplitter_test.cpp index d52f4b446b8f..cdf07386c70f 100644 --- a/tools/aapt2/split/TableSplitter_test.cpp +++ b/tools/aapt2/split/TableSplitter_test.cpp @@ -18,6 +18,8 @@ #include "test/Test.h" +using ::android::ConfigDescription; + namespace aapt { TEST(TableSplitterTest, NoSplitPreferredDensity) { diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp index c4eab1269e4e..f33ae3155192 100644 --- a/tools/aapt2/test/Builders.cpp +++ b/tools/aapt2/test/Builders.cpp @@ -28,6 +28,7 @@ using ::aapt::configuration::AndroidSdk; using ::aapt::configuration::ConfiguredArtifact; using ::aapt::configuration::GetOrCreateGroup; using ::aapt::io::StringInputStream; +using ::android::ConfigDescription; using ::android::StringPiece; namespace aapt { diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h index fd5262af6e48..a88b11e1334e 100644 --- a/tools/aapt2/test/Builders.h +++ b/tools/aapt2/test/Builders.h @@ -20,6 +20,7 @@ #include <memory> #include "android-base/macros.h" +#include "androidfw/ConfigDescription.h" #include "Resource.h" #include "ResourceTable.h" @@ -40,7 +41,8 @@ class ResourceTableBuilder { ResourceTableBuilder& SetPackageId(const android::StringPiece& package_name, uint8_t id); ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ResourceId& id = {}); - ResourceTableBuilder& AddSimple(const android::StringPiece& name, const ConfigDescription& config, + ResourceTableBuilder& AddSimple(const android::StringPiece& name, + const android::ConfigDescription& config, const ResourceId& id = {}); ResourceTableBuilder& AddReference(const android::StringPiece& name, const android::StringPiece& ref); @@ -51,7 +53,8 @@ class ResourceTableBuilder { ResourceTableBuilder& AddString(const android::StringPiece& name, const ResourceId& id, const android::StringPiece& str); ResourceTableBuilder& AddString(const android::StringPiece& name, const ResourceId& id, - const ConfigDescription& config, const android::StringPiece& str); + const android::ConfigDescription& config, + const android::StringPiece& str); ResourceTableBuilder& AddFileReference(const android::StringPiece& name, const android::StringPiece& path, io::IFile* file = nullptr); @@ -60,12 +63,13 @@ class ResourceTableBuilder { io::IFile* file = nullptr); ResourceTableBuilder& AddFileReference(const android::StringPiece& name, const android::StringPiece& path, - const ConfigDescription& config, + const android::ConfigDescription& config, io::IFile* file = nullptr); ResourceTableBuilder& AddValue(const android::StringPiece& name, std::unique_ptr<Value> value); ResourceTableBuilder& AddValue(const android::StringPiece& name, const ResourceId& id, std::unique_ptr<Value> value); - ResourceTableBuilder& AddValue(const android::StringPiece& name, const ConfigDescription& config, + ResourceTableBuilder& AddValue(const android::StringPiece& name, + const android::ConfigDescription& config, const ResourceId& id, std::unique_ptr<Value> value); ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id, Visibility::Level level, bool allow_new = false); @@ -163,8 +167,8 @@ class ArtifactBuilder { ArtifactBuilder& SetName(const std::string& name); ArtifactBuilder& SetVersion(int version); ArtifactBuilder& AddAbi(configuration::Abi abi); - ArtifactBuilder& AddDensity(const ConfigDescription& density); - ArtifactBuilder& AddLocale(const ConfigDescription& locale); + ArtifactBuilder& AddDensity(const android::ConfigDescription& density); + ArtifactBuilder& AddLocale(const android::ConfigDescription& locale); ArtifactBuilder& SetAndroidSdk(int min_sdk); configuration::OutputArtifact Build(); diff --git a/tools/aapt2/test/Common.cpp b/tools/aapt2/test/Common.cpp index 0fabbc4070e7..b54c155ddc2f 100644 --- a/tools/aapt2/test/Common.cpp +++ b/tools/aapt2/test/Common.cpp @@ -16,6 +16,8 @@ #include "test/Common.h" +using android::ConfigDescription; + namespace aapt { namespace test { diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h index aca161a5189d..777ca5c72619 100644 --- a/tools/aapt2/test/Common.h +++ b/tools/aapt2/test/Common.h @@ -21,11 +21,11 @@ #include "android-base/logging.h" #include "android-base/macros.h" +#include "androidfw/ConfigDescription.h" #include "androidfw/StringPiece.h" #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "ConfigDescription.h" #include "Debug.h" #include "ResourceTable.h" #include "ResourceUtils.h" @@ -45,15 +45,15 @@ inline ResourceName ParseNameOrDie(const android::StringPiece& str) { return ref.ToResourceName(); } -inline ConfigDescription ParseConfigOrDie(const android::StringPiece& str) { - ConfigDescription config; - CHECK(ConfigDescription::Parse(str, &config)) << "invalid configuration: " << str; +inline android::ConfigDescription ParseConfigOrDie(const android::StringPiece& str) { + android::ConfigDescription config; + CHECK(android::ConfigDescription::Parse(str, &config)) << "invalid configuration: " << str; return config; } template <typename T = Value> T* GetValueForConfigAndProduct(ResourceTable* table, const android::StringPiece& res_name, - const ConfigDescription& config, + const android::ConfigDescription& config, const android::StringPiece& product) { Maybe<ResourceTable::SearchResult> result = table->FindResource(ParseNameOrDie(res_name)); if (result) { @@ -68,12 +68,12 @@ T* GetValueForConfigAndProduct(ResourceTable* table, const android::StringPiece& template <> Value* GetValueForConfigAndProduct<Value>(ResourceTable* table, const android::StringPiece& res_name, - const ConfigDescription& config, + const android::ConfigDescription& config, const android::StringPiece& product); template <typename T = Value> T* GetValueForConfig(ResourceTable* table, const android::StringPiece& res_name, - const ConfigDescription& config) { + const android::ConfigDescription& config) { return GetValueForConfigAndProduct<T>(table, res_name, config, {}); } @@ -173,10 +173,12 @@ class ValueEqImpl : public ::testing::MatcherInterface<T> { template <typename TValue> class ValueEqMatcher { public: + // NOLINTNEXTLINE(google-explicit-constructor) ValueEqMatcher(TValue expected) : expected_(std::move(expected)) { } template <typename T> + // NOLINTNEXTLINE(google-explicit-constructor) operator ::testing::Matcher<T>() const { return ::testing::Matcher<T>(new ValueEqImpl<T>(&expected_)); } @@ -188,10 +190,12 @@ class ValueEqMatcher { template <typename TValue> class ValueEqPointerMatcher { public: + // NOLINTNEXTLINE(google-explicit-constructor) ValueEqPointerMatcher(const TValue* expected) : expected_(expected) { } template <typename T> + // NOLINTNEXTLINE(google-explicit-constructor) operator ::testing::Matcher<T>() const { return ::testing::Matcher<T>(new ValueEqImpl<T>(expected_)); } diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp index 5a8ff0926483..a407c228503a 100644 --- a/tools/aapt2/util/Files.cpp +++ b/tools/aapt2/util/Files.cpp @@ -165,7 +165,7 @@ void AppendPath(std::string* base, StringPiece part) { std::string PackageToPath(const StringPiece& package) { std::string out_path; - for (StringPiece part : util::Tokenize(package, '.')) { + for (const StringPiece& part : util::Tokenize(package, '.')) { AppendPath(&out_path, part); } return out_path; diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h index 031276c8b885..047e1a581330 100644 --- a/tools/aapt2/util/Maybe.h +++ b/tools/aapt2/util/Maybe.h @@ -44,12 +44,12 @@ class Maybe { Maybe(const Maybe& rhs); template <typename U> - Maybe(const Maybe<U>& rhs); // NOLINT(implicit) + Maybe(const Maybe<U>& rhs); // NOLINT(google-explicit-constructor) Maybe(Maybe&& rhs) noexcept; template <typename U> - Maybe(Maybe<U>&& rhs); // NOLINT(implicit) + Maybe(Maybe<U>&& rhs); // NOLINT(google-explicit-constructor) Maybe& operator=(const Maybe& rhs); @@ -64,12 +64,12 @@ class Maybe { /** * Construct a Maybe holding a value. */ - Maybe(const T& value); // NOLINT(implicit) + Maybe(const T& value); // NOLINT(google-explicit-constructor) /** * Construct a Maybe holding a value. */ - Maybe(T&& value); // NOLINT(implicit) + Maybe(T&& value); // NOLINT(google-explicit-constructor) /** * True if this holds a value, false if diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh index e50c70d0656a..f25fcdcb7479 100755 --- a/tools/aosp/aosp_sha.sh +++ b/tools/aosp/aosp_sha.sh @@ -19,6 +19,6 @@ else echo "If your change contains no confidential details (such as security fixes), please" echo "upload and merge this change at https://android-review.googlesource.com/." echo - exit 77 + exit 1 fi fi diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py index 70a47cf42755..59e89f515e82 100644 --- a/tools/apilint/apilint.py +++ b/tools/apilint/apilint.py @@ -26,7 +26,7 @@ $ git blame api/current.txt -t -e > /tmp/currentblame.txt $ apilint.py /tmp/currentblame.txt previous.txt --no-color """ -import re, sys, collections, traceback, argparse +import re, sys, collections, traceback, argparse, itertools BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) @@ -50,38 +50,37 @@ def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False): return "\033[%sm" % (";".join(codes)) -def ident(raw): - """Strips superficial signature changes, giving us a strong key that - can be used to identify members across API levels.""" - raw = raw.replace(" deprecated ", " ") - raw = raw.replace(" synchronized ", " ") - raw = raw.replace(" final ", " ") - raw = re.sub("<.+?>", "", raw) - if " throws " in raw: - raw = raw[:raw.index(" throws ")] - return raw - - class Field(): - def __init__(self, clazz, line, raw, blame): + def __init__(self, clazz, line, raw, blame, sig_format = 1): self.clazz = clazz self.line = line self.raw = raw.strip(" {;") self.blame = blame - raw = raw.split() - self.split = list(raw) + if sig_format == 2: + V2LineParser(raw).parse_into_field(self) + elif sig_format == 1: + # drop generics for now; may need multiple passes + raw = re.sub("<[^<]+?>", "", raw) + raw = re.sub("<[^<]+?>", "", raw) - for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]: - while r in raw: raw.remove(r) + raw = raw.split() + self.split = list(raw) - self.typ = raw[0] - self.name = raw[1].strip(";") - if len(raw) >= 4 and raw[2] == "=": - self.value = raw[3].strip(';"') - else: - self.value = None - self.ident = ident(self.raw) + for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]: + while r in raw: raw.remove(r) + + # ignore annotations for now + raw = [ r for r in raw if not r.startswith("@") ] + + self.typ = raw[0] + self.name = raw[1].strip(";") + if len(raw) >= 4 and raw[2] == "=": + self.value = raw[3].strip(';"') + else: + self.value = None + + self.ident = "-".join((self.typ, self.name, self.value or "")) def __hash__(self): return hash(self.raw) @@ -89,34 +88,55 @@ class Field(): def __repr__(self): return self.raw - class Method(): - def __init__(self, clazz, line, raw, blame): + def __init__(self, clazz, line, raw, blame, sig_format = 1): self.clazz = clazz self.line = line self.raw = raw.strip(" {;") self.blame = blame - # drop generics for now - raw = re.sub("<.+?>", "", raw) - - raw = re.split("[\s(),;]+", raw) - for r in ["", ";"]: - while r in raw: raw.remove(r) - self.split = list(raw) + if sig_format == 2: + V2LineParser(raw).parse_into_method(self) + elif sig_format == 1: + # drop generics for now; may need multiple passes + raw = re.sub("<[^<]+?>", "", raw) + raw = re.sub("<[^<]+?>", "", raw) + + # handle each clause differently + raw_prefix, raw_args, _, raw_throws = re.match(r"(.*?)\((.*?)\)( throws )?(.*?);$", raw).groups() + + # parse prefixes + raw = re.split("[\s]+", raw_prefix) + for r in ["", ";"]: + while r in raw: raw.remove(r) + self.split = list(raw) + + for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract", "default", "operator", "synchronized"]: + while r in raw: raw.remove(r) + + self.typ = raw[0] + self.name = raw[1] + + # parse args + self.args = [] + for arg in re.split(",\s*", raw_args): + arg = re.split("\s", arg) + # ignore annotations for now + arg = [ a for a in arg if not a.startswith("@") ] + if len(arg[0]) > 0: + self.args.append(arg[0]) + + # parse throws + self.throws = [] + for throw in re.split(",\s*", raw_throws): + self.throws.append(throw) + else: + raise ValueError("Unknown signature format: " + sig_format) - for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract", "default"]: - while r in raw: raw.remove(r) + self.ident = "-".join((self.typ, self.name, "-".join(self.args))) - self.typ = raw[0] - self.name = raw[1] - self.args = [] - self.throws = [] - target = self.args - for r in raw[2:]: - if r == "throws": target = self.throws - else: target.append(r) - self.ident = ident(self.raw) + def sig_matches(self, typ, name, args): + return typ == self.typ and name == self.name and args == self.args def __hash__(self): return hash(self.raw) @@ -126,7 +146,7 @@ class Method(): class Class(): - def __init__(self, pkg, line, raw, blame): + def __init__(self, pkg, line, raw, blame, sig_format = 1): self.pkg = pkg self.line = line self.raw = raw.strip(" {;") @@ -135,27 +155,51 @@ class Class(): self.fields = [] self.methods = [] - raw = raw.split() - self.split = list(raw) - if "class" in raw: - self.fullname = raw[raw.index("class")+1] - elif "interface" in raw: - self.fullname = raw[raw.index("interface")+1] - else: - raise ValueError("Funky class type %s" % (self.raw)) + if sig_format == 2: + V2LineParser(raw).parse_into_class(self) + elif sig_format == 1: + # drop generics for now; may need multiple passes + raw = re.sub("<[^<]+?>", "", raw) + raw = re.sub("<[^<]+?>", "", raw) + + raw = raw.split() + self.split = list(raw) + if "class" in raw: + self.fullname = raw[raw.index("class")+1] + elif "interface" in raw: + self.fullname = raw[raw.index("interface")+1] + elif "@interface" in raw: + self.fullname = raw[raw.index("@interface")+1] + else: + raise ValueError("Funky class type %s" % (self.raw)) - if "extends" in raw: - self.extends = raw[raw.index("extends")+1] - self.extends_path = self.extends.split(".") + if "extends" in raw: + self.extends = raw[raw.index("extends")+1] + else: + self.extends = None + + if "implements" in raw: + self.implements = raw[raw.index("implements")+1] + else: + self.implements = None else: - self.extends = None - self.extends_path = [] + raise ValueError("Unknown signature format: " + sig_format) self.fullname = self.pkg.name + "." + self.fullname self.fullname_path = self.fullname.split(".") + if self.extends is not None: + self.extends_path = self.extends.split(".") + else: + self.extends_path = [] + self.name = self.fullname[self.fullname.rindex(".")+1:] + def merge_from(self, other): + self.ctors.extend(other.ctors) + self.fields.extend(other.fields) + self.methods.extend(other.methods) + def __hash__(self): return hash((self.raw, tuple(self.ctors), tuple(self.fields), tuple(self.methods))) @@ -176,15 +220,387 @@ class Package(): def __repr__(self): return self.raw +class V2Tokenizer(object): + __slots__ = ["raw"] + + SIGNATURE_PREFIX = "// Signature format: " + DELIMITER = re.compile(r'\s+|[()@<>;,={}/"!?]|\[\]|\.\.\.') + STRING_SPECIAL = re.compile(r'["\\]') + + def __init__(self, raw): + self.raw = raw + + def tokenize(self): + tokens = [] + current = 0 + raw = self.raw + length = len(raw) + + while current < length: + while current < length: + start = current + match = V2Tokenizer.DELIMITER.search(raw, start) + if match is not None: + match_start = match.start() + if match_start == current: + end = match.end() + else: + end = match_start + else: + end = length -def _parse_stream(f, clazz_cb=None): - line = 0 + token = raw[start:end] + current = end + + if token == "" or token[0] == " ": + continue + else: + break + + if token == "@": + if raw[start:start+11] == "@interface ": + current = start + 11 + tokens.append("@interface") + continue + elif token == '/': + if raw[start:start+2] == "//": + current = length + continue + elif token == '"': + current, string_token = self.tokenize_string(raw, length, current) + tokens.append(token + string_token) + continue + + tokens.append(token) + + return tokens + + def tokenize_string(self, raw, length, current): + start = current + end = length + while start < end: + match = V2Tokenizer.STRING_SPECIAL.search(raw, start) + if match: + if match.group() == '"': + end = match.end() + break + elif match.group() == '\\': + # ignore whatever is after the slash + start += 2 + else: + raise ValueError("Unexpected match: `%s`" % (match.group())) + else: + raise ValueError("Unexpected EOF tokenizing string: `%s`" % (raw[current - 1:],)) + + token = raw[current:end] + return end, token + +class V2LineParser(object): + __slots__ = ["tokenized", "current", "len"] + + FIELD_KINDS = ("field", "property", "enum_constant") + MODIFIERS = set("public protected internal private abstract default static final transient volatile synchronized native operator sealed strictfp infix inline suspend vararg".split()) + JAVA_LANG_TYPES = set("AbstractMethodError AbstractStringBuilder Appendable ArithmeticException ArrayIndexOutOfBoundsException ArrayStoreException AssertionError AutoCloseable Boolean BootstrapMethodError Byte Character CharSequence Class ClassCastException ClassCircularityError ClassFormatError ClassLoader ClassNotFoundException Cloneable CloneNotSupportedException Comparable Compiler Deprecated Double Enum EnumConstantNotPresentException Error Exception ExceptionInInitializerError Float FunctionalInterface IllegalAccessError IllegalAccessException IllegalArgumentException IllegalMonitorStateException IllegalStateException IllegalThreadStateException IncompatibleClassChangeError IndexOutOfBoundsException InheritableThreadLocal InstantiationError InstantiationException Integer InternalError InterruptedException Iterable LinkageError Long Math NegativeArraySizeException NoClassDefFoundError NoSuchFieldError NoSuchFieldException NoSuchMethodError NoSuchMethodException NullPointerException Number NumberFormatException Object OutOfMemoryError Override Package package-info.java Process ProcessBuilder ProcessEnvironment ProcessImpl Readable ReflectiveOperationException Runnable Runtime RuntimeException RuntimePermission SafeVarargs SecurityException SecurityManager Short StackOverflowError StackTraceElement StrictMath String StringBuffer StringBuilder StringIndexOutOfBoundsException SuppressWarnings System Thread ThreadDeath ThreadGroup ThreadLocal Throwable TypeNotPresentException UNIXProcess UnknownError UnsatisfiedLinkError UnsupportedClassVersionError UnsupportedOperationException VerifyError VirtualMachineError Void".split()) + + def __init__(self, raw): + self.tokenized = V2Tokenizer(raw).tokenize() + self.current = 0 + self.len = len(self.tokenized) + + def parse_into_method(self, method): + method.split = [] + kind = self.parse_one_of("ctor", "method") + method.split.append(kind) + annotations = self.parse_annotations() + method.split.extend(self.parse_modifiers()) + self.parse_matching_paren("<", ">") + if "@Deprecated" in annotations: + method.split.append("deprecated") + if kind == "ctor": + method.typ = "ctor" + else: + method.typ = self.parse_type() + method.split.append(method.typ) + method.name = self.parse_name() + method.split.append(method.name) + self.parse_token("(") + method.args = self.parse_args() + self.parse_token(")") + method.throws = self.parse_throws() + if "@interface" in method.clazz.split: + self.parse_annotation_default() + self.parse_token(";") + self.parse_eof() + + def parse_into_class(self, clazz): + clazz.split = [] + annotations = self.parse_annotations() + if "@Deprecated" in annotations: + clazz.split.append("deprecated") + clazz.split.extend(self.parse_modifiers()) + kind = self.parse_one_of("class", "interface", "@interface", "enum") + if kind == "enum": + # enums are implicitly final + clazz.split.append("final") + clazz.split.append(kind) + clazz.fullname = self.parse_name() + self.parse_matching_paren("<", ">") + extends = self.parse_extends() + clazz.extends = extends[0] if extends else None + implements = self.parse_implements() + clazz.implements = implements[0] if implements else None + # The checks assume that interfaces are always found in implements, which isn't true for + # subinterfaces. + if not implements and "interface" in clazz.split: + clazz.implements = clazz.extends + self.parse_token("{") + self.parse_eof() + + def parse_into_field(self, field): + kind = self.parse_one_of(*V2LineParser.FIELD_KINDS) + field.split = [kind] + annotations = self.parse_annotations() + if "@Deprecated" in annotations: + field.split.append("deprecated") + field.split.extend(self.parse_modifiers()) + field.typ = self.parse_type() + field.split.append(field.typ) + field.name = self.parse_name() + field.split.append(field.name) + if self.parse_if("="): + field.value = self.parse_value_stripped() + else: + field.value = None + + self.parse_token(";") + self.parse_eof() + + def lookahead(self): + return self.tokenized[self.current] + + def parse_one_of(self, *options): + found = self.lookahead() + if found not in options: + raise ValueError("Parsing failed, expected one of `%s` but found `%s` in %s" % (options, found, repr(self.tokenized))) + return self.parse_token() + + def parse_token(self, tok = None): + found = self.lookahead() + if tok is not None and found != tok: + raise ValueError("Parsing failed, expected `%s` but found `%s` in %s" % (tok, found, repr(self.tokenized))) + self.current += 1 + return found + + def eof(self): + return self.current == self.len + + def parse_eof(self): + if not self.eof(): + raise ValueError("Parsing failed, expected EOF, but %s has not been parsed in %s" % (self.tokenized[self.current:], self.tokenized)) + + def parse_if(self, tok): + if not self.eof() and self.lookahead() == tok: + self.parse_token() + return True + return False + + def parse_annotations(self): + ret = [] + while self.lookahead() == "@": + ret.append(self.parse_annotation()) + return ret + + def parse_annotation(self): + ret = self.parse_token("@") + self.parse_token() + self.parse_matching_paren("(", ")") + return ret + + def parse_matching_paren(self, open, close): + start = self.current + if not self.parse_if(open): + return + length = len(self.tokenized) + count = 1 + while count > 0: + if self.current == length: + raise ValueError("Unexpected EOF looking for closing paren: `%s`" % (self.tokenized[start:],)) + t = self.parse_token() + if t == open: + count += 1 + elif t == close: + count -= 1 + return self.tokenized[start:self.current] + + def parse_modifiers(self): + ret = [] + while self.lookahead() in V2LineParser.MODIFIERS: + ret.append(self.parse_token()) + return ret + + def parse_kotlin_nullability(self): + t = self.lookahead() + if t == "?" or t == "!": + return self.parse_token() + return None + + def parse_type(self): + self.parse_annotations() + type = self.parse_token() + if type[-1] == '.': + self.parse_annotations() + type += self.parse_token() + if type in V2LineParser.JAVA_LANG_TYPES: + type = "java.lang." + type + self.parse_matching_paren("<", ">") + while True: + t = self.lookahead() + if t == "@": + self.parse_annotation() + elif t == "[]": + type += self.parse_token() + elif self.parse_kotlin_nullability() is not None: + pass # discard nullability for now + else: + break + return type + + def parse_arg_type(self): + type = self.parse_type() + if self.parse_if("..."): + type += "..." + self.parse_kotlin_nullability() # discard nullability for now + return type + + def parse_name(self): + return self.parse_token() + + def parse_args(self): + args = [] + if self.lookahead() == ")": + return args + + while True: + args.append(self.parse_arg()) + if self.lookahead() == ")": + return args + self.parse_token(",") + + def parse_arg(self): + self.parse_if("vararg") # kotlin vararg + self.parse_annotations() + type = self.parse_arg_type() + l = self.lookahead() + if l != "," and l != ")": + if self.lookahead() != '=': + self.parse_token() # kotlin argument name + if self.parse_if('='): # kotlin default value + self.parse_expression() + return type + + def parse_expression(self): + while not self.lookahead() in [')', ',', ';']: + (self.parse_matching_paren('(', ')') or + self.parse_matching_paren('{', '}') or + self.parse_token()) + + def parse_throws(self): + ret = [] + if self.parse_if("throws"): + ret.append(self.parse_type()) + while self.parse_if(","): + ret.append(self.parse_type()) + return ret + + def parse_extends(self): + if self.parse_if("extends"): + return self.parse_space_delimited_type_list() + return [] + + def parse_implements(self): + if self.parse_if("implements"): + return self.parse_space_delimited_type_list() + return [] + + def parse_space_delimited_type_list(self, terminals = ["implements", "{"]): + types = [] + while True: + types.append(self.parse_type()) + if self.lookahead() in terminals: + return types + + def parse_annotation_default(self): + if self.parse_if("default"): + self.parse_expression() + + def parse_value(self): + if self.lookahead() == "{": + return " ".join(self.parse_matching_paren("{", "}")) + elif self.lookahead() == "(": + return " ".join(self.parse_matching_paren("(", ")")) + else: + return self.parse_token() + + def parse_value_stripped(self): + value = self.parse_value() + if value[0] == '"': + return value[1:-1] + return value + + +def _parse_stream(f, clazz_cb=None, base_f=None, out_classes_with_base=None, + in_classes_with_base=[]): api = {} + in_classes_with_base = _retry_iterator(in_classes_with_base) + + if base_f: + base_classes = _retry_iterator(_parse_stream_to_generator(base_f)) + else: + base_classes = [] + + def handle_class(clazz): + if clazz_cb: + clazz_cb(clazz) + else: # In callback mode, don't keep track of the full API + api[clazz.fullname] = clazz + + def handle_missed_classes_with_base(clazz): + for c in _yield_until_matching_class(in_classes_with_base, clazz): + base_class = _skip_to_matching_class(base_classes, c) + if base_class: + handle_class(base_class) + + for clazz in _parse_stream_to_generator(f): + # Before looking at clazz, let's see if there's some classes that were not present, but + # may have an entry in the base stream. + handle_missed_classes_with_base(clazz) + + base_class = _skip_to_matching_class(base_classes, clazz) + if base_class: + clazz.merge_from(base_class) + if out_classes_with_base is not None: + out_classes_with_base.append(clazz) + handle_class(clazz) + + handle_missed_classes_with_base(None) + + return api + +def _parse_stream_to_generator(f): + line = 0 pkg = None clazz = None blame = None + sig_format = 1 re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$") + + field_prefixes = map(lambda kind: " %s" % (kind,), V2LineParser.FIELD_KINDS) + def startsWithFieldPrefix(raw): + for prefix in field_prefixes: + if raw.startswith(prefix): + return True + return False + for raw in f: line += 1 raw = raw.rstrip() @@ -195,29 +611,76 @@ def _parse_stream(f, clazz_cb=None): else: blame = None - if raw.startswith("package"): + if line == 1 and raw.startswith("// Signature format: "): + sig_format_string = raw[len(V2Tokenizer.SIGNATURE_PREFIX):] + if sig_format_string in ["2.0", "3.0"]: + sig_format = 2 + else: + raise ValueError("Unknown format: %s" % (sig_format_string,)) + elif raw.startswith("package"): pkg = Package(line, raw, blame) elif raw.startswith(" ") and raw.endswith("{"): - # When provided with class callback, we treat as incremental - # parse and don't build up entire API - if clazz and clazz_cb: - clazz_cb(clazz) - clazz = Class(pkg, line, raw, blame) - if not clazz_cb: - api[clazz.fullname] = clazz + clazz = Class(pkg, line, raw, blame, sig_format=sig_format) elif raw.startswith(" ctor"): - clazz.ctors.append(Method(clazz, line, raw, blame)) + clazz.ctors.append(Method(clazz, line, raw, blame, sig_format=sig_format)) elif raw.startswith(" method"): - clazz.methods.append(Method(clazz, line, raw, blame)) - elif raw.startswith(" field"): - clazz.fields.append(Field(clazz, line, raw, blame)) - - # Handle last trailing class - if clazz and clazz_cb: - clazz_cb(clazz) - - return api - + clazz.methods.append(Method(clazz, line, raw, blame, sig_format=sig_format)) + elif startsWithFieldPrefix(raw): + clazz.fields.append(Field(clazz, line, raw, blame, sig_format=sig_format)) + elif raw.startswith(" }") and clazz: + yield clazz + +def _retry_iterator(it): + """Wraps an iterator, such that calling send(True) on it will redeliver the same element""" + for e in it: + while True: + retry = yield e + if not retry: + break + # send() was called, asking us to redeliver clazz on next(). Still need to yield + # a dummy value to the send() first though. + if (yield "Returning clazz on next()"): + raise TypeError("send() must be followed by next(), not send()") + +def _skip_to_matching_class(classes, needle): + """Takes a classes iterator and consumes entries until it returns the class we're looking for + + This relies on classes being sorted by package and class name.""" + + for clazz in classes: + if clazz.pkg.name < needle.pkg.name: + # We haven't reached the right package yet + continue + if clazz.pkg.name == needle.pkg.name and clazz.fullname < needle.fullname: + # We're in the right package, but not the right class yet + continue + if clazz.fullname == needle.fullname: + return clazz + # We ran past the right class. Send it back into the generator, then report failure. + classes.send(clazz) + return None + +def _yield_until_matching_class(classes, needle): + """Takes a class iterator and yields entries it until it reaches the class we're looking for. + + This relies on classes being sorted by package and class name.""" + + for clazz in classes: + if needle is None: + yield clazz + elif clazz.pkg.name < needle.pkg.name: + # We haven't reached the right package yet + yield clazz + elif clazz.pkg.name == needle.pkg.name and clazz.fullname < needle.fullname: + # We're in the right package, but not the right class yet + yield clazz + elif clazz.fullname == needle.fullname: + # Class found, abort. + return + else: + # We ran past the right class. Send it back into the iterator, then abort. + classes.send(clazz) + return class Failure(): def __init__(self, sig, clazz, detail, error, rule, msg): @@ -257,7 +720,7 @@ def _fail(clazz, detail, error, rule, msg): """Records an API failure to be processed later.""" global failures - sig = "%s-%s-%s" % (clazz.fullname, repr(detail), msg) + sig = "%s-%s-%s" % (clazz.fullname, detail.ident if detail else None, msg) sig = sig.replace(" deprecated ", " ") failures[sig] = Failure(sig, clazz, detail, error, rule, msg) @@ -298,7 +761,7 @@ def verify_constants(clazz): def verify_enums(clazz): """Enums are bad, mmkay?""" - if "extends java.lang.Enum" in clazz.raw: + if clazz.extends == "java.lang.Enum" or "enum" in clazz.split: error(clazz, None, "F5", "Enums are not allowed") @@ -357,7 +820,7 @@ def verify_listeners(clazz): interface OnFooListener { void onFoo() }""" if clazz.name.endswith("Listener"): - if " abstract class " in clazz.raw: + if "abstract" in clazz.split and "class" in clazz.split: error(clazz, None, "L1", "Listeners should be an interface, or otherwise renamed Callback") for m in clazz.methods: @@ -436,16 +899,16 @@ def verify_equals(clazz): eq = False hc = False for m in clazz.methods: - if " static " in m.raw: continue - if "boolean equals(java.lang.Object)" in m.raw: eq = True - if "int hashCode()" in m.raw: hc = True + if "static" in m.split: continue + if m.sig_matches("boolean", "equals", ["java.lang.Object"]): eq = True + if m.sig_matches("int", "hashCode", []): hc = True if eq != hc: error(clazz, None, "M8", "Must override both equals and hashCode; missing one") def verify_parcelable(clazz): """Verify that Parcelable objects aren't hiding required bits.""" - if "implements android.os.Parcelable" in clazz.raw: + if clazz.implements == "android.os.Parcelable": creator = [ i for i in clazz.fields if i.name == "CREATOR" ] write = [ i for i in clazz.methods if i.name == "writeToParcel" ] describe = [ i for i in clazz.methods if i.name == "describeContents" ] @@ -453,8 +916,7 @@ def verify_parcelable(clazz): if len(creator) == 0 or len(write) == 0 or len(describe) == 0: error(clazz, None, "FW3", "Parcelable requires CREATOR, writeToParcel, and describeContents; missing one") - if ((" final class " not in clazz.raw) and - (" final deprecated class " not in clazz.raw)): + if "final" not in clazz.split: error(clazz, None, "FW8", "Parcelable classes must be final") for c in clazz.ctors: @@ -465,6 +927,7 @@ def verify_parcelable(clazz): def verify_protected(clazz): """Verify that no protected methods or fields are allowed.""" for m in clazz.methods: + if m.name == "finalize": continue if "protected" in m.split: error(clazz, m, "M7", "Protected methods not allowed; must be public") for f in clazz.fields: @@ -505,7 +968,7 @@ def verify_fields(clazz): else: error(clazz, f, "F2", "Bare fields must be marked final, or add accessors if mutable") - if not "static" in f.split: + if "static" not in f.split and "property" not in f.split: if not re.match("[a-z]([a-zA-Z]+)?", f.name): error(clazz, f, "S1", "Non-static fields must be named using myField style") @@ -573,7 +1036,7 @@ def verify_helper_classes(clazz): """Verify that helper classes are named consistently with what they extend. All developer extendable methods should be named onFoo().""" test_methods = False - if "extends android.app.Service" in clazz.raw: + if clazz.extends == "android.app.Service": test_methods = True if not clazz.name.endswith("Service"): error(clazz, None, "CL4", "Inconsistent class name; should be FooService") @@ -585,7 +1048,7 @@ def verify_helper_classes(clazz): if f.value != clazz.fullname: error(clazz, f, "C4", "Inconsistent interface constant; expected '%s'" % (clazz.fullname)) - if "extends android.content.ContentProvider" in clazz.raw: + if clazz.extends == "android.content.ContentProvider": test_methods = True if not clazz.name.endswith("Provider"): error(clazz, None, "CL4", "Inconsistent class name; should be FooProvider") @@ -597,12 +1060,12 @@ def verify_helper_classes(clazz): if f.value != clazz.fullname: error(clazz, f, "C4", "Inconsistent interface constant; expected '%s'" % (clazz.fullname)) - if "extends android.content.BroadcastReceiver" in clazz.raw: + if clazz.extends == "android.content.BroadcastReceiver": test_methods = True if not clazz.name.endswith("Receiver"): error(clazz, None, "CL4", "Inconsistent class name; should be FooReceiver") - if "extends android.app.Activity" in clazz.raw: + if clazz.extends == "android.app.Activity": test_methods = True if not clazz.name.endswith("Activity"): error(clazz, None, "CL4", "Inconsistent class name; should be FooActivity") @@ -620,7 +1083,7 @@ def verify_helper_classes(clazz): def verify_builder(clazz): """Verify builder classes. Methods should return the builder to enable chaining.""" - if " extends " in clazz.raw: return + if clazz.extends: return if not clazz.name.endswith("Builder"): return if clazz.name != "Builder": @@ -648,7 +1111,7 @@ def verify_builder(clazz): def verify_aidl(clazz): """Catch people exposing raw AIDL.""" - if "extends android.os.Binder" in clazz.raw or "implements android.os.IInterface" in clazz.raw: + if clazz.extends == "android.os.Binder" or clazz.implements == "android.os.IInterface": error(clazz, None, None, "Raw AIDL interfaces must not be exposed") @@ -657,48 +1120,66 @@ def verify_internal(clazz): if clazz.pkg.name.startswith("com.android"): error(clazz, None, None, "Internal classes must not be exposed") +def layering_build_ranking(ranking_list): + r = {} + for rank, ps in enumerate(ranking_list): + if not isinstance(ps, list): + ps = [ps] + for p in ps: + rs = r + for n in p.split('.'): + if n not in rs: + rs[n] = {} + rs = rs[n] + rs['-rank'] = rank + return r + +LAYERING_PACKAGE_RANKING = layering_build_ranking([ + ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"], + "android.app", + "android.widget", + "android.view", + "android.animation", + "android.provider", + ["android.content","android.graphics.drawable"], + "android.database", + "android.text", + "android.graphics", + "android.os", + "android.util" +]) def verify_layering(clazz): """Catch package layering violations. For example, something in android.os depending on android.app.""" - ranking = [ - ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"], - "android.app", - "android.widget", - "android.view", - "android.animation", - "android.provider", - ["android.content","android.graphics.drawable"], - "android.database", - "android.graphics", - "android.text", - "android.os", - "android.util" - ] def rank(p): - for i in range(len(ranking)): - if isinstance(ranking[i], list): - for j in ranking[i]: - if p.startswith(j): return i + r = None + l = LAYERING_PACKAGE_RANKING + for n in p.split('.'): + if n in l: + l = l[n] + if '-rank' in l: + r = l['-rank'] else: - if p.startswith(ranking[i]): return i + break + return r cr = rank(clazz.pkg.name) if cr is None: return for f in clazz.fields: ir = rank(f.typ) - if ir and ir < cr: + if ir is not None and ir < cr: warn(clazz, f, "FW6", "Field type violates package layering") - for m in clazz.methods: + for m in itertools.chain(clazz.methods, clazz.ctors): ir = rank(m.typ) - if ir and ir < cr: + if ir is not None and ir < cr: warn(clazz, m, "FW6", "Method return type violates package layering") for arg in m.args: ir = rank(arg) - if ir and ir < cr: + if ir is not None and ir < cr: warn(clazz, m, "FW6", "Method argument type violates package layering") @@ -789,21 +1270,18 @@ def verify_exception(clazz): if len(m.args) == 0 and t in ["java.lang.IllegalArgumentException", "java.lang.NullPointerException"]: warn(clazz, m, "S1", "Methods taking no arguments should throw IllegalStateException") +GOOGLE_IGNORECASE = re.compile("google", re.IGNORECASE) def verify_google(clazz): """Verifies that APIs never reference Google.""" - if re.search("google", clazz.raw, re.IGNORECASE): + if GOOGLE_IGNORECASE.search(clazz.raw) is not None: error(clazz, None, None, "Must never reference Google") - test = [] - test.extend(clazz.ctors) - test.extend(clazz.fields) - test.extend(clazz.methods) - - for t in test: - if re.search("google", t.raw, re.IGNORECASE): - error(clazz, t, None, "Must never reference Google") + for test in clazz.ctors, clazz.fields, clazz.methods: + for t in test: + if GOOGLE_IGNORECASE.search(t.raw) is not None: + error(clazz, t, None, "Must never reference Google") def verify_bitset(clazz): @@ -998,6 +1476,10 @@ def verify_resource_names(clazz): # Resources defined by files are foo_bar_baz if clazz.name in ["anim","animator","color","dimen","drawable","interpolator","layout","transition","menu","mipmap","string","plurals","raw","xml"]: for f in clazz.fields: + if re.match("config_[a-z][a-zA-Z1-9]*$", f.name): continue + if f.name.startswith("config_"): + error(clazz, f, None, "Expected config name to be config_fooBarBaz style") + if re.match("[a-z1-9_]+$", f.name): continue error(clazz, f, None, "Expected resource name in this class to be foo_bar_baz style") @@ -1053,7 +1535,7 @@ def verify_abstract_inner(clazz): """Verifies that abstract inner classes are static.""" if re.match(".+?\.[A-Z][^\.]+\.[A-Z]", clazz.fullname): - if " abstract " in clazz.raw and " static " not in clazz.raw: + if "abstract" in clazz.split and "static" not in clazz.split: warn(clazz, None, None, "Abstract inner classes should be static to improve testability") @@ -1148,8 +1630,8 @@ def verify_units(clazz): def verify_closable(clazz): """Verifies that classes are AutoClosable.""" - if "implements java.lang.AutoCloseable" in clazz.raw: return - if "implements java.io.Closeable" in clazz.raw: return + if clazz.implements == "java.lang.AutoCloseable": return + if clazz.implements == "java.io.Closeable": return for m in clazz.methods: if len(m.args) > 0: continue @@ -1194,7 +1676,7 @@ def verify_method_name_not_kotlin_operator(clazz): binary.add(op) for m in clazz.methods: - if 'static' in m.split: + if 'static' in m.split or 'operator' in m.split: continue # https://kotlinlang.org/docs/reference/operator-overloading.html#unary-prefix-operators @@ -1235,6 +1717,9 @@ def verify_method_name_not_kotlin_operator(clazz): def verify_collections_over_arrays(clazz): """Warn that [] should be Collections.""" + if "@interface" in clazz.split: + return + safe = ["java.lang.String[]","byte[]","short[]","int[]","long[]","float[]","double[]","boolean[]","char[]"] for m in clazz.methods: if m.typ.endswith("[]") and m.typ not in safe: @@ -1253,10 +1738,19 @@ def verify_user_handle(clazz): if clazz.fullname == "android.os.UserManager": return for m in clazz.methods: - if m.name.endswith("AsUser") or m.name.endswith("ForUser"): continue if re.match("on[A-Z]+", m.name): continue - if "android.os.UserHandle" in m.args: - warn(clazz, m, None, "Method taking UserHandle should be named 'doFooAsUser' or 'queryFooForUser'") + + has_arg = "android.os.UserHandle" in m.args + has_name = m.name.endswith("AsUser") or m.name.endswith("ForUser") + + if clazz.fullname.endswith("Manager") and has_arg: + warn(clazz, m, None, "When a method overload is needed to target a specific " + "UserHandle, callers should be directed to use " + "Context.createPackageContextAsUser() and re-obtain the relevant " + "Manager, and no new API should be added") + elif has_arg and not has_name: + warn(clazz, m, None, "Method taking UserHandle should be named 'doFooAsUser' " + "or 'queryFooForUser'") def verify_params(clazz): @@ -1334,18 +1828,79 @@ def verify_clone(clazz): error(clazz, m, None, "Provide an explicit copy constructor instead of implementing clone()") +def verify_pfd(clazz): + """Verify that android APIs use PFD over FD.""" + examine = clazz.ctors + clazz.methods + for m in examine: + if m.typ == "java.io.FileDescriptor": + error(clazz, m, "FW11", "Must use ParcelFileDescriptor") + if m.typ == "int": + if "Fd" in m.name or "FD" in m.name or "FileDescriptor" in m.name: + error(clazz, m, "FW11", "Must use ParcelFileDescriptor") + for arg in m.args: + if arg == "java.io.FileDescriptor": + error(clazz, m, "FW11", "Must use ParcelFileDescriptor") + + for f in clazz.fields: + if f.typ == "java.io.FileDescriptor": + error(clazz, f, "FW11", "Must use ParcelFileDescriptor") + + +def verify_numbers(clazz): + """Discourage small numbers types like short and byte.""" + + discouraged = ["short","byte"] + + for c in clazz.ctors: + for arg in c.args: + if arg in discouraged: + warn(clazz, c, "FW12", "Should avoid odd sized primitives; use int instead") + + for f in clazz.fields: + if f.typ in discouraged: + warn(clazz, f, "FW12", "Should avoid odd sized primitives; use int instead") + + for m in clazz.methods: + if m.typ in discouraged: + warn(clazz, m, "FW12", "Should avoid odd sized primitives; use int instead") + for arg in m.args: + if arg in discouraged: + warn(clazz, m, "FW12", "Should avoid odd sized primitives; use int instead") + + +def verify_singleton(clazz): + """Catch singleton objects with constructors.""" + + singleton = False + for m in clazz.methods: + if m.name.startswith("get") and m.name.endswith("Instance") and " static " in m.raw: + singleton = True + + if singleton: + for c in clazz.ctors: + error(clazz, c, None, "Singleton classes should use getInstance() methods") + + + +def is_interesting(clazz): + """Test if given class is interesting from an Android PoV.""" + + if clazz.pkg.name.startswith("java"): return False + if clazz.pkg.name.startswith("junit"): return False + if clazz.pkg.name.startswith("org.apache"): return False + if clazz.pkg.name.startswith("org.xml"): return False + if clazz.pkg.name.startswith("org.json"): return False + if clazz.pkg.name.startswith("org.w3c"): return False + if clazz.pkg.name.startswith("android.icu."): return False + return True + + def examine_clazz(clazz): """Find all style issues in the given class.""" notice(clazz) - if clazz.pkg.name.startswith("java"): return - if clazz.pkg.name.startswith("junit"): return - if clazz.pkg.name.startswith("org.apache"): return - if clazz.pkg.name.startswith("org.xml"): return - if clazz.pkg.name.startswith("org.json"): return - if clazz.pkg.name.startswith("org.w3c"): return - if clazz.pkg.name.startswith("android.icu."): return + if not is_interesting(clazz): return verify_constants(clazz) verify_enums(clazz) @@ -1397,14 +1952,19 @@ def examine_clazz(clazz): verify_tense(clazz) verify_icu(clazz) verify_clone(clazz) + verify_pfd(clazz) + verify_numbers(clazz) + verify_singleton(clazz) -def examine_stream(stream): +def examine_stream(stream, base_stream=None, in_classes_with_base=[], out_classes_with_base=None): """Find all style issues in the given API stream.""" global failures, noticed failures = {} noticed = {} - _parse_stream(stream, examine_clazz) + _parse_stream(stream, examine_clazz, base_f=base_stream, + in_classes_with_base=in_classes_with_base, + out_classes_with_base=out_classes_with_base) return (failures, noticed) @@ -1479,6 +2039,7 @@ def show_deprecations_at_birth(cur, prev): # Remove all existing things so we're left with new for prev_clazz in prev.values(): cur_clazz = cur[prev_clazz.fullname] + if not is_interesting(cur_clazz): continue sigs = { i.ident: i for i in prev_clazz.ctors } cur_clazz.ctors = [ i for i in cur_clazz.ctors if i.ident not in sigs ] @@ -1492,11 +2053,11 @@ def show_deprecations_at_birth(cur, prev): del cur[prev_clazz.fullname] for clazz in cur.values(): - if " deprecated " in clazz.raw and not clazz.fullname in prev: + if "deprecated" in clazz.split and not clazz.fullname in prev: error(clazz, None, None, "Found API deprecation at birth") for i in clazz.ctors + clazz.methods + clazz.fields: - if " deprecated " in i.raw: + if "deprecated" in i.split: error(clazz, i, None, "Found API deprecation at birth") print "%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), @@ -1506,12 +2067,50 @@ def show_deprecations_at_birth(cur, prev): print +def show_stats(cur, prev): + """Show API stats.""" + + stats = collections.defaultdict(int) + for cur_clazz in cur.values(): + if not is_interesting(cur_clazz): continue + + if cur_clazz.fullname not in prev: + stats['new_classes'] += 1 + stats['new_ctors'] += len(cur_clazz.ctors) + stats['new_methods'] += len(cur_clazz.methods) + stats['new_fields'] += len(cur_clazz.fields) + else: + prev_clazz = prev[cur_clazz.fullname] + + sigs = { i.ident: i for i in prev_clazz.ctors } + ctors = len([ i for i in cur_clazz.ctors if i.ident not in sigs ]) + sigs = { i.ident: i for i in prev_clazz.methods } + methods = len([ i for i in cur_clazz.methods if i.ident not in sigs ]) + sigs = { i.ident: i for i in prev_clazz.fields } + fields = len([ i for i in cur_clazz.fields if i.ident not in sigs ]) + + if ctors + methods + fields > 0: + stats['extend_classes'] += 1 + stats['extend_ctors'] += ctors + stats['extend_methods'] += methods + stats['extend_fields'] += fields + + print "#", "".join([ k.ljust(20) for k in sorted(stats.keys()) ]) + print " ", "".join([ str(stats[k]).ljust(20) for k in sorted(stats.keys()) ]) + + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Enforces common Android public API design \ patterns. It ignores lint messages from a previous API level, if provided.") parser.add_argument("current.txt", type=argparse.FileType('r'), help="current.txt") parser.add_argument("previous.txt", nargs='?', type=argparse.FileType('r'), default=None, help="previous.txt") + parser.add_argument("--base-current", nargs='?', type=argparse.FileType('r'), default=None, + help="The base current.txt to use when examining system-current.txt or" + " test-current.txt") + parser.add_argument("--base-previous", nargs='?', type=argparse.FileType('r'), default=None, + help="The base previous.txt to use when examining system-previous.txt or" + " test-previous.txt") parser.add_argument("--no-color", action='store_const', const=True, help="Disable terminal colors") parser.add_argument("--allow-google", action='store_const', const=True, @@ -1520,6 +2119,8 @@ if __name__ == "__main__": help="Show API changes noticed") parser.add_argument("--show-deprecations-at-birth", action='store_const', const=True, help="Show API deprecations at birth") + parser.add_argument("--show-stats", action='store_const', const=True, + help="Show API stats") args = vars(parser.parse_args()) if args['no_color']: @@ -1529,7 +2130,9 @@ if __name__ == "__main__": ALLOW_GOOGLE = True current_file = args['current.txt'] + base_current_file = args['base_current'] previous_file = args['previous.txt'] + base_previous_file = args['base_previous'] if args['show_deprecations_at_birth']: with current_file as f: @@ -1539,11 +2142,32 @@ if __name__ == "__main__": show_deprecations_at_birth(cur, prev) sys.exit() + if args['show_stats']: + with current_file as f: + cur = _parse_stream(f) + with previous_file as f: + prev = _parse_stream(f) + show_stats(cur, prev) + sys.exit() + + classes_with_base = [] + with current_file as f: - cur_fail, cur_noticed = examine_stream(f) + if base_current_file: + with base_current_file as base_f: + cur_fail, cur_noticed = examine_stream(f, base_f, + out_classes_with_base=classes_with_base) + else: + cur_fail, cur_noticed = examine_stream(f, out_classes_with_base=classes_with_base) + if not previous_file is None: with previous_file as f: - prev_fail, prev_noticed = examine_stream(f) + if base_previous_file: + with base_previous_file as base_f: + prev_fail, prev_noticed = examine_stream(f, base_f, + in_classes_with_base=classes_with_base) + else: + prev_fail, prev_noticed = examine_stream(f, in_classes_with_base=classes_with_base) # ignore errors from previous API level for p in prev_fail: diff --git a/tools/apilint/apilint_sha_system.sh b/tools/apilint/apilint_sha_system.sh new file mode 100755 index 000000000000..8538a3d904f5 --- /dev/null +++ b/tools/apilint/apilint_sha_system.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if git show --name-only --pretty=format: $1 | grep api/ > /dev/null; then + python tools/apilint/apilint.py \ + --base-current <(git show $1:api/current.txt) \ + --base-previous <(git show $1^:api/current.txt) \ + <(git show $1:api/system-current.txt) \ + <(git show $1^:api/system-current.txt) +fi diff --git a/tools/apilint/apilint_stats.sh b/tools/apilint/apilint_stats.sh new file mode 100755 index 000000000000..052d9a5265fe --- /dev/null +++ b/tools/apilint/apilint_stats.sh @@ -0,0 +1,7 @@ +#!/bin/bash +API=28 +while [ $API -gt 14 ]; do + echo "# Changes in API $((API))" + python tools/apilint/apilint.py --show-stats ../../prebuilts/sdk/$((API))/public/api/android.txt ../../prebuilts/sdk/$((API-1))/public/api/android.txt + let API=API-1 +done diff --git a/tools/apilint/apilint_test.py b/tools/apilint/apilint_test.py new file mode 100644 index 000000000000..3716bf9f5d6f --- /dev/null +++ b/tools/apilint/apilint_test.py @@ -0,0 +1,373 @@ +#!/usr/bin/env python + +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +import apilint + +def cls(pkg, name): + return apilint.Class(apilint.Package(999, "package %s {" % pkg, None), 999, + "public final class %s {" % name, None) + +_ri = apilint._retry_iterator + +c1 = cls("android.app", "ActivityManager") +c2 = cls("android.app", "Notification") +c3 = cls("android.app", "Notification.Action") +c4 = cls("android.graphics", "Bitmap") + +class UtilTests(unittest.TestCase): + def test_retry_iterator(self): + it = apilint._retry_iterator([1, 2, 3, 4]) + self.assertEqual(it.next(), 1) + self.assertEqual(it.next(), 2) + self.assertEqual(it.next(), 3) + it.send("retry") + self.assertEqual(it.next(), 3) + self.assertEqual(it.next(), 4) + with self.assertRaises(StopIteration): + it.next() + + def test_retry_iterator_one(self): + it = apilint._retry_iterator([1]) + self.assertEqual(it.next(), 1) + it.send("retry") + self.assertEqual(it.next(), 1) + with self.assertRaises(StopIteration): + it.next() + + def test_retry_iterator_one(self): + it = apilint._retry_iterator([1]) + self.assertEqual(it.next(), 1) + it.send("retry") + self.assertEqual(it.next(), 1) + with self.assertRaises(StopIteration): + it.next() + + def test_skip_to_matching_class_found(self): + it = _ri([c1, c2, c3, c4]) + self.assertEquals(apilint._skip_to_matching_class(it, c3), + c3) + self.assertEqual(it.next(), c4) + + def test_skip_to_matching_class_not_found(self): + it = _ri([c1, c2, c3, c4]) + self.assertEquals(apilint._skip_to_matching_class(it, cls("android.content", "ContentProvider")), + None) + self.assertEqual(it.next(), c4) + + def test_yield_until_matching_class_found(self): + it = _ri([c1, c2, c3, c4]) + self.assertEquals(list(apilint._yield_until_matching_class(it, c3)), + [c1, c2]) + self.assertEqual(it.next(), c4) + + def test_yield_until_matching_class_not_found(self): + it = _ri([c1, c2, c3, c4]) + self.assertEquals(list(apilint._yield_until_matching_class(it, cls("android.content", "ContentProvider"))), + [c1, c2, c3]) + self.assertEqual(it.next(), c4) + + def test_yield_until_matching_class_None(self): + it = _ri([c1, c2, c3, c4]) + self.assertEquals(list(apilint._yield_until_matching_class(it, None)), + [c1, c2, c3, c4]) + + +faulty_current_txt = """ +package android.app { + public final class Activity { + } + + public final class WallpaperColors implements android.os.Parcelable { + ctor public WallpaperColors(android.os.Parcel); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR; + } +} +""".split('\n') + +ok_current_txt = """ +package android.app { + public final class Activity { + } + + public final class WallpaperColors implements android.os.Parcelable { + ctor public WallpaperColors(); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR; + } +} +""".split('\n') + +system_current_txt = """ +package android.app { + public final class WallpaperColors implements android.os.Parcelable { + method public int getSomething(); + } +} +""".split('\n') + + + +class BaseFileTests(unittest.TestCase): + def test_base_file_avoids_errors(self): + failures, _ = apilint.examine_stream(system_current_txt, ok_current_txt) + self.assertEquals(failures, {}) + + def test_class_with_base_finds_same_errors(self): + failures_with_classes_with_base, _ = apilint.examine_stream("", faulty_current_txt, + in_classes_with_base=[cls("android.app", "WallpaperColors")]) + failures_with_system_txt, _ = apilint.examine_stream(system_current_txt, faulty_current_txt) + + self.assertEquals(failures_with_classes_with_base.keys(), failures_with_system_txt.keys()) + + def test_classes_with_base_is_emited(self): + classes_with_base = [] + _, _ = apilint.examine_stream(system_current_txt, faulty_current_txt, + out_classes_with_base=classes_with_base) + self.assertEquals(map(lambda x: x.fullname, classes_with_base), ["android.app.WallpaperColors"]) + +class ParseV2Stream(unittest.TestCase): + def test_field_kinds(self): + api = apilint._parse_stream(""" +// Signature format: 2.0 +package android { + public enum SomeEnum { + enum_constant public static final android.SomeEnum ENUM_CONST; + field public static final int FIELD_CONST; + property public final int someProperty; + ctor public SomeEnum(); + method public Object? getObject(); + } +} + """.strip().split('\n')) + + self.assertEquals(api['android.SomeEnum'].fields[0].split[0], 'enum_constant') + self.assertEquals(api['android.SomeEnum'].fields[1].split[0], 'field') + self.assertEquals(api['android.SomeEnum'].fields[2].split[0], 'property') + self.assertEquals(api['android.SomeEnum'].ctors[0].split[0], 'ctor') + self.assertEquals(api['android.SomeEnum'].methods[0].split[0], 'method') + +class ParseV3Stream(unittest.TestCase): + def test_field_kinds(self): + api = apilint._parse_stream(""" +// Signature format: 3.0 +package a { + + public final class ContextKt { + method public static inline <reified T> T! getSystemService(android.content.Context); + method public static inline void withStyledAttributes(android.content.Context, android.util.AttributeSet? set = null, int[] attrs, @AttrRes int defStyleAttr = 0, @StyleRes int defStyleRes = 0, kotlin.jvm.functions.Function1<? super android.content.res.TypedArray,kotlin.Unit> block); + } +} + """.strip().split('\n')) + self.assertEquals(api['a.ContextKt'].methods[0].name, 'getSystemService') + self.assertEquals(api['a.ContextKt'].methods[0].split[:4], ['method', 'public', 'static', 'inline']) + self.assertEquals(api['a.ContextKt'].methods[1].name, 'withStyledAttributes') + self.assertEquals(api['a.ContextKt'].methods[1].split[:4], ['method', 'public', 'static', 'inline']) + +class V2TokenizerTests(unittest.TestCase): + def _test(self, raw, expected): + self.assertEquals(apilint.V2Tokenizer(raw).tokenize(), expected) + + def test_simple(self): + self._test(" method public some.Type someName(some.Argument arg, int arg);", + ['method', 'public', 'some.Type', 'someName', '(', 'some.Argument', + 'arg', ',', 'int', 'arg', ')', ';']) + self._test("class Some.Class extends SomeOther {", + ['class', 'Some.Class', 'extends', 'SomeOther', '{']) + + def test_varargs(self): + self._test("name(String...)", + ['name', '(', 'String', '...', ')']) + + def test_kotlin(self): + self._test("String? name(String!...)", + ['String', '?', 'name', '(', 'String', '!', '...', ')']) + + def test_annotation(self): + self._test("method @Nullable public void name();", + ['method', '@', 'Nullable', 'public', 'void', 'name', '(', ')', ';']) + + def test_annotation_args(self): + self._test("@Some(val=1, other=2) class Class {", + ['@', 'Some', '(', 'val', '=', '1', ',', 'other', '=', '2', ')', + 'class', 'Class', '{']) + def test_comment(self): + self._test("some //comment", ['some']) + + def test_strings(self): + self._test(r'"" "foo" "\"" "\\"', ['""', '"foo"', r'"\""', r'"\\"']) + + def test_at_interface(self): + self._test("public @interface Annotation {", + ['public', '@interface', 'Annotation', '{']) + + def test_array_type(self): + self._test("int[][]", ['int', '[]', '[]']) + + def test_generics(self): + self._test("<>foobar<A extends Object>", + ['<', '>', 'foobar', '<', 'A', 'extends', 'Object', '>']) + +class V2ParserTests(unittest.TestCase): + def _cls(self, raw): + pkg = apilint.Package(999, "package pkg {", None) + return apilint.Class(pkg, 1, raw, '', sig_format=2) + + def _method(self, raw, cls=None): + if not cls: + cls = self._cls("class Class {") + return apilint.Method(cls, 1, raw, '', sig_format=2) + + def _field(self, raw): + cls = self._cls("class Class {") + return apilint.Field(cls, 1, raw, '', sig_format=2) + + def test_class(self): + cls = self._cls("@Deprecated @IntRange(from=1, to=2) public static abstract class Some.Name extends Super<Class> implements Interface<Class> {") + self.assertTrue('deprecated' in cls.split) + self.assertTrue('static' in cls.split) + self.assertTrue('abstract' in cls.split) + self.assertTrue('class' in cls.split) + self.assertEquals('Super', cls.extends) + self.assertEquals('Interface', cls.implements) + self.assertEquals('pkg.Some.Name', cls.fullname) + + def test_enum(self): + cls = self._cls("public enum Some.Name {") + self._field("enum_constant public static final android.ValueType COLOR;") + + def test_interface(self): + cls = self._cls("@Deprecated @IntRange(from=1, to=2) public interface Some.Name extends Interface<Class> {") + self.assertTrue('deprecated' in cls.split) + self.assertTrue('interface' in cls.split) + self.assertEquals('Interface', cls.extends) + self.assertEquals('Interface', cls.implements) + self.assertEquals('pkg.Some.Name', cls.fullname) + + def test_at_interface(self): + cls = self._cls("@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface SuppressLint {") + self.assertTrue('@interface' in cls.split) + self.assertEquals('pkg.SuppressLint', cls.fullname) + + def test_parse_method(self): + m = self._method("method @Deprecated public static native <T> Class<T>[][] name(" + + "Class<T[]>[][], Class<T[][][]>[][]...) throws Exception, T;") + self.assertTrue('static' in m.split) + self.assertTrue('public' in m.split) + self.assertTrue('method' in m.split) + self.assertTrue('native' in m.split) + self.assertTrue('deprecated' in m.split) + self.assertEquals('java.lang.Class[][]', m.typ) + self.assertEquals('name', m.name) + self.assertEquals(['java.lang.Class[][]', 'java.lang.Class[][]...'], m.args) + self.assertEquals(['java.lang.Exception', 'T'], m.throws) + + def test_ctor(self): + m = self._method("ctor @Deprecated <T> ClassName();") + self.assertTrue('ctor' in m.split) + self.assertTrue('deprecated' in m.split) + self.assertEquals('ctor', m.typ) + self.assertEquals('ClassName', m.name) + + def test_parse_annotation_method(self): + cls = self._cls("@interface Annotation {") + self._method('method abstract String category() default "";', cls=cls) + self._method('method abstract boolean deepExport() default false;', cls=cls) + self._method('method abstract ViewDebug.FlagToString[] flagMapping() default {};', cls=cls) + self._method('method abstract ViewDebug.FlagToString[] flagMapping() default (double)java.lang.Float.NEGATIVE_INFINITY;', cls=cls) + + def test_parse_string_field(self): + f = self._field('field @Deprecated public final String SOME_NAME = "value";') + self.assertTrue('field' in f.split) + self.assertTrue('deprecated' in f.split) + self.assertTrue('final' in f.split) + self.assertEquals('java.lang.String', f.typ) + self.assertEquals('SOME_NAME', f.name) + self.assertEquals('value', f.value) + + def test_parse_field(self): + f = self._field('field public Object SOME_NAME;') + self.assertTrue('field' in f.split) + self.assertEquals('java.lang.Object', f.typ) + self.assertEquals('SOME_NAME', f.name) + self.assertEquals(None, f.value) + + def test_parse_int_field(self): + f = self._field('field public int NAME = 123;') + self.assertTrue('field' in f.split) + self.assertEquals('int', f.typ) + self.assertEquals('NAME', f.name) + self.assertEquals('123', f.value) + + def test_parse_quotient_field(self): + f = self._field('field public int NAME = (0.0/0.0);') + self.assertTrue('field' in f.split) + self.assertEquals('int', f.typ) + self.assertEquals('NAME', f.name) + self.assertEquals('( 0.0 / 0.0 )', f.value) + + def test_kotlin_types(self): + self._field('field public List<Integer[]?[]!>?[]![]? NAME;') + self._method("method <T?> Class<T!>?[]![][]? name(Type!, Type argname," + + "Class<T?>[][]?[]!...!) throws Exception, T;") + self._method("method <T> T name(T a = 1, T b = A(1), Lambda f = { false }, N? n = null, " + + """double c = (1/0), float d = 1.0f, String s = "heyo", char c = 'a');""") + + def test_kotlin_operator(self): + self._method('method public operator void unaryPlus(androidx.navigation.NavDestination);') + self._method('method public static operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, @IdRes int id);') + self._method('method public static operator <T> T get(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<T> clazz);') + + def test_kotlin_property(self): + self._field('property public VM value;') + self._field('property public final String? action;') + + def test_kotlin_varargs(self): + self._method('method public void error(int p = "42", Integer int2 = "null", int p1 = "42", vararg String args);') + + def test_kotlin_default_values(self): + self._method('method public void foo(String! = null, String! = "Hello World", int = 42);') + self._method('method void method(String, String firstArg = "hello", int secondArg = "42", String thirdArg = "world");') + self._method('method void method(String, String firstArg = "hello", int secondArg = "42");') + self._method('method void method(String, String firstArg = "hello");') + self._method('method void edit(android.Type, boolean commit = false, Function1<? super Editor,kotlin.Unit> action);') + self._method('method <K, V> LruCache<K,V> lruCache(int maxSize, Function2<? super K,? super V,java.lang.Integer> sizeOf = { _, _ -> 1 }, Function1<? extends V> create = { (V)null }, Function4<kotlin.Unit> onEntryRemoved = { _, _, _, _ -> });') + self._method('method android.Bitmap? drawToBitmap(android.View, android.Config config = android.graphics.Bitmap.Config.ARGB_8888);') + self._method('method void emptyLambda(Function0<kotlin.Unit> sizeOf = {});') + self._method('method void method1(int p = 42, Integer? int2 = null, int p1 = 42, String str = "hello world", java.lang.String... args);') + self._method('method void method2(int p, int int2 = (2 * int) * some.other.pkg.Constants.Misc.SIZE);') + self._method('method void method3(String str, int p, int int2 = double(int) + str.length);') + self._method('method void print(test.pkg.Foo foo = test.pkg.Foo());') + + def test_type_use_annotation(self): + self._method('method public static int codePointAt(char @NonNull [], int);') + self._method('method @NonNull public java.util.Set<java.util.Map.@NonNull Entry<K,V>> entrySet();') + + m = self._method('method @NonNull public java.lang.annotation.@NonNull Annotation @NonNull [] getAnnotations();') + self.assertEquals('java.lang.annotation.Annotation[]', m.typ) + + m = self._method('method @NonNull public abstract java.lang.annotation.@NonNull Annotation @NonNull [] @NonNull [] getParameterAnnotations();') + self.assertEquals('java.lang.annotation.Annotation[][]', m.typ) + + m = self._method('method @NonNull public @NonNull String @NonNull [] split(@NonNull String, int);') + self.assertEquals('java.lang.String[]', m.typ) + +if __name__ == "__main__": + unittest.main() diff --git a/tools/bit/command.h b/tools/bit/command.h index fb44900b0806..dd7103e10fe7 100644 --- a/tools/bit/command.h +++ b/tools/bit/command.h @@ -25,7 +25,7 @@ using namespace std; struct Command { - Command(const string& prog); + explicit Command(const string& prog); ~Command(); void AddArg(const string& arg); diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py index a8411aa5c488..ce9becd40b1e 100755 --- a/tools/fonts/fontchain_linter.py +++ b/tools/fonts/fontchain_linter.py @@ -524,9 +524,12 @@ LEGACY_ANDROID_EMOJI = { 0xFE837: (ord('0'), COMBINING_KEYCAP), } +# This is used to define the emoji that should have the same glyph. +# i.e. previously we had gender based Kiss (0x1F48F), which had the same glyph +# with Kiss: Woman, Man (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468) +# in that case a valid row would be: +# (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468): 0x1F48F, ZWJ_IDENTICALS = { - # KISS - (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468): 0x1F48F, } SAME_FLAG_MAPPINGS = [ diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh new file mode 100755 index 000000000000..4ffcf6846947 --- /dev/null +++ b/tools/hiddenapi/exclude.sh @@ -0,0 +1,58 @@ +#!/bin/bash +set -e +# Make sure that entries are not added for packages that are already fully handled using +# annotations. +LOCAL_DIR="$( dirname ${BASH_SOURCE} )" +# Each team should add a <team>_PACKAGES and <team>_EMAIL with the list of packages and +# the team email to use in the event of this detecting an entry in a <team> package. Also +# add <team> to the TEAMS list. +LIBCORE_PACKAGES="\ + android.icu \ + android.system \ + com.android.bouncycastle \ + com.android.conscrypt \ + com.android.i18n.phonenumbers \ + com.android.okhttp \ + com.sun \ + dalvik \ + java \ + javax \ + libcore \ + org.apache.harmony \ + org.json \ + org.w3c.dom \ + org.xml.sax \ + sun \ + " +LIBCORE_EMAIL=libcore-team@android.com + +# List of teams. +TEAMS=LIBCORE + +# Generate the list of packages and convert to a regular expression. +PACKAGES=$(for t in $TEAMS; do echo $(eval echo \${${t}_PACKAGES}); done) +RE=$(echo ${PACKAGES} | sed "s/ /|/g") +git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do + ENTRIES=$(grep -E "^L(${RE})/" <(git show $1:$file)) + if [[ -n "${ENTRIES}" ]]; then + echo -e "\e[1m\e[31m$file $1 contains the following entries\e[0m" + echo -e "\e[1m\e[31mfor packages that are handled using UnsupportedAppUsage. Please remove\e[0m" + echo -e "\e[1m\e[31mthese entries and add annotations instead.\e[0m" + # Partition the entries by team and provide contact details to aid in fixing the issue. + for t in ${TEAMS} + do + PACKAGES=$(eval echo \${${t}_PACKAGES}) + RE=$(echo ${PACKAGES} | sed "s/ /|/g") + TEAM_ENTRIES=$(grep -E "^L(${RE})/" <(echo "${ENTRIES}")) + if [[ -n "${TEAM_ENTRIES}" ]]; then + EMAIL=$(eval echo \${${t}_EMAIL}) + echo -e "\e[33mContact ${EMAIL} or compat- for help with the following:\e[0m" + for i in ${ENTRIES} + do + echo -e "\e[33m ${i}\e[0m" + done + fi + done + exit 1 + fi +done diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py index fdc800bcc177..2a8f69525eb8 100755 --- a/tools/hiddenapi/generate_hiddenapi_lists.py +++ b/tools/hiddenapi/generate_hiddenapi_lists.py @@ -15,23 +15,61 @@ # limitations under the License. """ Generate API lists for non-SDK API enforcement. - -usage: generate-hiddenapi-lists.py [-h] - --input-public INPUT_PUBLIC - --input-private INPUT_PRIVATE - [--input-whitelists [INPUT_WHITELISTS [INPUT_WHITELISTS ...]]] - [--input-greylists [INPUT_GREYLISTS [INPUT_GREYLISTS ...]]] - [--input-blacklists [INPUT_BLACKLISTS [INPUT_BLACKLISTS ...]]] - --output-whitelist OUTPUT_WHITELIST - --output-light-greylist OUTPUT_LIGHT_GREYLIST - --output-dark-greylist OUTPUT_DARK_GREYLIST - --output-blacklist OUTPUT_BLACKLIST """ import argparse +from collections import defaultdict import os import sys import re +# Names of flags recognized by the `hiddenapi` tool. +FLAG_WHITELIST = "whitelist" +FLAG_GREYLIST = "greylist" +FLAG_BLACKLIST = "blacklist" +FLAG_GREYLIST_MAX_O = "greylist-max-o" +FLAG_GREYLIST_MAX_P = "greylist-max-p" +FLAG_CORE_PLATFORM_API = "core-platform-api" + +# List of all known flags. +FLAGS_API_LIST = [ + FLAG_WHITELIST, + FLAG_GREYLIST, + FLAG_BLACKLIST, + FLAG_GREYLIST_MAX_O, + FLAG_GREYLIST_MAX_P, +] +ALL_FLAGS = FLAGS_API_LIST + [ FLAG_CORE_PLATFORM_API ] + +FLAGS_API_LIST_SET = set(FLAGS_API_LIST) +ALL_FLAGS_SET = set(ALL_FLAGS) + +# Suffix used in command line args to express that only known and +# otherwise unassigned entries should be assign the given flag. +# For example, the P dark greylist is checked in as it was in P, +# but signatures have changes since then. The flag instructs this +# script to skip any entries which do not exist any more. +FLAG_IGNORE_CONFLICTS_SUFFIX = "-ignore-conflicts" + +# Regex patterns of fields/methods used in serialization. These are +# considered public API despite being hidden. +SERIALIZATION_PATTERNS = [ + r'readObject\(Ljava/io/ObjectInputStream;\)V', + r'readObjectNoData\(\)V', + r'readResolve\(\)Ljava/lang/Object;', + r'serialVersionUID:J', + r'serialPersistentFields:\[Ljava/io/ObjectStreamField;', + r'writeObject\(Ljava/io/ObjectOutputStream;\)V', + r'writeReplace\(\)Ljava/lang/Object;', +] + +# Single regex used to match serialization API. It combines all the +# SERIALIZATION_PATTERNS into a single regular expression. +SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$') + +# Predicates to be used with filter_apis. +HAS_NO_API_LIST_ASSIGNED = lambda api, flags: not FLAGS_API_LIST_SET.intersection(flags) +IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api) + def get_args(): """Parses command line arguments. @@ -39,21 +77,19 @@ def get_args(): Namespace: dictionary of parsed arguments """ parser = argparse.ArgumentParser() - parser.add_argument('--input-public', required=True, help='List of all public members') - parser.add_argument('--input-private', required=True, help='List of all private members') - parser.add_argument( - '--input-whitelists', nargs='*', - help='Lists of members to force on whitelist') - parser.add_argument( - '--input-greylists', nargs='*', - help='Lists of members to force on light greylist') - parser.add_argument( - '--input-blacklists', nargs='*', - help='Lists of members to force on blacklist') - parser.add_argument('--output-whitelist', required=True) - parser.add_argument('--output-light-greylist', required=True) - parser.add_argument('--output-dark-greylist', required=True) - parser.add_argument('--output-blacklist', required=True) + parser.add_argument('--output', required=True) + parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE', + help='CSV files to be merged into output') + + for flag in ALL_FLAGS: + ignore_conflicts_flag = flag + FLAG_IGNORE_CONFLICTS_SUFFIX + parser.add_argument('--' + flag, dest=flag, nargs='*', default=[], metavar='TXT_FILE', + help='lists of entries with flag "' + flag + '"') + parser.add_argument('--' + ignore_conflicts_flag, dest=ignore_conflicts_flag, nargs='*', + default=[], metavar='TXT_FILE', + help='lists of entries with flag "' + flag + + '". skip entry if missing or flag conflict.') + return parser.parse_args() def read_lines(filename): @@ -65,10 +101,13 @@ def read_lines(filename): filename (string): Path to the file to read from. Returns: - list: Lines of the loaded file as a list of strings. + Lines of the file as a list of string. """ with open(filename, 'r') as f: - return filter(lambda line: not line.startswith('#'), f.readlines()) + lines = f.readlines(); + lines = filter(lambda line: not line.startswith('#'), lines) + lines = map(lambda line: line.strip(), lines) + return set(lines) def write_lines(filename, lines): """Writes list of lines into a file, overwriting the file it it exists. @@ -77,167 +116,155 @@ def write_lines(filename, lines): filename (string): Path to the file to be writting into. lines (list): List of strings to write into the file. """ + lines = map(lambda line: line + '\n', lines) with open(filename, 'w') as f: f.writelines(lines) -def move_between_sets(subset, src, dst, source = "<unknown>"): - """Removes a subset of elements from one set and add it to another. - - Args: - subset (set): The subset of `src` to be moved from `src` to `dst`. - src (set): Source set. Must be a superset of `subset`. - dst (set): Destination set. Must be disjoint with `subset`. - """ - assert src.issuperset(subset), ( - "Error processing: {}\n" - "The following entries were not found:\n" - "{}" - "Please visit go/hiddenapi for more information.").format( - source, "".join(map(lambda x: " " + str(x), subset.difference(src)))) - assert dst.isdisjoint(subset) - # Order matters if `src` and `subset` are the same object. - dst.update(subset) - src.difference_update(subset) - -def get_package_name(signature): - """Returns the package name prefix of a class member signature. - - Example: "Ljava/lang/String;->hashCode()J" --> "Ljava/lang/" - - Args: - signature (string): Member signature - - Returns - string: Package name of the given member - """ - class_name_end = signature.find("->") - assert class_name_end != -1, "Invalid signature: {}".format(signature) - package_name_end = signature.rfind("/", 0, class_name_end) - assert package_name_end != -1, "Invalid signature: {}".format(signature) - return signature[:package_name_end + 1] - -def all_package_names(*args): - """Returns a set of packages names in given lists of member signatures. - - Example: args = [ set([ "Lpkg1/ClassA;->foo()V", "Lpkg2/ClassB;->bar()J" ]), - set([ "Lpkg1/ClassC;->baz()Z" ]) ] - return value = set([ "Lpkg1/", "Lpkg2" ]) - - Args: - *args (list): List of sets to iterate over and extract the package names - of its elements (member signatures) - - Returns: - set: All package names extracted from the given lists of signatures. - """ - packages = set() - for arg in args: - packages = packages.union(map(get_package_name, arg)) - return packages - -def move_all(src, dst): - """Moves all elements of one set to another. - - Args: - src (set): Source set. Will become empty. - dst (set): Destination set. Will contain all elements of `src`. - """ - move_between_sets(src, src, dst) - -def move_from_files(filenames, src, dst): - """Loads member signatures from a list of files and moves them to a given set. - - Opens files in `filenames`, reads all their lines and moves those from `src` - set to `dst` set. - - Args: - filenames (list): List of paths to files to be loaded. - src (set): Set that loaded lines should be moved from. - dst (set): Set that loaded lines should be moved to. - """ - if filenames: - for filename in filenames: - move_between_sets(set(read_lines(filename)), src, dst, filename) - -def move_serialization(src, dst): - """Moves all members matching serialization API signatures between given sets. - - Args: - src (set): Set that will be searched for serialization API and that API - will be removed from it. - dst (set): Set that serialization API will be moved to. - """ - serialization_patterns = [ - r'readObject\(Ljava/io/ObjectInputStream;\)V', - r'readObjectNoData\(\)V', - r'readResolve\(\)Ljava/lang/Object;', - r'serialVersionUID:J', - r'serialPersistentFields:\[Ljava/io/ObjectStreamField;', - r'writeObject\(Ljava/io/ObjectOutputStream;\)V', - r'writeReplace\(\)Ljava/lang/Object;', - ] - regex = re.compile(r'.*->(' + '|'.join(serialization_patterns) + r')$') - move_between_sets(filter(lambda api: regex.match(api), src), src, dst) - -def move_from_packages(packages, src, dst): - """Moves all members of given package names from one set to another. - - Args: - packages (list): List of string package names. - src (set): Set that will be searched for API matching one of the given - package names. Surch API will be removed from the set. - dst (set): Set that matching API will be moved to. - """ - move_between_sets(filter(lambda api: get_package_name(api) in packages, src), src, dst) +class FlagsDict: + def __init__(self): + self._dict_keyset = set() + self._dict = defaultdict(set) + + def _check_entries_set(self, keys_subset, source): + assert isinstance(keys_subset, set) + assert keys_subset.issubset(self._dict_keyset), ( + "Error processing: {}\n" + "The following entries were unexpected:\n" + "{}" + "Please visit go/hiddenapi for more information.").format( + source, "".join(map(lambda x: " " + str(x), keys_subset - self._dict_keyset))) + + def _check_flags_set(self, flags_subset, source): + assert isinstance(flags_subset, set) + assert flags_subset.issubset(ALL_FLAGS_SET), ( + "Error processing: {}\n" + "The following flags were not recognized: \n" + "{}\n" + "Please visit go/hiddenapi for more information.").format( + source, "\n".join(flags_subset - ALL_FLAGS_SET)) + + def filter_apis(self, filter_fn): + """Returns APIs which match a given predicate. + + This is a helper function which allows to filter on both signatures (keys) and + flags (values). The built-in filter() invokes the lambda only with dict's keys. + + Args: + filter_fn : Function which takes two arguments (signature/flags) and returns a boolean. + + Returns: + A set of APIs which match the predicate. + """ + return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset)) + + def get_valid_subset_of_unassigned_apis(self, api_subset): + """Sanitizes a key set input to only include keys which exist in the dictionary + and have not been assigned any API list flags. + + Args: + entries_subset (set/list): Key set to be sanitized. + + Returns: + Sanitized key set. + """ + assert isinstance(api_subset, set) + return api_subset.intersection(self.filter_apis(HAS_NO_API_LIST_ASSIGNED)) + + def generate_csv(self): + """Constructs CSV entries from a dictionary. + + Returns: + List of lines comprising a CSV file. See "parse_and_merge_csv" for format description. + """ + return sorted(map(lambda api: ",".join([api] + sorted(self._dict[api])), self._dict)) + + def parse_and_merge_csv(self, csv_lines, source = "<unknown>"): + """Parses CSV entries and merges them into a given dictionary. + + The expected CSV format is: + <api signature>,<flag1>,<flag2>,...,<flagN> + + Args: + csv_lines (list of strings): Lines read from a CSV file. + source (string): Origin of `csv_lines`. Will be printed in error messages. + + Throws: + AssertionError if parsed flags are invalid. + """ + # Split CSV lines into arrays of values. + csv_values = [ line.split(',') for line in csv_lines ] + + # Update the full set of API signatures. + self._dict_keyset.update([ csv[0] for csv in csv_values ]) + + # Check that all flags are known. + csv_flags = set(reduce(lambda x, y: set(x).union(y), [ csv[1:] for csv in csv_values ], [])) + self._check_flags_set(csv_flags, source) + + # Iterate over all CSV lines, find entry in dict and append flags to it. + for csv in csv_values: + self._dict[csv[0]].update(csv[1:]) + + def assign_flag(self, flag, apis, source="<unknown>"): + """Assigns a flag to given subset of entries. + + Args: + flag (string): One of ALL_FLAGS. + apis (set): Subset of APIs to recieve the flag. + source (string): Origin of `entries_subset`. Will be printed in error messages. + + Throws: + AssertionError if parsed API signatures of flags are invalid. + """ + # Check that all APIs exist in the dict. + self._check_entries_set(apis, source) + + # Check that the flag is known. + self._check_flags_set(set([ flag ]), source) + + # Iterate over the API subset, find each entry in dict and assign the flag to it. + for api in apis: + self._dict[api].add(flag) def main(argv): - args = get_args() - - # Initialize API sets by loading lists of public and private API. Public API - # are all members resolvable from SDK API stubs, other members are private. - # As an optimization, skip the step of moving public API from a full set of - # members and start with a populated whitelist. - whitelist = set(read_lines(args.input_public)) - uncategorized = set(read_lines(args.input_private)) - light_greylist = set() - dark_greylist = set() - blacklist = set() - - # Assert that there is no overlap between public and private API. - assert whitelist.isdisjoint(uncategorized) - num_all_api = len(whitelist) + len(uncategorized) - - # Read all files which manually assign members to specific lists. - move_from_files(args.input_whitelists, uncategorized, whitelist) - move_from_files(args.input_greylists, uncategorized, light_greylist) - move_from_files(args.input_blacklists, uncategorized, blacklist) - - # Iterate over all uncategorized members and move serialization API to whitelist. - move_serialization(uncategorized, whitelist) - - # Extract package names of members from whitelist and light greylist, which - # are assumed to have been finalized at this point. Assign all uncategorized - # members from the same packages to the dark greylist. - dark_greylist_packages = all_package_names(whitelist, light_greylist) - move_from_packages(dark_greylist_packages, uncategorized, dark_greylist) - - # Assign all uncategorized members to the blacklist. - move_all(uncategorized, blacklist) - - # Assert we have not missed anything. - assert whitelist.isdisjoint(light_greylist) - assert whitelist.isdisjoint(dark_greylist) - assert whitelist.isdisjoint(blacklist) - assert light_greylist.isdisjoint(dark_greylist) - assert light_greylist.isdisjoint(blacklist) - assert dark_greylist.isdisjoint(blacklist) - assert num_all_api == len(whitelist) + len(light_greylist) + len(dark_greylist) + len(blacklist) - - # Write final lists to disk. - write_lines(args.output_whitelist, whitelist) - write_lines(args.output_light_greylist, light_greylist) - write_lines(args.output_dark_greylist, dark_greylist) - write_lines(args.output_blacklist, blacklist) + # Parse arguments. + args = vars(get_args()) + + # Initialize API->flags dictionary. + flags = FlagsDict() + + # Merge input CSV files into the dictionary. + # Do this first because CSV files produced by parsing API stubs will + # contain the full set of APIs. Subsequent additions from text files + # will be able to detect invalid entries, and/or filter all as-yet + # unassigned entries. + for filename in args["csv"]: + flags.parse_and_merge_csv(read_lines(filename), filename) + + # Combine inputs which do not require any particular order. + # (1) Assign serialization API to whitelist. + flags.assign_flag(FLAG_WHITELIST, flags.filter_apis(IS_SERIALIZATION)) + + # (2) Merge text files with a known flag into the dictionary. + for flag in ALL_FLAGS: + for filename in args[flag]: + flags.assign_flag(flag, read_lines(filename), filename) + + # Merge text files where conflicts should be ignored. + # This will only assign the given flag if: + # (a) the entry exists, and + # (b) it has not been assigned any other flag. + # Because of (b), this must run after all strict assignments have been performed. + for flag in ALL_FLAGS: + for filename in args[flag + FLAG_IGNORE_CONFLICTS_SUFFIX]: + valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(filename)) + flags.assign_flag(flag, valid_entries, filename) + + # Assign all remaining entries to the blacklist. + flags.assign_flag(FLAG_BLACKLIST, flags.filter_apis(HAS_NO_API_LIST_ASSIGNED)) + + # Write output. + write_lines(args["output"], flags.generate_csv()) if __name__ == "__main__": main(sys.argv) diff --git a/tools/hiddenapi/generate_hiddenapi_lists_test.py b/tools/hiddenapi/generate_hiddenapi_lists_test.py index 4716241940b5..249f37db5a82 100755 --- a/tools/hiddenapi/generate_hiddenapi_lists_test.py +++ b/tools/hiddenapi/generate_hiddenapi_lists_test.py @@ -2,14 +2,14 @@ # # Copyright (C) 2018 The Android Open Source Project # -# Licensed under the Apache License, Version 2.0 (the "License"); +# Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, +# distributed under the License is distributed on an 'AS IS' BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. @@ -18,90 +18,90 @@ import unittest from generate_hiddenapi_lists import * class TestHiddenapiListGeneration(unittest.TestCase): + def test_init(self): + # Check empty lists + flags = FlagsDict([], []) + self.assertEquals(flags.generate_csv(), []) - def test_move_between_sets(self): - A = set([1, 2, 3, 4]) - B = set([5, 6, 7, 8]) - move_between_sets(set([2, 4]), A, B) - self.assertEqual(A, set([1, 3])) - self.assertEqual(B, set([2, 4, 5, 6, 7, 8])) - - def test_move_between_sets_fail_not_superset(self): - A = set([1, 2, 3, 4]) - B = set([5, 6, 7, 8]) - with self.assertRaises(AssertionError) as ar: - move_between_sets(set([0, 2]), A, B) - - def test_move_between_sets_fail_not_disjoint(self): - A = set([1, 2, 3, 4]) - B = set([4, 5, 6, 7, 8]) - with self.assertRaises(AssertionError) as ar: - move_between_sets(set([1, 4]), A, B) - - def test_get_package_name(self): - self.assertEqual(get_package_name("Ljava/lang/String;->clone()V"), "Ljava/lang/") - - def test_get_package_name_fail_no_arrow(self): - with self.assertRaises(AssertionError) as ar: - get_package_name("Ljava/lang/String;-clone()V") - with self.assertRaises(AssertionError) as ar: - get_package_name("Ljava/lang/String;>clone()V") - with self.assertRaises(AssertionError) as ar: - get_package_name("Ljava/lang/String;__clone()V") - - def test_get_package_name_fail_no_package(self): - with self.assertRaises(AssertionError) as ar: - get_package_name("LString;->clone()V") - - def test_all_package_names(self): - self.assertEqual(all_package_names(), set()) - self.assertEqual(all_package_names(set(["Lfoo/Bar;->baz()V"])), set(["Lfoo/"])) - self.assertEqual( - all_package_names(set(["Lfoo/Bar;->baz()V", "Lfoo/BarX;->bazx()I"])), - set(["Lfoo/"])) - self.assertEqual( - all_package_names( - set(["Lfoo/Bar;->baz()V"]), - set(["Lfoo/BarX;->bazx()I", "Labc/xyz/Mno;->ijk()J"])), - set(["Lfoo/", "Labc/xyz/"])) - - def test_move_all(self): - src = set([ "abc", "xyz" ]) - dst = set([ "def" ]) - move_all(src, dst) - self.assertEqual(src, set()) - self.assertEqual(dst, set([ "abc", "def", "xyz" ])) - - def test_move_from_packages(self): - src = set([ "Lfoo/bar/ClassA;->abc()J", # will be moved - "Lfoo/bar/ClassA;->def()J", # will be moved - "Lcom/pkg/example/ClassD;->ijk:J", # not moved: different package - "Lfoo/bar/xyz/ClassC;->xyz()Z" ]) # not moved: subpackage - dst = set() - packages = set([ "Lfoo/bar/" ]) - move_from_packages(packages, src, dst) - self.assertEqual( - src, set([ "Lfoo/bar/xyz/ClassC;->xyz()Z", "Lcom/pkg/example/ClassD;->ijk:J" ])) + # Check valid input - two public and two private API signatures. + flags = FlagsDict(['A', 'B'], ['C', 'D']) + self.assertEquals(flags.generate_csv(), + [ 'A,' + FLAG_WHITELIST, 'B,' + FLAG_WHITELIST, 'C', 'D' ]) + + # Check invalid input - overlapping public/private API signatures. + with self.assertRaises(AssertionError): + flags = FlagsDict(['A', 'B'], ['B', 'C', 'D']) + + def test_filter_apis(self): + # Initialize flags so that A and B are put on the whitelist and + # C, D, E are left unassigned. Try filtering for the unassigned ones. + flags = FlagsDict(['A', 'B'], ['C', 'D', 'E']) + filter_set = flags.filter_apis(lambda api, flags: not flags) + self.assertTrue(isinstance(filter_set, set)) + self.assertEqual(filter_set, set([ 'C', 'D', 'E' ])) + + def test_get_valid_subset_of_unassigned_keys(self): + # Create flags where only A is unassigned. + flags = FlagsDict(['A'], ['B', 'C']) + flags.assign_flag(FLAG_GREYLIST, set(['C'])) + self.assertEquals(flags.generate_csv(), + [ 'A,' + FLAG_WHITELIST, 'B', 'C,' + FLAG_GREYLIST ]) + + # Check three things: + # (1) B is selected as valid unassigned + # (2) A is not selected because it is assigned 'whitelist' + # (3) D is not selected because it is not a valid key self.assertEqual( - dst, set([ "Lfoo/bar/ClassA;->abc()J", "Lfoo/bar/ClassA;->def()J" ])) - - def test_move_serialization(self): - # All the entries should be moved apart from the last one - src = set([ "Lfoo/bar/ClassA;->readObject(Ljava/io/ObjectInputStream;)V", - "Lfoo/bar/ClassA;->readObjectNoData()V", - "Lfoo/bar/ClassA;->readResolve()Ljava/lang/Object;", - "Lfoo/bar/ClassA;->serialVersionUID:J", - "Lfoo/bar/ClassA;->serialPersistentFields:[Ljava/io/ObjectStreamField;", - "Lfoo/bar/ClassA;->writeObject(Ljava/io/ObjectOutputStream;)V", - "Lfoo/bar/ClassA;->writeReplace()Ljava/lang/Object;", - # Should not be moved as signature does not match - "Lfoo/bar/ClassA;->readObject(Ljava/io/ObjectInputStream;)I"]) - expectedToMove = len(src) - 1 - dst = set() - packages = set([ "Lfoo/bar/" ]) - move_serialization(src, dst) - self.assertEqual(len(src), 1) - self.assertEqual(len(dst), expectedToMove) + flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ])) + + def test_parse_and_merge_csv(self): + flags = FlagsDict(['A'], ['B']) + self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ]) + + # Test empty CSV entry. + flags.parse_and_merge_csv(['B']) + self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ]) + + # Test assigning an already assigned flag. + flags.parse_and_merge_csv(['A,' + FLAG_WHITELIST]) + self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ]) + + # Test new additions. + flags.parse_and_merge_csv([ + 'A,' + FLAG_GREYLIST, + 'B,' + FLAG_BLACKLIST + ',' + FLAG_GREYLIST_MAX_O ]) + self.assertEqual(flags.generate_csv(), + [ 'A,' + FLAG_GREYLIST + "," + FLAG_WHITELIST, + 'B,' + FLAG_BLACKLIST + "," + FLAG_GREYLIST_MAX_O ]) + + # Test unknown API signature. + with self.assertRaises(AssertionError): + flags.parse_and_merge_csv([ 'C' ]) + + # Test unknown flag. + with self.assertRaises(AssertionError): + flags.parse_and_merge_csv([ 'A,foo' ]) + + def test_assign_flag(self): + flags = FlagsDict(['A'], ['B']) + self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ]) + + # Test assigning an already assigned flag. + flags.assign_flag(FLAG_WHITELIST, set([ 'A' ])) + self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ]) + + # Test new additions. + flags.assign_flag(FLAG_GREYLIST, set([ 'A', 'B' ])) + self.assertEquals(flags.generate_csv(), + [ 'A,' + FLAG_GREYLIST + "," + FLAG_WHITELIST, 'B,' + FLAG_GREYLIST ]) + + # Test invalid API signature. + with self.assertRaises(AssertionError): + flags.assign_flag(FLAG_WHITELIST, set([ 'C' ])) + + # Test invalid flag. + with self.assertRaises(AssertionError): + flags.assign_flag('foo', set([ 'A' ])) if __name__ == '__main__': unittest.main() diff --git a/tools/hiddenapi/merge_csv.py b/tools/hiddenapi/merge_csv.py new file mode 100755 index 000000000000..48c07553ffef --- /dev/null +++ b/tools/hiddenapi/merge_csv.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Merge mutliple CSV files, possibly with different columns, writing to stdout. +""" + +import csv +import sys + +csv_readers = [ + csv.DictReader(open(csv_file, 'rb'), delimiter=',', quotechar='|') + for csv_file in sys.argv[1:] +] + +# Build union of all columns from source files: +headers = set() +for reader in csv_readers: + headers = headers.union(reader.fieldnames) + +# Concatenate all files to output: +out = csv.DictWriter(sys.stdout, delimiter=',', quotechar='|', fieldnames = sorted(headers)) +out.writeheader() +for reader in csv_readers: + for row in reader: + out.writerow(row) + + diff --git a/tools/hiddenapi/sort_api.sh b/tools/hiddenapi/sort_api.sh index 76a2f2d6eba1..710da40585ac 100755 --- a/tools/hiddenapi/sort_api.sh +++ b/tools/hiddenapi/sort_api.sh @@ -21,4 +21,6 @@ A=( $(uniq <<< "${A[*]}") ) A=( ${C[*]} ${A[*]} ) unset IFS # Dump array back into the file -printf '%s\n' "${A[@]}" > "$dest_list" +if [ ${#A[@]} -ne 0 ]; then + printf '%s\n' "${A[@]}" > "$dest_list" +fi diff --git a/tools/incident_report/printer.h b/tools/incident_report/printer.h index ed93fa19542c..63e276b367ca 100644 --- a/tools/incident_report/printer.h +++ b/tools/incident_report/printer.h @@ -22,7 +22,7 @@ class Out { public: - Out(int fd); + explicit Out(int fd); ~Out(); void printf(const char* format, ...); diff --git a/tools/incident_section_gen/Android.bp b/tools/incident_section_gen/Android.bp index f07445a17781..0c7797eacf09 100644 --- a/tools/incident_section_gen/Android.bp +++ b/tools/incident_section_gen/Android.bp @@ -24,6 +24,8 @@ cc_binary_host { "-O0", "-Wall", "-Werror", + // Allow implicit fallthroughs in main.cpp until they are fixed. + "-Wno-error=implicit-fallthrough", ], srcs: ["main.cpp"], shared_libs: [ diff --git a/tools/localedata/extract_icu_data.py b/tools/localedata/extract_icu_data.py index 9dceba2163eb..6b4c34677d96 100755 --- a/tools/localedata/extract_icu_data.py +++ b/tools/localedata/extract_icu_data.py @@ -155,7 +155,7 @@ def dump_representative_locales(representative_locales): print print 'std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({' for locale in sorted(representative_locales): - print ' 0x%08Xllu, // %s' % ( + print ' 0x%08XLLU, // %s' % ( pack_to_uint64(locale), locale) print '});' diff --git a/tools/preload/Android.bp b/tools/preload/Android.bp new file mode 100644 index 000000000000..809ee474969a --- /dev/null +++ b/tools/preload/Android.bp @@ -0,0 +1,17 @@ +java_library_host { + name: "preload", + srcs: [ + "Compile.java", + "LoadedClass.java", + "MemoryUsage.java", + "Operation.java", + "Policy.java", + "PrintCsv.java", + "PrintHtmlDiff.java", + "PrintPsTree.java", + "Proc.java", + "Record.java", + "Root.java", + "WritePreloadedClassFile.java", + ], +} diff --git a/tools/preload/Android.mk b/tools/preload/Android.mk deleted file mode 100644 index 14a4547cccbf..000000000000 --- a/tools/preload/Android.mk +++ /dev/null @@ -1,23 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - Compile.java \ - LoadedClass.java \ - MemoryUsage.java \ - Operation.java \ - Policy.java \ - PrintCsv.java \ - PrintHtmlDiff.java \ - PrintPsTree.java \ - Proc.java \ - Record.java \ - Root.java \ - WritePreloadedClassFile.java - -LOCAL_MODULE:= preload - -include $(BUILD_HOST_JAVA_LIBRARY) - -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/tools/preload/loadclass/Android.bp b/tools/preload/loadclass/Android.bp new file mode 100644 index 000000000000..6f12015dae2b --- /dev/null +++ b/tools/preload/loadclass/Android.bp @@ -0,0 +1,4 @@ +java_test { + name: "loadclass", + srcs: ["**/*.java"], +} diff --git a/tools/preload/loadclass/Android.mk b/tools/preload/loadclass/Android.mk deleted file mode 100644 index 65828be617df..000000000000 --- a/tools/preload/loadclass/Android.mk +++ /dev/null @@ -1,9 +0,0 @@ -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_MODULE_TAGS := tests - -LOCAL_MODULE := loadclass - -include $(BUILD_JAVA_LIBRARY) diff --git a/tools/processors/unsupportedappusage/Android.bp b/tools/processors/unsupportedappusage/Android.bp index 1aca3edfab88..0e33fddcde07 100644 --- a/tools/processors/unsupportedappusage/Android.bp +++ b/tools/processors/unsupportedappusage/Android.bp @@ -1,6 +1,8 @@ -java_library_host { +java_plugin { name: "unsupportedappusage-annotation-processor", + processor_class: "android.processor.unsupportedappusage.UnsupportedAppUsageProcessor", + java_resources: [ "META-INF/**/*", ], diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java index 1d4c435939db..d368136c7081 100644 --- a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java +++ b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java @@ -28,6 +28,7 @@ import com.sun.tools.javac.util.Position; import java.io.IOException; import java.io.PrintStream; +import java.net.URLEncoder; import java.util.Map; import java.util.Set; import java.util.TreeMap; @@ -38,7 +39,9 @@ import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; /** @@ -108,10 +111,25 @@ public class UnsupportedAppUsageProcessor extends AbstractProcessor { "startline", "startcol", "endline", - "endcol" + "endcol", + "properties" ); } + private String encodeAnnotationProperties(AnnotationMirror annotation) { + StringBuilder sb = new StringBuilder(); + for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> e + : annotation.getElementValues().entrySet()) { + if (sb.length() > 0) { + sb.append("&"); + } + sb.append(e.getKey().getSimpleName()) + .append("=") + .append(URLEncoder.encode(e.getValue().toString())); + } + return sb.toString(); + } + /** * Maps an annotated element to the source position of the @UnsupportedAppUsage annotation * attached to it. It returns CSV in the format: @@ -137,7 +155,8 @@ public class UnsupportedAppUsageProcessor extends AbstractProcessor { lines.getLineNumber(pair.fst.pos().getStartPosition()), lines.getColumnNumber(pair.fst.pos().getStartPosition()), lines.getLineNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)), - lines.getColumnNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions))); + lines.getColumnNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)), + encodeAnnotationProperties(unsupportedAppUsage)); } /** diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp index 703a67b791be..5725f0cdae6e 100644 --- a/tools/stats_log_api_gen/Android.bp +++ b/tools/stats_log_api_gen/Android.bp @@ -96,6 +96,7 @@ genrule { cc_library_shared { name: "libstatslog", + host_supported: true, generated_sources: ["statslog.cpp"], generated_headers: ["statslog.h"], cflags: [ @@ -105,8 +106,19 @@ cc_library_shared { export_generated_headers: ["statslog.h"], shared_libs: [ "liblog", - "libutils", "libcutils", ], static_libs: ["libstatssocket"], + target: { + android: { + shared_libs: [ + "libutils", + ], + }, + host: { + static_libs: [ + "libutils", + ], + }, + }, } diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp index ebdcdfdd6c50..61174d99198b 100644 --- a/tools/stats_log_api_gen/Collation.cpp +++ b/tools/stats_log_api_gen/Collation.cpp @@ -47,7 +47,8 @@ AtomDecl::AtomDecl(const AtomDecl& that) fields(that.fields), primaryFields(that.primaryFields), exclusiveField(that.exclusiveField), - uidField(that.uidField) {} + uidField(that.uidField), + binaryFields(that.binaryFields) {} AtomDecl::AtomDecl(int c, const string& n, const string& m) :code(c), @@ -116,6 +117,9 @@ java_type(const FieldDescriptor* field) if (field->message_type()->full_name() == "android.os.statsd.AttributionNode") { return JAVA_TYPE_ATTRIBUTION_CHAIN; + } else if (field->options().GetExtension(os::statsd::log_mode) == + os::statsd::LogMode::MODE_BYTES) { + return JAVA_TYPE_BYTE_ARRAY; } else { return JAVA_TYPE_OBJECT; } @@ -185,6 +189,8 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin(); it != fields.end(); it++) { const FieldDescriptor *field = it->second; + bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) == + os::statsd::LogMode::MODE_BYTES; java_type_t javaType = java_type(field); @@ -198,12 +204,19 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, field->name().c_str()); errorCount++; continue; - } else if (javaType == JAVA_TYPE_BYTE_ARRAY) { + } else if (javaType == JAVA_TYPE_BYTE_ARRAY && !isBinaryField) { print_error(field, "Raw bytes type not allowed for field: %s\n", field->name().c_str()); errorCount++; continue; } + + if (isBinaryField && javaType != JAVA_TYPE_BYTE_ARRAY) { + print_error(field, "Cannot mark field %s as bytes.\n", + field->name().c_str()); + errorCount++; + continue; + } } // Check that if there's an attribution chain, it's at position 1. @@ -228,12 +241,16 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, it != fields.end(); it++) { const FieldDescriptor *field = it->second; java_type_t javaType = java_type(field); + bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) == + os::statsd::LogMode::MODE_BYTES; AtomField atField(field->name(), javaType); if (javaType == JAVA_TYPE_ENUM) { // All enums are treated as ints when it comes to function signatures. signature->push_back(JAVA_TYPE_INT); collate_enums(*field->enum_type(), &atField); + } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) { + signature->push_back(JAVA_TYPE_BYTE_ARRAY); } else { signature->push_back(javaType); } @@ -275,6 +292,10 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, errorCount++; } } + // Binary field validity is already checked above. + if (isBinaryField) { + atomDecl->binaryFields.push_back(it->first); + } } return errorCount; diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h index 5d2c30292c9c..a8b270caefaf 100644 --- a/tools/stats_log_api_gen/Collation.h +++ b/tools/stats_log_api_gen/Collation.h @@ -86,6 +86,8 @@ struct AtomDecl { int uidField = 0; + vector<int> binaryFields; + AtomDecl(); AtomDecl(const AtomDecl& that); AtomDecl(int code, const string& name, const string& message); diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index e519909aa026..27e77fee83a3 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -68,6 +68,8 @@ cpp_type_name(java_type_t type) return "double"; case JAVA_TYPE_STRING: return "char const*"; + case JAVA_TYPE_BYTE_ARRAY: + return "const BytesField&"; default: return "UNKNOWN"; } @@ -90,6 +92,8 @@ java_type_name(java_type_t type) return "double"; case JAVA_TYPE_STRING: return "java.lang.String"; + case JAVA_TYPE_BYTE_ARRAY: + return "byte[]"; default: return "UNKNOWN"; } @@ -104,7 +108,9 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, fprintf(out, "#include <mutex>\n"); fprintf(out, "#include <chrono>\n"); fprintf(out, "#include <thread>\n"); + fprintf(out, "#ifdef __ANDROID__\n"); fprintf(out, "#include <cutils/properties.h>\n"); + fprintf(out, "#endif\n"); fprintf(out, "#include <stats_event_list.h>\n"); fprintf(out, "#include <log/log.h>\n"); fprintf(out, "#include <statslog.h>\n"); @@ -115,7 +121,11 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, fprintf(out, "namespace util {\n"); fprintf(out, "// the single event tag id for all stats logs\n"); fprintf(out, "const static int kStatsEventTag = 1937006964;\n"); + fprintf(out, "#ifdef __ANDROID__\n"); fprintf(out, "const static bool kStatsdEnabled = property_get_bool(\"ro.statsd.enable\", true);\n"); + fprintf(out, "#else\n"); + fprintf(out, "const static bool kStatsdEnabled = false;\n"); + fprintf(out, "#endif\n"); std::set<string> kTruncatingAtomNames = {"mobile_radio_power_state_changed", "audio_state_changed", @@ -200,13 +210,40 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, } fprintf(out, " return options;\n"); - fprintf(out, " }\n"); + fprintf(out, "}\n"); fprintf(out, "const std::map<int, StateAtomFieldOptions> " "AtomsInfo::kStateAtomsFieldOptions = " "getStateAtomFieldOptions();\n"); + fprintf(out, + "static std::map<int, std::vector<int>> " + "getBinaryFieldAtoms() {\n"); + fprintf(out, " std::map<int, std::vector<int>> options;\n"); + for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); + atom != atoms.decls.end(); atom++) { + if (atom->binaryFields.size() == 0) { + continue; + } + fprintf(out, + "\n // Adding binary fields for atom " + "(%d)%s\n", + atom->code, atom->name.c_str()); + + for (const auto& field : atom->binaryFields) { + fprintf(out, " options[static_cast<int>(%s)].push_back(%d);\n", + make_constant_name(atom->name).c_str(), field); + } + } + + fprintf(out, " return options;\n"); + fprintf(out, "}\n"); + + fprintf(out, + "const std::map<int, std::vector<int>> " + "AtomsInfo::kBytesFieldAtoms = " + "getBinaryFieldAtoms();\n"); fprintf(out, "int64_t lastRetryTimestampNs = -1;\n"); fprintf(out, "const int64_t kMinRetryIntervalNs = NS_PER_SEC * 60 * 20; // 20 minutes\n"); @@ -277,6 +314,11 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, fprintf(out, " event.end();\n"); fprintf(out, " }\n"); fprintf(out, " event.end();\n\n"); + } else if (*arg == JAVA_TYPE_BYTE_ARRAY) { + fprintf(out, + " event.AppendCharArray(arg%d.arg, " + "arg%d.arg_length);\n", + argIndex, argIndex); } else { if (*arg == JAVA_TYPE_STRING) { fprintf(out, " if (arg%d == NULL) {\n", argIndex); @@ -396,7 +438,14 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, fprintf(out, " arg%d = \"\";\n", argIndex); fprintf(out, " }\n"); } - fprintf(out, " event << arg%d;\n", argIndex); + if (*arg == JAVA_TYPE_BYTE_ARRAY) { + fprintf(out, + " event.AppendCharArray(arg%d.arg, " + "arg%d.arg_length);", + argIndex, argIndex); + } else { + fprintf(out, " event << arg%d;\n", argIndex); + } if (argIndex == 2) { fprintf(out, " event.end();\n\n"); fprintf(out, " event.end();\n\n"); @@ -476,7 +525,9 @@ void build_non_chained_decl_map(const Atoms& atoms, static void write_cpp_usage( FILE* out, const string& method_name, const string& atom_code_name, const AtomDecl& atom, const AtomDecl &attributionDecl) { - fprintf(out, " * Usage: %s(StatsLog.%s", method_name.c_str(), atom_code_name.c_str()); + fprintf(out, " * Usage: %s(StatsLog.%s", method_name.c_str(), + atom_code_name.c_str()); + for (vector<AtomField>::const_iterator field = atom.fields.begin(); field != atom.fields.end(); field++) { if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) { @@ -584,6 +635,15 @@ write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributio fprintf(out, "};\n"); fprintf(out, "\n"); + fprintf(out, "struct BytesField {\n"); + fprintf(out, + " BytesField(char const* array, size_t len) : arg(array), " + "arg_length(len) {}\n"); + fprintf(out, " char const* arg;\n"); + fprintf(out, " size_t arg_length;\n"); + fprintf(out, "};\n"); + fprintf(out, "\n"); + fprintf(out, "struct StateAtomFieldOptions {\n"); fprintf(out, " std::vector<int> primaryFields;\n"); fprintf(out, " int exclusiveField;\n"); @@ -600,6 +660,9 @@ write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributio fprintf(out, " const static std::map<int, StateAtomFieldOptions> " "kStateAtomsFieldOptions;\n"); + fprintf(out, + " const static std::map<int, std::vector<int>> " + "kBytesFieldAtoms;"); fprintf(out, "};\n"); fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", @@ -632,6 +695,8 @@ static void write_java_usage(FILE* out, const string& method_name, const string& field != atom.fields.end(); field++) { if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) { fprintf(out, ", android.os.WorkSource workSource"); + } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) { + fprintf(out, ", byte[] %s", field->name.c_str()); } else { fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str()); } @@ -644,6 +709,7 @@ static void write_java_method( const AtomDecl &attributionDecl) { for (set<vector<java_type_t>>::const_iterator signature = signatures.begin(); signature != signatures.end(); signature++) { + fprintf(out, " /** @hide */\n"); fprintf(out, " public static native int %s(int code", method_name.c_str()); int argIndex = 1; for (vector<java_type_t>::const_iterator arg = signature->begin(); @@ -687,6 +753,7 @@ static void write_java_work_source_method(FILE* out, const set<vector<java_type_ } // Method header (signature) + fprintf(out, " /** @hide */\n"); fprintf(out, " public static void write(int code"); int argIndex = 1; for (vector<java_type_t>::const_iterator arg = signature->begin(); @@ -766,6 +833,7 @@ write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionD if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) { write_java_usage(out, "write_non_chained", constant, *non_chained_decl->second); } + fprintf(out, " * @hide\n"); fprintf(out, " */\n"); fprintf(out, " public static final int %s = %d;\n", constant.c_str(), atom->code); } @@ -782,6 +850,7 @@ write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionD field->name.c_str()); for (map<int, string>::const_iterator value = field->enumValues.begin(); value != field->enumValues.end(); value++) { + fprintf(out, " /** @hide */\n"); fprintf(out, " public static final int %s__%s__%s = %d;\n", make_constant_name(atom->message).c_str(), make_constant_name(field->name).c_str(), @@ -821,6 +890,8 @@ jni_type_name(java_type_t type) return "jdouble"; case JAVA_TYPE_STRING: return "jstring"; + case JAVA_TYPE_BYTE_ARRAY: + return "jbyteArray"; default: return "UNKNOWN"; } @@ -868,6 +939,9 @@ jni_function_name(const string& method_name, const vector<java_type_t>& signatur case JAVA_TYPE_ATTRIBUTION_CHAIN: result += "_AttributionChain"; break; + case JAVA_TYPE_BYTE_ARRAY: + result += "_bytes"; + break; default: result += "_UNKNOWN"; break; @@ -893,6 +967,8 @@ java_type_signature(java_type_t type) return "D"; case JAVA_TYPE_STRING: return "Ljava/lang/String;"; + case JAVA_TYPE_BYTE_ARRAY: + return "[B"; default: return "UNKNOWN"; } @@ -960,6 +1036,37 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp fprintf(out, " } else {\n"); fprintf(out, " str%d = NULL;\n", argIndex); fprintf(out, " }\n"); + } else if (*arg == JAVA_TYPE_BYTE_ARRAY) { + hadStringOrChain = true; + fprintf(out, " jbyte* jbyte_array%d;\n", argIndex); + fprintf(out, " const char* str%d;\n", argIndex); + fprintf(out, " int str%d_length = 0;\n", argIndex); + fprintf(out, + " if (arg%d != NULL && env->GetArrayLength(arg%d) > " + "0) {\n", + argIndex, argIndex); + fprintf(out, + " jbyte_array%d = " + "env->GetByteArrayElements(arg%d, NULL);\n", + argIndex, argIndex); + fprintf(out, + " str%d_length = env->GetArrayLength(arg%d);\n", + argIndex, argIndex); + fprintf(out, + " str%d = " + "reinterpret_cast<char*>(env->GetByteArrayElements(arg%" + "d, NULL));\n", + argIndex, argIndex); + fprintf(out, " } else {\n"); + fprintf(out, " jbyte_array%d = NULL;\n", argIndex); + fprintf(out, " str%d = NULL;\n", argIndex); + fprintf(out, " }\n"); + + fprintf(out, + " android::util::BytesField bytesField%d(str%d, " + "str%d_length);", + argIndex, argIndex, argIndex); + } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { hadStringOrChain = true; for (auto chainField : attributionDecl.fields) { @@ -1012,7 +1119,8 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp // stats_write call argIndex = 1; - fprintf(out, " int ret = android::util::%s(code", cpp_method_name.c_str()); + fprintf(out, "\n int ret = android::util::%s(code", + cpp_method_name.c_str()); for (vector<java_type_t>::const_iterator arg = signature->begin(); arg != signature->end(); arg++) { if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { @@ -1025,8 +1133,11 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp fprintf(out, ", %s_vec", chainField.name.c_str()); } } + } else if (*arg == JAVA_TYPE_BYTE_ARRAY) { + fprintf(out, ", bytesField%d", argIndex); } else { - const char *argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg"; + const char* argName = + (*arg == JAVA_TYPE_STRING) ? "str" : "arg"; fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex); } argIndex++; @@ -1043,6 +1154,13 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp fprintf(out, " env->ReleaseStringUTFChars(arg%d, str%d);\n", argIndex, argIndex); fprintf(out, " }\n"); + } else if (*arg == JAVA_TYPE_BYTE_ARRAY) { + fprintf(out, " if (str%d != NULL) { \n", argIndex); + fprintf(out, + " env->ReleaseByteArrayElements(arg%d, " + "jbyte_array%d, 0);\n", + argIndex, argIndex); + fprintf(out, " }\n"); } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { for (auto chainField : attributionDecl.fields) { if (chainField.javaType == JAVA_TYPE_INT) { diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto index 264a865e3b39..188b765e241e 100644 --- a/tools/stats_log_api_gen/test.proto +++ b/tools/stats_log_api_gen/test.proto @@ -109,6 +109,28 @@ message BadAttributionNodePosition { oneof event { BadAttributionNodePositionAtom bad = 1; } } +message GoodEventWithBinaryFieldAtom { + oneof event { GoodBinaryFieldAtom field1 = 1; } +} + +message ComplexField { + optional string str = 1; +} + +message GoodBinaryFieldAtom { + optional int32 field1 = 1; + optional ComplexField bf = 2 [(android.os.statsd.log_mode) = MODE_BYTES]; +} + +message BadEventWithBinaryFieldAtom { + oneof event { BadBinaryFieldAtom field1 = 1; } +} + +message BadBinaryFieldAtom { + optional int32 field1 = 1; + optional ComplexField bf = 2; +} + message BadStateAtoms { oneof event { BadStateAtom1 bad1 = 1; diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp index 1936d9667948..ad3bffacd442 100644 --- a/tools/stats_log_api_gen/test_collation.cpp +++ b/tools/stats_log_api_gen/test_collation.cpp @@ -212,5 +212,19 @@ TEST(CollationTest, PassOnGoodStateAtomOptions) { EXPECT_EQ(0, errorCount); } +TEST(CollationTest, PassOnGoodBinaryFieldAtom) { + Atoms atoms; + int errorCount = + collate_atoms(GoodEventWithBinaryFieldAtom::descriptor(), &atoms); + EXPECT_EQ(0, errorCount); +} + +TEST(CollationTest, FailOnBadBinaryFieldAtom) { + Atoms atoms; + int errorCount = + collate_atoms(BadEventWithBinaryFieldAtom::descriptor(), &atoms); + EXPECT_TRUE(errorCount > 0); +} + } // namespace stats_log_api_gen } // namespace android
\ No newline at end of file diff --git a/tools/streaming_proto/Errors.h b/tools/streaming_proto/Errors.h index f14bbfd55b5f..bddd9819e8f5 100644 --- a/tools/streaming_proto/Errors.h +++ b/tools/streaming_proto/Errors.h @@ -11,7 +11,7 @@ using namespace std; struct Error { Error(); - explicit Error(const Error& that); + Error(const Error& that); Error(const string& filename, int lineno, const char* message); string filename; |