Implement issue #73301635: Ability to extract device configuration

am get-config can now return its information as a protobuf,
so that we have a nice machine readable representation of the
configuration available.  This is available through the new
"--proto" option.

Also add a new "--device" option which will include additional
device configuration information in the output.

Defined the protos to contain all of this information.  Updated
Configuration to generate everything it should in the proto.

The output has been changed so that configuration history
will not be returned unless you specify the --days option.

Bug: 73301635
Test: Booted, ran, output

Change-Id: I074b92b45d6b1da1c1a499080db9e006d12b9fea
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index eb30979..93690bf 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -18,13 +18,27 @@
 
 import static android.content.ConfigurationProto.DENSITY_DPI;
 import static android.content.ConfigurationProto.FONT_SCALE;
+import static android.content.ConfigurationProto.HARD_KEYBOARD_HIDDEN;
+import static android.content.ConfigurationProto.HDR_COLOR_MODE;
+import static android.content.ConfigurationProto.KEYBOARD_HIDDEN;
+import static android.content.ConfigurationProto.LOCALES;
+import static android.content.ConfigurationProto.MCC;
+import static android.content.ConfigurationProto.MNC;
+import static android.content.ConfigurationProto.NAVIGATION;
+import static android.content.ConfigurationProto.NAVIGATION_HIDDEN;
 import static android.content.ConfigurationProto.ORIENTATION;
 import static android.content.ConfigurationProto.SCREEN_HEIGHT_DP;
 import static android.content.ConfigurationProto.SCREEN_LAYOUT;
 import static android.content.ConfigurationProto.SCREEN_WIDTH_DP;
 import static android.content.ConfigurationProto.SMALLEST_SCREEN_WIDTH_DP;
+import static android.content.ConfigurationProto.TOUCHSCREEN;
 import static android.content.ConfigurationProto.UI_MODE;
+import static android.content.ConfigurationProto.WIDE_COLOR_GAMUT;
 import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
+import static android.content.ResourcesConfigurationProto.CONFIGURATION;
+import static android.content.ResourcesConfigurationProto.SCREEN_HEIGHT_PX;
+import static android.content.ResourcesConfigurationProto.SCREEN_WIDTH_PX;
+import static android.content.ResourcesConfigurationProto.SDK_VERSION;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -38,6 +52,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
+import android.util.DisplayMetrics;
 import android.util.proto.ProtoOutputStream;
 import android.view.View;
 
@@ -1076,7 +1091,19 @@
     public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
         final long token = protoOutputStream.start(fieldId);
         protoOutputStream.write(FONT_SCALE, fontScale);
+        protoOutputStream.write(MCC, mcc);
+        protoOutputStream.write(MNC, mnc);
+        mLocaleList.writeToProto(protoOutputStream, LOCALES);
         protoOutputStream.write(SCREEN_LAYOUT, screenLayout);
+        protoOutputStream.write(HDR_COLOR_MODE,
+                (colorMode & Configuration.COLOR_MODE_HDR_MASK) >> COLOR_MODE_HDR_SHIFT);
+        protoOutputStream.write(WIDE_COLOR_GAMUT,
+                colorMode & Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_MASK);
+        protoOutputStream.write(TOUCHSCREEN, touchscreen);
+        protoOutputStream.write(KEYBOARD_HIDDEN, keyboardHidden);
+        protoOutputStream.write(HARD_KEYBOARD_HIDDEN, hardKeyboardHidden);
+        protoOutputStream.write(NAVIGATION, navigation);
+        protoOutputStream.write(NAVIGATION_HIDDEN, navigationHidden);
         protoOutputStream.write(ORIENTATION, orientation);
         protoOutputStream.write(UI_MODE, uiMode);
         protoOutputStream.write(SCREEN_WIDTH_DP, screenWidthDp);
@@ -1088,6 +1115,36 @@
     }
 
     /**
+     * Write full {@link android.content.ResourcesConfigurationProto} to protocol buffer output
+     * stream.
+     *
+     * @param protoOutputStream Stream to write the Configuration object to.
+     * @param fieldId           Field Id of the Configuration as defined in the parent message
+     * @param metrics           Current display information
+     * @hide
+     */
+    public void writeResConfigToProto(ProtoOutputStream protoOutputStream, long fieldId,
+            DisplayMetrics metrics) {
+        final int width, height;
+        if (metrics.widthPixels >= metrics.heightPixels) {
+            width = metrics.widthPixels;
+            height = metrics.heightPixels;
+        } else {
+            //noinspection SuspiciousNameCombination
+            width = metrics.heightPixels;
+            //noinspection SuspiciousNameCombination
+            height = metrics.widthPixels;
+        }
+
+        final long token = protoOutputStream.start(fieldId);
+        writeToProto(protoOutputStream, CONFIGURATION);
+        protoOutputStream.write(SDK_VERSION, Build.VERSION.RESOURCES_SDK_INT);
+        protoOutputStream.write(SCREEN_WIDTH_PX, width);
+        protoOutputStream.write(SCREEN_HEIGHT_PX, height);
+        protoOutputStream.end(token);
+    }
+
+    /**
      * Convert the UI mode to a human readable format.
      * @hide
      */
