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