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

Commit 8ae9a37b authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Implement issue #73301635: Ability to extract device configuration"

parents c57ee73c bf5ba6bb
Loading
Loading
Loading
Loading
+82 −1
Original line number Diff line number Diff line
@@ -18,13 +18,27 @@ package android.content.res;

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.LocaleList;
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 final class Configuration implements Parcelable, Comparable<Configuration
    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);
@@ -1087,6 +1114,36 @@ public final class Configuration implements Parcelable, Comparable<Configuration
        protoOutputStream.end(token);
    }

    /**
     * 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 @@ public final class Configuration implements Parcelable, Comparable<Configuration

    /**
     * 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 @@ public final class Configuration implements Parcelable, Comparable<Configuration
                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);
    }
+21 −0
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@ import android.annotation.IntRange;
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;

@@ -139,6 +141,25 @@ public final class LocaleList implements Parcelable {
        dest.writeString(mStringRepresentation);
    }

    /**
     * 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.
     */
+61 −13
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@ import "frameworks/base/core/proto/android/content/locale.proto";
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 @@ message ConfigurationProto {
  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;
}
+156 −21
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.app.IActivityController;
import android.app.IActivityManager;
import android.app.IStopUserCallback;
import android.app.IUidObserver;
import android.app.KeyguardManager;
import android.app.ProfilerInfo;
import android.app.WaitResult;
import android.app.usage.AppStandbyInfo;
@@ -32,16 +33,24 @@ import android.app.usage.UsageStatsManager;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.Context;
import android.content.DeviceConfigurationProto;
import android.content.GlobalConfigurationProto;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.ConfigurationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.UserInfo;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -57,8 +66,11 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DebugUtils;
import android.util.DisplayMetrics;
import android.util.proto.ProtoOutputStream;
import android.view.Display;

import com.android.internal.util.HexDump;
import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;

import java.io.BufferedReader;
@@ -75,6 +87,11 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;