@@ -1925,11 +1982,21 @@
 
     /**
      * Returns a string representation of the configuration that can be parsed
-     * by build tools (like AAPT).
+     * by build tools (like AAPT), without display metrics included
      *
      * @hide
      */
     public static String resourceQualifierString(Configuration config) {
+        return resourceQualifierString(config, null);
+    }
+
+    /**
+     * Returns a string representation of the configuration that can be parsed
+     * by build tools (like AAPT).
+     *
+     * @hide
+     */
+    public static String resourceQualifierString(Configuration config, DisplayMetrics metrics) {
         ArrayList<String> parts = new ArrayList<String>();
 
         if (config.mcc != 0) {
@@ -2177,6 +2244,20 @@
                 break;
         }
 
+        if (metrics != null) {
+            final int width, height;
+            if (metrics.widthPixels >= metrics.heightPixels) {
+                width = metrics.widthPixels;
+                height = metrics.heightPixels;
+            } else {
+                //noinspection SuspiciousNameCombination
+                width = metrics.heightPixels;
+                //noinspection SuspiciousNameCombination
+                height = metrics.widthPixels;
+            }
+            parts.add(width + "x" + height);
+        }
+
         parts.add("v" + Build.VERSION.RESOURCES_SDK_INT);
         return TextUtils.join("-", parts);
     }
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index ca9cbec..87e1b7d 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -20,7 +20,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.content.LocaleProto;
 import android.icu.util.ULocale;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -140,6 +142,25 @@
     }
 
     /**
+     * Helper to write LocaleList to a protocol buffer output stream.  Assumes the parent
+     * protobuf has declared the locale as repeated.
+     *
+     * @param protoOutputStream Stream to write the locale to.
+     * @param fieldId Field Id of the Locale as defined in the parent message.
+     * @hide
+     */
+    public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+        for (int i = 0; i < mList.length; i++) {
+            final Locale locale = mList[i];
+            final long token = protoOutputStream.start(fieldId);
+            protoOutputStream.write(LocaleProto.LANGUAGE, locale.getLanguage());
+            protoOutputStream.write(LocaleProto.COUNTRY, locale.getCountry());
+            protoOutputStream.write(LocaleProto.VARIANT, locale.getVariant());
+            protoOutputStream.end(token);
+        }
+    }
+
+    /**
      * Retrieves a String representation of the language tags in this list.
      */
     @NonNull
diff --git a/core/proto/android/content/configuration.proto b/core/proto/android/content/configuration.proto
index a62d56c..834ecde 100644
--- a/core/proto/android/content/configuration.proto
+++ b/core/proto/android/content/configuration.proto
@@ -25,7 +25,7 @@
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 /**
- * An android resource configuration.
+ * An android Configuration object.
  */
 message ConfigurationProto {
   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -35,17 +35,65 @@
   optional uint32 mnc = 3;
   repeated LocaleProto locales = 4;
   optional uint32 screen_layout = 5;
-  optional uint32 touchscreen = 6;
-  optional uint32 keyboard_hidden = 7;
-  optional uint32 hard_keyboard_hidden = 8;
-  optional uint32 navigation = 9;
-  optional uint32 navigation_hidden = 10;
-  optional uint32 orientation = 11;
-  optional uint32 ui_mode = 12;
-  optional uint32 screen_width_dp = 13;
-  optional uint32 screen_height_dp = 14;
-  optional uint32 smallest_screen_width_dp = 15;
-  optional uint32 density_dpi = 16;
-  optional .android.app.WindowConfigurationProto window_configuration = 17;
+  optional uint32 hdr_color_mode = 6;
+  optional uint32 wide_color_gamut = 7;
+  optional uint32 touchscreen = 8;
+  optional uint32 keyboard_hidden = 9;
+  optional uint32 hard_keyboard_hidden = 10;
+  optional uint32 navigation = 11;
+  optional uint32 navigation_hidden = 12;
+  optional uint32 orientation = 13;
+  optional uint32 ui_mode = 14;
+  optional uint32 screen_width_dp = 15;
+  optional uint32 screen_height_dp = 16;
+  optional uint32 smallest_screen_width_dp = 17;
+  optional uint32 density_dpi = 18;
+  optional .android.app.WindowConfigurationProto window_configuration = 19;
 }
 
+/**
+ * All current configuration data used to select resources.
+ */
+message ResourcesConfigurationProto {
+  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  required ConfigurationProto configuration = 1;
+
+  optional uint32 sdk_version = 2;
+  optional uint32 screen_width_px = 3;
+  optional uint32 screen_height_px = 4;
+}
+
+/**
+ * Overall device configuration data.
+ */
+message DeviceConfigurationProto {
+  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  optional uint32 stable_screen_width_px = 1;
+  optional uint32 stable_screen_height_px = 2;
+  optional uint32 stable_density_dpi = 3;
+
+  optional uint64 total_ram = 4;
+  optional bool low_ram = 5;
+  optional uint32 max_cores = 6;
+  optional bool has_secure_screen_lock = 7;
+
+  optional uint32 opengl_version = 8;
+  repeated string opengl_extensions = 9;
+
+  repeated string shared_libraries = 10;
+  repeated string features = 11;
+  repeated string cpu_architectures = 12;
+}
+
+/**
+ * All current configuration data device is running with, everything used
+ * to filter and target apps.
+ */
+message GlobalConfigurationProto {
+  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  optional ResourcesConfigurationProto resources = 1;
+  optional DeviceConfigurationProto device = 2;
+}