AAPT2: Convert binary ResTable_config to proto
Test: make aapt2_tests
Change-Id: I8641f13ce41cd58ac8bc3da31c4e15a7b3f06092
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 43918da..f0ebf10 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -139,6 +139,7 @@
"xml/XmlDom.cpp",
"xml/XmlPullParser.cpp",
"xml/XmlUtil.cpp",
+ "Configuration.proto",
"Resources.proto",
"ResourcesInternal.proto",
],
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
index a9278c1..59a6e12 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -876,6 +876,12 @@
return copy;
}
+std::string ConfigDescription::GetBcp47LanguageTag(bool canonicalize) const {
+ char locale[RESTABLE_MAX_LOCALE_LEN];
+ getBcp47Locale(locale, canonicalize);
+ return std::string(locale);
+}
+
bool ConfigDescription::Dominates(const ConfigDescription& o) const {
if (*this == o) {
return true;
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index 65c9617..c1d0e10 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -61,6 +61,9 @@
ConfigDescription CopyWithoutSdkVersion() const;
+ // Returns the BCP-47 language tag of this configuration's locale.
+ std::string GetBcp47LanguageTag(bool canonicalize = false) 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
diff --git a/tools/aapt2/Configuration.proto b/tools/aapt2/Configuration.proto
new file mode 100644
index 0000000..fc636a4
--- /dev/null
+++ b/tools/aapt2/Configuration.proto
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto3";
+
+package aapt.pb;
+
+option java_package = "com.android.aapt";
+option optimize_for = LITE_RUNTIME;
+
+// A description of the requirements a device must have in order for a
+// resource to be matched and selected.
+message Configuration {
+ enum LayoutDirection {
+ LAYOUT_DIRECTION_UNSET = 0;
+ LAYOUT_DIRECTION_LTR = 1;
+ LAYOUT_DIRECTION_RTL = 2;
+ }
+
+ enum ScreenLayoutSize {
+ SCREEN_LAYOUT_SIZE_UNSET = 0;
+ SCREEN_LAYOUT_SIZE_SMALL = 1;
+ SCREEN_LAYOUT_SIZE_NORMAL = 2;
+ SCREEN_LAYOUT_SIZE_LARGE = 3;
+ SCREEN_LAYOUT_SIZE_XLARGE = 4;
+ }
+
+ enum ScreenLayoutLong {
+ SCREEN_LAYOUT_LONG_UNSET = 0;
+ SCREEN_LAYOUT_LONG_LONG = 1;
+ SCREEN_LAYOUT_LONG_NOTLONG = 2;
+ }
+
+ enum ScreenRound {
+ SCREEN_ROUND_UNSET = 0;
+ SCREEN_ROUND_ROUND = 1;
+ SCREEN_ROUND_NOTROUND = 2;
+ }
+
+ enum WideColorGamut {
+ WIDE_COLOR_GAMUT_UNSET = 0;
+ WIDE_COLOR_GAMUT_WIDECG = 1;
+ WIDE_COLOR_GAMUT_NOWIDECG = 2;
+ }
+
+ enum Hdr {
+ HDR_UNSET = 0;
+ HDR_HIGHDR = 1;
+ HDR_LOWDR = 2;
+ }
+
+ enum Orientation {
+ ORIENTATION_UNSET = 0;
+ ORIENTATION_PORT = 1;
+ ORIENTATION_LAND = 2;
+ ORIENTATION_SQUARE = 3;
+ }
+
+ enum UiModeType {
+ UI_MODE_TYPE_UNSET = 0;
+ UI_MODE_TYPE_NORMAL = 1;
+ UI_MODE_TYPE_DESK = 2;
+ UI_MODE_TYPE_CAR = 3;
+ UI_MODE_TYPE_TELEVISION = 4;
+ UI_MODE_TYPE_APPLIANCE = 5;
+ UI_MODE_TYPE_WATCH = 6;
+ UI_MODE_TYPE_VRHEADSET = 7;
+ }
+
+ enum UiModeNight {
+ UI_MODE_NIGHT_UNSET = 0;
+ UI_MODE_NIGHT_NIGHT = 1;
+ UI_MODE_NIGHT_NOTNIGHT = 2;
+ }
+
+ enum Touchscreen {
+ TOUCHSCREEN_UNSET = 0;
+ TOUCHSCREEN_NOTOUCH = 1;
+ TOUCHSCREEN_STYLUS = 2;
+ TOUCHSCREEN_FINGER = 3;
+ }
+
+ enum KeysHidden {
+ KEYS_HIDDEN_UNSET = 0;
+ KEYS_HIDDEN_KEYSEXPOSED = 1;
+ KEYS_HIDDEN_KEYSHIDDEN = 2;
+ KEYS_HIDDEN_KEYSSOFT = 3;
+ }
+
+ enum Keyboard {
+ KEYBOARD_UNSET = 0;
+ KEYBOARD_NOKEYS = 1;
+ KEYBOARD_QWERTY = 2;
+ KEYBOARD_TWELVEKEY = 3;
+ }
+
+ enum NavHidden {
+ NAV_HIDDEN_UNSET = 0;
+ NAV_HIDDEN_NAVEXPOSED = 1;
+ NAV_HIDDEN_NAVHIDDEN = 2;
+ }
+
+ enum Navigation {
+ NAVIGATION_UNSET = 0;
+ NAVIGATION_NONAV = 1;
+ NAVIGATION_DPAD = 2;
+ NAVIGATION_TRACKBALL = 3;
+ NAVIGATION_WHEEL = 4;
+ }
+
+ //
+ // Axis/dimensions that are understood by the runtime.
+ //
+
+ // Mobile country code.
+ uint32 mcc = 1;
+
+ // Mobile network code.
+ uint32 mnc = 2;
+
+ // BCP-47 locale tag.
+ string locale = 3;
+
+ // Left-to-right, right-to-left...
+ LayoutDirection layout_direction = 4;
+
+ // Screen width in pixels. Prefer screen_width_dp.
+ uint32 screen_width = 5;
+
+ // Screen height in pixels. Prefer screen_height_dp.
+ uint32 screen_height = 6;
+
+ // Screen width in density independent pixels (dp).
+ uint32 screen_width_dp = 7;
+
+ // Screen height in density independent pixels (dp).
+ uint32 screen_height_dp = 8;
+
+ // The smallest screen dimension, regardless of orientation, in dp.
+ uint32 smallest_screen_width_dp = 9;
+
+ // Whether the device screen is classified as small, normal, large, xlarge.
+ ScreenLayoutSize screen_layout_size = 10;
+
+ // Whether the device screen is long.
+ ScreenLayoutLong screen_layout_long = 11;
+
+ // Whether the screen is round (Android Wear).
+ ScreenRound screen_round = 12;
+
+ // Whether the screen supports wide color gamut.
+ WideColorGamut wide_color_gamut = 13;
+
+ // Whether the screen has high dynamic range.
+ Hdr hdr = 14;
+
+ // Which orientation the device is in (portrait, landscape).
+ Orientation orientation = 15;
+
+ // Which type of UI mode the device is in (television, car, etc.).
+ UiModeType ui_mode_type = 16;
+
+ // Whether the device is in night mode.
+ UiModeNight ui_mode_night = 17;
+
+ // The device's screen density in dots-per-inch (dpi).
+ uint32 density = 18;
+
+ // Whether a touchscreen exists, supports a stylus, or finger.
+ Touchscreen touchscreen = 19;
+
+ // Whether the keyboard hardware keys are currently hidden, exposed, or
+ // if the keyboard is a software keyboard.
+ KeysHidden keys_hidden = 20;
+
+ // The type of keyboard present (none, QWERTY, 12-key).
+ Keyboard keyboard = 21;
+
+ // Whether the navigation is exposed or hidden.
+ NavHidden nav_hidden = 22;
+
+ // The type of navigation present on the device
+ // (trackball, wheel, dpad, etc.).
+ Navigation navigation = 23;
+
+ // The minimum SDK version of the device.
+ uint32 sdk_version = 24;
+
+ //
+ // Build-time only dimensions.
+ //
+
+ string product = 25;
+}
diff --git a/tools/aapt2/Locale.cpp b/tools/aapt2/Locale.cpp
index 7664fac..d81921f 100644
--- a/tools/aapt2/Locale.cpp
+++ b/tools/aapt2/Locale.cpp
@@ -24,9 +24,10 @@
#include "util/Util.h"
-namespace aapt {
+using ::android::ResTable_config;
+using ::android::StringPiece;
-using android::ResTable_config;
+namespace aapt {
void LocaleValue::set_language(const char* language_chars) {
size_t i = 0;
@@ -72,7 +73,7 @@
return std::all_of(std::begin(str), std::end(str), ::isdigit);
}
-bool LocaleValue::InitFromFilterString(const android::StringPiece& str) {
+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, '_');
@@ -138,6 +139,71 @@
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;
@@ -145,71 +211,13 @@
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 "-".
- std::vector<std::string> subtags = util::SplitAndLowercase(part, '+');
- subtags.erase(subtags.begin());
- 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 -1;
- }
- } 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 -1;
- }
-
- // 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 {
+ // 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") {
+ if ((part.length() == 2 || part.length() == 3) && is_alpha(part) && part != "car") {
set_language(part.c_str());
++iter;
@@ -222,7 +230,6 @@
}
}
}
-
return static_cast<ssize_t>(iter - start_iter);
}
diff --git a/tools/aapt2/Locale.h b/tools/aapt2/Locale.h
index 3d73b2e..6d8b598 100644
--- a/tools/aapt2/Locale.h
+++ b/tools/aapt2/Locale.h
@@ -41,6 +41,9 @@
*/
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.
*/
@@ -67,6 +70,8 @@
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);
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index c1af88a..174b7f6 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -16,19 +16,13 @@
syntax = "proto3";
+import "frameworks/base/tools/aapt2/Configuration.proto";
+
package aapt.pb;
option java_package = "com.android.aapt";
option optimize_for = LITE_RUNTIME;
-// A configuration description that wraps the binary form of the C++ class
-// aapt::ConfigDescription, with an added product definition.
-// TODO(adamlesinski): Flesh this out to be represented in proto.
-message ConfigDescription {
- bytes data = 1;
- string product = 2;
-}
-
// A string pool that wraps the binary form of the C++ class android::ResStringPool.
message StringPool {
bytes data = 1;
@@ -163,7 +157,7 @@
// A Configuration/Value pair.
message ConfigValue {
- ConfigDescription config = 1;
+ Configuration config = 1;
Value value = 2;
}
diff --git a/tools/aapt2/ResourcesInternal.proto b/tools/aapt2/ResourcesInternal.proto
index 17604e4..0b0a252 100644
--- a/tools/aapt2/ResourcesInternal.proto
+++ b/tools/aapt2/ResourcesInternal.proto
@@ -16,6 +16,7 @@
syntax = "proto3";
+import "frameworks/base/tools/aapt2/Configuration.proto";
import "frameworks/base/tools/aapt2/Resources.proto";
package aapt.pb.internal;
@@ -38,7 +39,7 @@
string resource_name = 1;
// The configuration for which the resource is defined.
- aapt.pb.ConfigDescription config = 2;
+ aapt.pb.Configuration config = 2;
// The filesystem path to where the source file originated.
// Mainly used to display helpful error messages.
diff --git a/tools/aapt2/proto/ProtoHelpers.cpp b/tools/aapt2/proto/ProtoHelpers.cpp
index 59ca8e4..18f7e1d 100644
--- a/tools/aapt2/proto/ProtoHelpers.cpp
+++ b/tools/aapt2/proto/ProtoHelpers.cpp
@@ -16,6 +16,8 @@
#include "proto/ProtoHelpers.h"
+#include "Locale.h"
+
namespace aapt {
void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool) {
@@ -70,27 +72,507 @@
return SymbolState::kUndefined;
}
-void SerializeConfig(const ConfigDescription& config, pb::ConfigDescription* out_pb_config) {
- android::ResTable_config flat_config = config;
- flat_config.size = sizeof(flat_config);
- flat_config.swapHtoD();
- out_pb_config->set_data(&flat_config, sizeof(flat_config));
+void SerializeConfig(const ConfigDescription& config, pb::Configuration* out_pb_config) {
+ out_pb_config->set_mcc(config.mcc);
+ out_pb_config->set_mnc(config.mnc);
+ out_pb_config->set_locale(config.GetBcp47LanguageTag());
+
+ switch (config.screenLayout & ConfigDescription::MASK_LAYOUTDIR) {
+ case ConfigDescription::LAYOUTDIR_LTR:
+ out_pb_config->set_layout_direction(pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_LTR);
+ break;
+
+ case ConfigDescription::LAYOUTDIR_RTL:
+ out_pb_config->set_layout_direction(pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_RTL);
+ break;
+ }
+
+ out_pb_config->set_screen_width(config.screenWidth);
+ out_pb_config->set_screen_height(config.screenHeight);
+ out_pb_config->set_screen_width_dp(config.screenWidthDp);
+ out_pb_config->set_screen_height_dp(config.screenHeightDp);
+ out_pb_config->set_smallest_screen_width_dp(config.smallestScreenWidthDp);
+
+ switch (config.screenLayout & ConfigDescription::MASK_SCREENSIZE) {
+ case ConfigDescription::SCREENSIZE_SMALL:
+ out_pb_config->set_screen_layout_size(
+ pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_SMALL);
+ break;
+
+ case ConfigDescription::SCREENSIZE_NORMAL:
+ out_pb_config->set_screen_layout_size(
+ pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_NORMAL);
+ break;
+
+ case ConfigDescription::SCREENSIZE_LARGE:
+ out_pb_config->set_screen_layout_size(
+ pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_LARGE);
+ break;
+
+ case ConfigDescription::SCREENSIZE_XLARGE:
+ out_pb_config->set_screen_layout_size(
+ pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_XLARGE);
+ break;
+ }
+
+ switch (config.screenLayout & ConfigDescription::MASK_SCREENLONG) {
+ case ConfigDescription::SCREENLONG_YES:
+ out_pb_config->set_screen_layout_long(
+ pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_LONG);
+ break;
+
+ case ConfigDescription::SCREENLONG_NO:
+ out_pb_config->set_screen_layout_long(
+ pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_NOTLONG);
+ break;
+ }
+
+ switch (config.screenLayout2 & ConfigDescription::MASK_SCREENROUND) {
+ case ConfigDescription::SCREENROUND_YES:
+ out_pb_config->set_screen_round(pb::Configuration_ScreenRound_SCREEN_ROUND_ROUND);
+ break;
+
+ case ConfigDescription::SCREENROUND_NO:
+ out_pb_config->set_screen_round(pb::Configuration_ScreenRound_SCREEN_ROUND_NOTROUND);
+ break;
+ }
+
+ switch (config.colorMode & ConfigDescription::MASK_WIDE_COLOR_GAMUT) {
+ case ConfigDescription::WIDE_COLOR_GAMUT_YES:
+ out_pb_config->set_wide_color_gamut(pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_WIDECG);
+ break;
+
+ case ConfigDescription::WIDE_COLOR_GAMUT_NO:
+ out_pb_config->set_wide_color_gamut(
+ pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_NOWIDECG);
+ break;
+ }
+
+ switch (config.colorMode & ConfigDescription::MASK_HDR) {
+ case ConfigDescription::HDR_YES:
+ out_pb_config->set_hdr(pb::Configuration_Hdr_HDR_HIGHDR);
+ break;
+
+ case ConfigDescription::HDR_NO:
+ out_pb_config->set_hdr(pb::Configuration_Hdr_HDR_LOWDR);
+ break;
+ }
+
+ switch (config.orientation) {
+ case ConfigDescription::ORIENTATION_PORT:
+ out_pb_config->set_orientation(pb::Configuration_Orientation_ORIENTATION_PORT);
+ break;
+
+ case ConfigDescription::ORIENTATION_LAND:
+ out_pb_config->set_orientation(pb::Configuration_Orientation_ORIENTATION_LAND);
+ break;
+
+ case ConfigDescription::ORIENTATION_SQUARE:
+ out_pb_config->set_orientation(pb::Configuration_Orientation_ORIENTATION_SQUARE);
+ break;
+ }
+
+ switch (config.uiMode & ConfigDescription::MASK_UI_MODE_TYPE) {
+ case ConfigDescription::UI_MODE_TYPE_NORMAL:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_NORMAL);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_DESK:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_DESK);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_CAR:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_CAR);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_TELEVISION:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_TELEVISION);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_APPLIANCE:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_APPLIANCE);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_WATCH:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_WATCH);
+ break;
+
+ case ConfigDescription::UI_MODE_TYPE_VR_HEADSET:
+ out_pb_config->set_ui_mode_type(pb::Configuration_UiModeType_UI_MODE_TYPE_VRHEADSET);
+ break;
+ }
+
+ switch (config.uiMode & ConfigDescription::MASK_UI_MODE_NIGHT) {
+ case ConfigDescription::UI_MODE_NIGHT_YES:
+ out_pb_config->set_ui_mode_night(pb::Configuration_UiModeNight_UI_MODE_NIGHT_NIGHT);
+ break;
+
+ case ConfigDescription::UI_MODE_NIGHT_NO:
+ out_pb_config->set_ui_mode_night(pb::Configuration_UiModeNight_UI_MODE_NIGHT_NOTNIGHT);
+ break;
+ }
+
+ out_pb_config->set_density(config.density);
+
+ switch (config.touchscreen) {
+ case ConfigDescription::TOUCHSCREEN_NOTOUCH:
+ out_pb_config->set_touchscreen(pb::Configuration_Touchscreen_TOUCHSCREEN_NOTOUCH);
+ break;
+
+ case ConfigDescription::TOUCHSCREEN_STYLUS:
+ out_pb_config->set_touchscreen(pb::Configuration_Touchscreen_TOUCHSCREEN_STYLUS);
+ break;
+
+ case ConfigDescription::TOUCHSCREEN_FINGER:
+ out_pb_config->set_touchscreen(pb::Configuration_Touchscreen_TOUCHSCREEN_FINGER);
+ break;
+ }
+
+ switch (config.inputFlags & ConfigDescription::MASK_KEYSHIDDEN) {
+ case ConfigDescription::KEYSHIDDEN_NO:
+ out_pb_config->set_keys_hidden(pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSEXPOSED);
+ break;
+
+ case ConfigDescription::KEYSHIDDEN_YES:
+ out_pb_config->set_keys_hidden(pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSHIDDEN);
+ break;
+
+ case ConfigDescription::KEYSHIDDEN_SOFT:
+ out_pb_config->set_keys_hidden(pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSSOFT);
+ break;
+ }
+
+ switch (config.keyboard) {
+ case ConfigDescription::KEYBOARD_NOKEYS:
+ out_pb_config->set_keyboard(pb::Configuration_Keyboard_KEYBOARD_NOKEYS);
+ break;
+
+ case ConfigDescription::KEYBOARD_QWERTY:
+ out_pb_config->set_keyboard(pb::Configuration_Keyboard_KEYBOARD_QWERTY);
+ break;
+
+ case ConfigDescription::KEYBOARD_12KEY:
+ out_pb_config->set_keyboard(pb::Configuration_Keyboard_KEYBOARD_TWELVEKEY);
+ break;
+ }
+
+ switch (config.inputFlags & ConfigDescription::MASK_NAVHIDDEN) {
+ case ConfigDescription::NAVHIDDEN_NO:
+ out_pb_config->set_nav_hidden(pb::Configuration_NavHidden_NAV_HIDDEN_NAVEXPOSED);
+ break;
+
+ case ConfigDescription::NAVHIDDEN_YES:
+ out_pb_config->set_nav_hidden(pb::Configuration_NavHidden_NAV_HIDDEN_NAVHIDDEN);
+ break;
+ }
+
+ switch (config.navigation) {
+ case ConfigDescription::NAVIGATION_NONAV:
+ out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_NONAV);
+ break;
+
+ case ConfigDescription::NAVIGATION_DPAD:
+ out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_DPAD);
+ break;
+
+ case ConfigDescription::NAVIGATION_TRACKBALL:
+ out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_TRACKBALL);
+ break;
+
+ case ConfigDescription::NAVIGATION_WHEEL:
+ out_pb_config->set_navigation(pb::Configuration_Navigation_NAVIGATION_WHEEL);
+ break;
+ }
+
+ out_pb_config->set_sdk_version(config.sdkVersion);
}
-bool DeserializeConfigDescriptionFromPb(const pb::ConfigDescription& pb_config,
+bool DeserializeConfigDescriptionFromPb(const pb::Configuration& pb_config,
ConfigDescription* out_config) {
- // a ConfigDescription must be at least 4 bytes to store the size.
- if (pb_config.data().size() < 4) {
- return false;
+ out_config->mcc = static_cast<uint16_t>(pb_config.mcc());
+ out_config->mnc = static_cast<uint16_t>(pb_config.mnc());
+
+ if (!pb_config.locale().empty()) {
+ LocaleValue lv;
+ if (!lv.InitFromBcp47Tag(pb_config.locale())) {
+ return false;
+ }
+ lv.WriteTo(out_config);
}
- const android::ResTable_config* config;
- if (pb_config.data().size() > sizeof(*config)) {
- return false;
+ switch (pb_config.layout_direction()) {
+ case pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_LTR:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_LAYOUTDIR) |
+ ConfigDescription::LAYOUTDIR_LTR;
+ break;
+
+ case pb::Configuration_LayoutDirection_LAYOUT_DIRECTION_RTL:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_LAYOUTDIR) |
+ ConfigDescription::LAYOUTDIR_RTL;
+ break;
+
+ default:
+ break;
}
- config = reinterpret_cast<const android::ResTable_config*>(pb_config.data().data());
- out_config->copyFromDtoH(*config);
+ out_config->smallestScreenWidthDp = static_cast<uint16_t>(pb_config.smallest_screen_width_dp());
+ out_config->screenWidthDp = static_cast<uint16_t>(pb_config.screen_width_dp());
+ out_config->screenHeightDp = static_cast<uint16_t>(pb_config.screen_height_dp());
+
+ switch (pb_config.screen_layout_size()) {
+ case pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_SMALL:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENSIZE) |
+ ConfigDescription::SCREENSIZE_SMALL;
+ break;
+
+ case pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_NORMAL:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENSIZE) |
+ ConfigDescription::SCREENSIZE_NORMAL;
+ break;
+
+ case pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_LARGE:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENSIZE) |
+ ConfigDescription::SCREENSIZE_LARGE;
+ break;
+
+ case pb::Configuration_ScreenLayoutSize_SCREEN_LAYOUT_SIZE_XLARGE:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENSIZE) |
+ ConfigDescription::SCREENSIZE_XLARGE;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.screen_layout_long()) {
+ case pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_LONG:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENLONG) |
+ ConfigDescription::SCREENLONG_YES;
+ break;
+
+ case pb::Configuration_ScreenLayoutLong_SCREEN_LAYOUT_LONG_NOTLONG:
+ out_config->screenLayout = (out_config->screenLayout & ~ConfigDescription::MASK_SCREENLONG) |
+ ConfigDescription::SCREENLONG_NO;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.screen_round()) {
+ case pb::Configuration_ScreenRound_SCREEN_ROUND_ROUND:
+ out_config->screenLayout2 =
+ (out_config->screenLayout2 & ~ConfigDescription::MASK_SCREENROUND) |
+ ConfigDescription::SCREENROUND_YES;
+ break;
+
+ case pb::Configuration_ScreenRound_SCREEN_ROUND_NOTROUND:
+ out_config->screenLayout2 =
+ (out_config->screenLayout2 & ~ConfigDescription::MASK_SCREENROUND) |
+ ConfigDescription::SCREENROUND_NO;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.wide_color_gamut()) {
+ case pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_WIDECG:
+ out_config->colorMode = (out_config->colorMode & ~ConfigDescription::MASK_WIDE_COLOR_GAMUT) |
+ ConfigDescription::WIDE_COLOR_GAMUT_YES;
+ break;
+
+ case pb::Configuration_WideColorGamut_WIDE_COLOR_GAMUT_NOWIDECG:
+ out_config->colorMode = (out_config->colorMode & ~ConfigDescription::MASK_WIDE_COLOR_GAMUT) |
+ ConfigDescription::WIDE_COLOR_GAMUT_NO;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.hdr()) {
+ case pb::Configuration_Hdr_HDR_HIGHDR:
+ out_config->colorMode =
+ (out_config->colorMode & ~ConfigDescription::MASK_HDR) | ConfigDescription::HDR_YES;
+ break;
+
+ case pb::Configuration_Hdr_HDR_LOWDR:
+ out_config->colorMode =
+ (out_config->colorMode & ~ConfigDescription::MASK_HDR) | ConfigDescription::HDR_NO;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.orientation()) {
+ case pb::Configuration_Orientation_ORIENTATION_PORT:
+ out_config->orientation = ConfigDescription::ORIENTATION_PORT;
+ break;
+
+ case pb::Configuration_Orientation_ORIENTATION_LAND:
+ out_config->orientation = ConfigDescription::ORIENTATION_LAND;
+ break;
+
+ case pb::Configuration_Orientation_ORIENTATION_SQUARE:
+ out_config->orientation = ConfigDescription::ORIENTATION_SQUARE;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.ui_mode_type()) {
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_NORMAL:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_NORMAL;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_DESK:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_DESK;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_CAR:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_CAR;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_TELEVISION:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_TELEVISION;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_APPLIANCE:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_APPLIANCE;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_WATCH:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_WATCH;
+ break;
+
+ case pb::Configuration_UiModeType_UI_MODE_TYPE_VRHEADSET:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_TYPE) |
+ ConfigDescription::UI_MODE_TYPE_VR_HEADSET;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.ui_mode_night()) {
+ case pb::Configuration_UiModeNight_UI_MODE_NIGHT_NIGHT:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_NIGHT) |
+ ConfigDescription::UI_MODE_NIGHT_YES;
+ break;
+
+ case pb::Configuration_UiModeNight_UI_MODE_NIGHT_NOTNIGHT:
+ out_config->uiMode = (out_config->uiMode & ~ConfigDescription::MASK_UI_MODE_NIGHT) |
+ ConfigDescription::UI_MODE_NIGHT_NO;
+ break;
+
+ default:
+ break;
+ }
+
+ out_config->density = static_cast<uint16_t>(pb_config.density());
+
+ switch (pb_config.touchscreen()) {
+ case pb::Configuration_Touchscreen_TOUCHSCREEN_NOTOUCH:
+ out_config->touchscreen = ConfigDescription::TOUCHSCREEN_NOTOUCH;
+ break;
+
+ case pb::Configuration_Touchscreen_TOUCHSCREEN_STYLUS:
+ out_config->touchscreen = ConfigDescription::TOUCHSCREEN_STYLUS;
+ break;
+
+ case pb::Configuration_Touchscreen_TOUCHSCREEN_FINGER:
+ out_config->touchscreen = ConfigDescription::TOUCHSCREEN_FINGER;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.keys_hidden()) {
+ case pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSEXPOSED:
+ out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_KEYSHIDDEN) |
+ ConfigDescription::KEYSHIDDEN_NO;
+ break;
+
+ case pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSHIDDEN:
+ out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_KEYSHIDDEN) |
+ ConfigDescription::KEYSHIDDEN_YES;
+ break;
+
+ case pb::Configuration_KeysHidden_KEYS_HIDDEN_KEYSSOFT:
+ out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_KEYSHIDDEN) |
+ ConfigDescription::KEYSHIDDEN_SOFT;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.keyboard()) {
+ case pb::Configuration_Keyboard_KEYBOARD_NOKEYS:
+ out_config->keyboard = ConfigDescription::KEYBOARD_NOKEYS;
+ break;
+
+ case pb::Configuration_Keyboard_KEYBOARD_QWERTY:
+ out_config->keyboard = ConfigDescription::KEYBOARD_QWERTY;
+ break;
+
+ case pb::Configuration_Keyboard_KEYBOARD_TWELVEKEY:
+ out_config->keyboard = ConfigDescription::KEYBOARD_12KEY;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.nav_hidden()) {
+ case pb::Configuration_NavHidden_NAV_HIDDEN_NAVEXPOSED:
+ out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_NAVHIDDEN) |
+ ConfigDescription::NAVHIDDEN_NO;
+ break;
+
+ case pb::Configuration_NavHidden_NAV_HIDDEN_NAVHIDDEN:
+ out_config->inputFlags = (out_config->inputFlags & ~ConfigDescription::MASK_NAVHIDDEN) |
+ ConfigDescription::NAVHIDDEN_YES;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (pb_config.navigation()) {
+ case pb::Configuration_Navigation_NAVIGATION_NONAV:
+ out_config->navigation = ConfigDescription::NAVIGATION_NONAV;
+ break;
+
+ case pb::Configuration_Navigation_NAVIGATION_DPAD:
+ out_config->navigation = ConfigDescription::NAVIGATION_DPAD;
+ break;
+
+ case pb::Configuration_Navigation_NAVIGATION_TRACKBALL:
+ out_config->navigation = ConfigDescription::NAVIGATION_TRACKBALL;
+ break;
+
+ case pb::Configuration_Navigation_NAVIGATION_WHEEL:
+ out_config->navigation = ConfigDescription::NAVIGATION_WHEEL;
+ break;
+
+ default:
+ break;
+ }
+
+ out_config->screenWidth = static_cast<uint16_t>(pb_config.screen_width());
+ out_config->screenHeight = static_cast<uint16_t>(pb_config.screen_height());
+ out_config->sdkVersion = static_cast<uint16_t>(pb_config.sdk_version());
return true;
}
diff --git a/tools/aapt2/proto/ProtoHelpers.h b/tools/aapt2/proto/ProtoHelpers.h
index 2f268f4..714a2b2 100644
--- a/tools/aapt2/proto/ProtoHelpers.h
+++ b/tools/aapt2/proto/ProtoHelpers.h
@@ -20,11 +20,12 @@
#include "androidfw/ResourceTypes.h"
#include "ConfigDescription.h"
+#include "Configuration.pb.h"
#include "ResourceTable.h"
-#include "Source.h"
-#include "StringPool.h"
#include "Resources.pb.h"
#include "ResourcesInternal.pb.h"
+#include "Source.h"
+#include "StringPool.h"
namespace aapt {
@@ -39,9 +40,9 @@
SymbolState DeserializeVisibilityFromPb(pb::SymbolStatus_Visibility pb_visibility);
-void SerializeConfig(const ConfigDescription& config, pb::ConfigDescription* out_pb_config);
+void SerializeConfig(const ConfigDescription& config, pb::Configuration* out_pb_config);
-bool DeserializeConfigDescriptionFromPb(const pb::ConfigDescription& pb_config,
+bool DeserializeConfigDescriptionFromPb(const pb::Configuration& pb_config,
ConfigDescription* out_config);
pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type);
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
index f4a2b1e..4a88d61 100644
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -123,7 +123,7 @@
}
for (const pb::ConfigValue& pb_config_value : pb_entry.config_value()) {
- const pb::ConfigDescription& pb_config = pb_config_value.config();
+ const pb::Configuration& pb_config = pb_config_value.config();
ConfigDescription config;
if (!DeserializeConfigDescriptionFromPb(pb_config, &config)) {
@@ -395,14 +395,16 @@
}
file->name = name_ref.ToResourceName();
file->source.path = pb_file.source_path();
- DeserializeConfigDescriptionFromPb(pb_file.config(), &file->config);
+ if (!DeserializeConfigDescriptionFromPb(pb_file.config(), &file->config)) {
+ diag->Error(DiagMessage(source) << "invalid resource configuration in compiled file header");
+ return {};
+ }
for (const pb::internal::CompiledFile_Symbol& pb_symbol : pb_file.exported_symbol()) {
// Need to create an lvalue here so that nameRef can point to something real.
if (!ResourceUtils::ParseResourceName(pb_symbol.resource_name(), &name_ref)) {
diag->Error(DiagMessage(source)
- << "invalid resource name for exported symbol in "
- "compiled file header: "
+ << "invalid resource name for exported symbol in compiled file header: "
<< pb_file.resource_name());
return {};
}
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
index 981b72a..3d5407c 100644
--- a/tools/aapt2/proto/TableProtoSerializer.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer.cpp
@@ -282,10 +282,10 @@
}
void CompiledFileOutputStream::EnsureAlignedWrite() {
- const int padding = out_.ByteCount() % 4;
- if (padding > 0) {
+ const int overflow = out_.ByteCount() % 4;
+ if (overflow > 0) {
uint32_t zero = 0u;
- out_.WriteRaw(&zero, padding);
+ out_.WriteRaw(&zero, 4 - overflow);
}
}
@@ -322,10 +322,10 @@
: in_(static_cast<const uint8_t*>(data), size) {}
void CompiledFileInputStream::EnsureAlignedRead() {
- const int padding = in_.CurrentPosition() % 4;
- if (padding > 0) {
+ const int overflow = in_.CurrentPosition() % 4;
+ if (overflow > 0) {
// Reads are always 4 byte aligned.
- in_.Skip(padding);
+ in_.Skip(4 - overflow);
}
}
diff --git a/tools/aapt2/proto/TableProtoSerializer_test.cpp b/tools/aapt2/proto/TableProtoSerializer_test.cpp
index 80608b3..8f6414c 100644
--- a/tools/aapt2/proto/TableProtoSerializer_test.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer_test.cpp
@@ -19,6 +19,7 @@
#include "ResourceTable.h"
#include "test/Test.h"
+using ::android::StringPiece;
using ::google::protobuf::io::StringOutputStream;
using ::testing::Eq;
using ::testing::NotNull;
@@ -239,4 +240,97 @@
EXPECT_FALSE(in_file_stream.ReadDataMetaData(&offset, &len));
}
+static void ExpectConfigSerializes(const StringPiece& config_str) {
+ const ConfigDescription expected_config = test::ParseConfigOrDie(config_str);
+ pb::Configuration pb_config;
+ SerializeConfig(expected_config, &pb_config);
+
+ ConfigDescription actual_config;
+ ASSERT_TRUE(DeserializeConfigDescriptionFromPb(pb_config, &actual_config));
+ EXPECT_EQ(expected_config, actual_config);
+}
+
+TEST(TableProtoSerializer, SerializeDeserializeConfiguration) {
+ ExpectConfigSerializes("");
+
+ ExpectConfigSerializes("mcc123");
+
+ ExpectConfigSerializes("mnc123");
+
+ ExpectConfigSerializes("en");
+ ExpectConfigSerializes("en-rGB");
+ ExpectConfigSerializes("b+en+GB");
+
+ ExpectConfigSerializes("ldltr");
+ ExpectConfigSerializes("ldrtl");
+
+ ExpectConfigSerializes("sw3600dp");
+
+ ExpectConfigSerializes("w300dp");
+
+ ExpectConfigSerializes("h400dp");
+
+ ExpectConfigSerializes("small");
+ ExpectConfigSerializes("normal");
+ ExpectConfigSerializes("large");
+ ExpectConfigSerializes("xlarge");
+
+ ExpectConfigSerializes("long");
+ ExpectConfigSerializes("notlong");
+
+ ExpectConfigSerializes("round");
+ ExpectConfigSerializes("notround");
+
+ ExpectConfigSerializes("widecg");
+ ExpectConfigSerializes("nowidecg");
+
+ ExpectConfigSerializes("highdr");
+ ExpectConfigSerializes("lowdr");
+
+ ExpectConfigSerializes("port");
+ ExpectConfigSerializes("land");
+ ExpectConfigSerializes("square");
+
+ ExpectConfigSerializes("desk");
+ ExpectConfigSerializes("car");
+ ExpectConfigSerializes("television");
+ ExpectConfigSerializes("appliance");
+ ExpectConfigSerializes("watch");
+ ExpectConfigSerializes("vrheadset");
+
+ ExpectConfigSerializes("night");
+ ExpectConfigSerializes("notnight");
+
+ ExpectConfigSerializes("300dpi");
+ ExpectConfigSerializes("hdpi");
+
+ ExpectConfigSerializes("notouch");
+ ExpectConfigSerializes("stylus");
+ ExpectConfigSerializes("finger");
+
+ ExpectConfigSerializes("keysexposed");
+ ExpectConfigSerializes("keyshidden");
+ ExpectConfigSerializes("keyssoft");
+
+ ExpectConfigSerializes("nokeys");
+ ExpectConfigSerializes("qwerty");
+ ExpectConfigSerializes("12key");
+
+ ExpectConfigSerializes("navhidden");
+ ExpectConfigSerializes("navexposed");
+
+ ExpectConfigSerializes("nonav");
+ ExpectConfigSerializes("dpad");
+ ExpectConfigSerializes("trackball");
+ ExpectConfigSerializes("wheel");
+
+ ExpectConfigSerializes("300x200");
+
+ ExpectConfigSerializes("v8");
+
+ ExpectConfigSerializes(
+ "mcc123-mnc456-b+en+GB-ldltr-sw300dp-w300dp-h400dp-large-long-round-widecg-highdr-land-car-"
+ "night-xhdpi-stylus-keysexposed-qwerty-navhidden-dpad-300x200-v23");
+}
+
} // namespace aapt