import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityManager.RESIZE_MODE_USER;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
@@ -1835,18 +1852,113 @@ final class ActivityManagerShellCommand extends ShellCommand {
        }
    }

    int runGetConfig(PrintWriter pw) throws RemoteException {
        int days = 14;
        String option = getNextOption();
        if (option != null) {
            if (!option.equals("--days")) {
                throw new IllegalArgumentException("unrecognized option " + option);
    private void writeDeviceConfig(ProtoOutputStream protoOutputStream, long fieldId,
            PrintWriter pw, Configuration config, DisplayManager dm) {
        Point stableSize = dm.getStableDisplaySize();
        long token = -1;
        if (protoOutputStream != null) {
            token = protoOutputStream.start(fieldId);
            protoOutputStream.write(DeviceConfigurationProto.STABLE_SCREEN_WIDTH_PX, stableSize.x);
            protoOutputStream.write(DeviceConfigurationProto.STABLE_SCREEN_HEIGHT_PX, stableSize.y);
            protoOutputStream.write(DeviceConfigurationProto.STABLE_DENSITY_DPI,
                    DisplayMetrics.DENSITY_DEVICE_STABLE);
        }
        if (pw != null) {
            pw.print("stable-width-px: "); pw.println(stableSize.x);
            pw.print("stable-height-px: "); pw.println(stableSize.y);
            pw.print("stable-density-dpi: "); pw.println(DisplayMetrics.DENSITY_DEVICE_STABLE);
        }

        MemInfoReader memreader = new MemInfoReader();
        memreader.readMemInfo();
        KeyguardManager kgm = mInternal.mContext.getSystemService(KeyguardManager.class);
        if (protoOutputStream != null) {
            protoOutputStream.write(DeviceConfigurationProto.TOTAL_RAM, memreader.getTotalSize());
            protoOutputStream.write(DeviceConfigurationProto.LOW_RAM,
                    ActivityManager.isLowRamDeviceStatic());
            protoOutputStream.write(DeviceConfigurationProto.MAX_CORES,
                    Runtime.getRuntime().availableProcessors());
            protoOutputStream.write(DeviceConfigurationProto.HAS_SECURE_SCREEN_LOCK,
                    kgm.isDeviceSecure());
        }
        if (pw != null) {
            pw.print("total-ram: "); pw.println(memreader.getTotalSize());
            pw.print("low-ram: "); pw.println(ActivityManager.isLowRamDeviceStatic());
            pw.print("max-cores: "); pw.println(Runtime.getRuntime().availableProcessors());
            pw.print("has-secure-screen-lock: "); pw.println(kgm.isDeviceSecure());
        }

        ConfigurationInfo configInfo = mInternal.getDeviceConfigurationInfo();
        if (configInfo.reqGlEsVersion != ConfigurationInfo.GL_ES_VERSION_UNDEFINED) {
            if (protoOutputStream != null) {
                protoOutputStream.write(DeviceConfigurationProto.OPENGL_VERSION,
                        configInfo.reqGlEsVersion);
            }
            if (pw != null) {
                pw.print("opengl-version: 0x");
                pw.println(Integer.toHexString(configInfo.reqGlEsVersion));
            }
        }

        /*
        GL10 gl = ((GL10)((EGL10)EGLContext.getEGL()).eglGetCurrentContext().getGL());
        protoOutputStream.write(DeviceConfigurationProto.OPENGL_VERSION,
                gl.glGetString(GL10.GL_VERSION));
        String glExtensions = gl.glGetString(GL10.GL_EXTENSIONS);
        for (String ext : glExtensions.split(" ")) {
            protoOutputStream.write(DeviceConfigurationProto.OPENGL_EXTENSIONS, ext);
        }
        */

        PackageManager pm = mInternal.mContext.getPackageManager();
        List<SharedLibraryInfo> slibs = pm.getSharedLibraries(0);
        for (int i = 0; i < slibs.size(); i++) {
            if (protoOutputStream != null) {
                protoOutputStream.write(DeviceConfigurationProto.SHARED_LIBRARIES,
                        slibs.get(i).getName());
            }
            if (pw != null) {
                pw.print("shared-libraries: "); pw.println(slibs.get(i).getName());
            }
        }

        FeatureInfo[] features = pm.getSystemAvailableFeatures();
        for (int i = 0; i < features.length; i++) {
            if (features[i].name != null) {
                if (protoOutputStream != null) {
                    protoOutputStream.write(DeviceConfigurationProto.FEATURES, features[i].name);
                }
                if (pw != null) {
                    pw.print("features: "); pw.println(features[i].name);
                }
            }
        }

        if (protoOutputStream != null) {
            protoOutputStream.end(token);
        }
    }

    int runGetConfig(PrintWriter pw) throws RemoteException {
        int days = -1;
        boolean asProto = false;
        boolean inclDevice = false;

        String opt;
        while ((opt=getNextOption()) != null) {
            if (opt.equals("--days")) {
                days = Integer.parseInt(getNextArgRequired());
                if (days <= 0) {
                    throw new IllegalArgumentException("--days must be a positive integer");
                }
            } else if (opt.equals("--proto")) {
                asProto = true;
            } else if (opt.equals("--device")) {
                inclDevice = true;
            } else {
                getErrPrintWriter().println("Error: Unknown option: " + opt);
                return -1;
            }
        }

        Configuration config = mInterface.getConfiguration();
@@ -1855,19 +1967,39 @@ final class ActivityManagerShellCommand extends ShellCommand {
            return -1;
        }

        pw.println("config: " + Configuration.resourceQualifierString(config));
        DisplayManager dm = mInternal.mContext.getSystemService(DisplayManager.class);
        Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
        DisplayMetrics metrics = new DisplayMetrics();
        display.getMetrics(metrics);

        if (asProto) {
            final ProtoOutputStream proto = new ProtoOutputStream(getOutFileDescriptor());
            config.writeResConfigToProto(proto, GlobalConfigurationProto.RESOURCES, metrics);
            if (inclDevice) {
                writeDeviceConfig(proto, GlobalConfigurationProto.DEVICE, null, config, dm);
            }
            proto.flush();

        } else {
            pw.println("config: " + Configuration.resourceQualifierString(config, metrics));
            pw.println("abi: " + TextUtils.join(",", Build.SUPPORTED_ABIS));
            if (inclDevice) {
                writeDeviceConfig(null, -1, pw, config, dm);
            }

            if (days >= 0) {
                final List<Configuration> recentConfigs = getRecentConfigurations(days);
                final int recentConfigSize = recentConfigs.size();
                if (recentConfigSize > 0) {
                    pw.println("recentConfigs:");
        }

                    for (int i = 0; i < recentConfigSize; i++) {
                        pw.println("  config: " + Configuration.resourceQualifierString(
                                recentConfigs.get(i)));
                    }
                }
            }

        }
        return 0;
    }

@@ -2729,8 +2861,11 @@ final class ActivityManagerShellCommand extends ShellCommand {
            pw.println("      Gets the process state of an app given its <UID>.");
            pw.println("  attach-agent <PROCESS> <FILE>");
            pw.println("    Attach an agent to the specified <PROCESS>, which may be either a process name or a PID.");
            pw.println("  get-config");
            pw.println("      Rtrieve the configuration and any recent configurations of the device.");
            pw.println("  get-config [--days N] [--device] [--proto]");
            pw.println("      Retrieve the configuration and any recent configurations of the device.");
            pw.println("      --days: also return last N days of configurations that have been seen.");
            pw.println("      --device: also output global device configuration info.");
            pw.println("      --proto: return result as a proto; does not include --days info.");
            pw.println("  supports-multiwindow");
            pw.println("      Returns true if the device supports multiwindow.");
            pw.println("  supports-split-screen-multi-window");