Snap for 10453563 from 4eb7b888ab394100643c6500e06e3b31f1780760 to mainline-tzdata5-release

Change-Id: I4895ad412593f7eab35363332240542322685ffc
diff --git a/Settings/AndroidManifest.xml b/Settings/AndroidManifest.xml
index d077ad9..db2d4ce 100644
--- a/Settings/AndroidManifest.xml
+++ b/Settings/AndroidManifest.xml
@@ -1304,8 +1304,17 @@
             </intent-filter>
         </activity>
 
-        <service android:name=".device.eco.EnergyModesService"
-            android:exported="true" />
+        <service android:name=".device.eco.EnergyModesStatsLogJobService"
+            android:permission="android.permission.BIND_JOB_SERVICE" />
+
+        <provider
+            android:name=".device.eco.EnergyModesContentProvider"
+            android:authorities="com.android.tv.settings.device.eco.energymodes"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.tv.settings.ENERGY_MODES_PROVIDER"/>
+            </intent-filter>
+        </provider>
 
         <activity
             android:name=".device.eco.EnergyModesActivity"
diff --git a/Settings/res/values/energy-modes.xml b/Settings/res/values/energy-modes.xml
index 50a1374..1827962 100644
--- a/Settings/res/values/energy-modes.xml
+++ b/Settings/res/values/energy-modes.xml
@@ -21,6 +21,9 @@
          If false, falls back to a toggle to enable/disable Low Power Standby if LPS is supported. -->
     <bool name="enable_energy_modes">true</bool>
 
+    <!-- The default energy mode, used if none has been set yet. -->
+    <string name="default_energy_mode" translatable="false">@null</string>
+
     <!-- Energy mode: Low -->
     <eat-comment />
 
diff --git a/Settings/res/values/integers.xml b/Settings/res/values/integers.xml
index a65838f..68960fc 100644
--- a/Settings/res/values/integers.xml
+++ b/Settings/res/values/integers.xml
@@ -14,6 +14,9 @@
      limitations under the License.
 -->
 <resources>
+    <!-- Reserve all the job ids in TvSettings -->
+    <integer name="job_energy_modes_stats_log">100</integer>
+
     <!-- Pin dialog -->
     <integer name="pin_dialog_anim_duration">250</integer>
     <integer name="pin_dialog_enter_offset_y">32</integer>
diff --git a/Settings/res/values/strings.xml b/Settings/res/values/strings.xml
index 29245e7..2220972 100644
--- a/Settings/res/values/strings.xml
+++ b/Settings/res/values/strings.xml
@@ -579,7 +579,7 @@
     <!-- Description of resolution mode that is shown on the side panel when user is scrolling
         through resolution modes. It lists all HDR types that this mode supports.
         [CHAR LIMIT=300]-->
-    <string name="resolution_hdr_description_info">This mode supports %1$s. On some TVs, you may
+    <string name="resolution_hdr_description_info">This mode supports: %1$s\nOn some TVs, you may
         need to turn on Enhanced HDMI to enable more HDR formats. Check your TV settings to see if
         this is supported.</string>
     <!-- Cancel button title for dialog that is shown when user changes the resolution.
@@ -899,10 +899,8 @@
     <string name="do_disclosure_with_name">This device is managed by <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g>.</string>
     <!-- Message indicating that the device is enterprise-managed: Space that separates the main text and the "learn more" link that follows it. [CHAR LIMIT=NONE] -->
     <string name="do_disclosure_learn_more_separator">" "</string>
-    <!-- A word separator (e.g. a comma), indicating a pause between parts of a sentence or separating items in a list. [CHAR LIMIT=NONE] -->
-    <string name="word_separator">,</string>
-    <!-- A space, used for separating words -->
-    <string name="space_separator">" "</string>
+    <!-- An HDR capability[CHAR LIMIT=NONE] -->
+    <string name="hdr_capability">- %1$s</string>
     <!-- Button label to allow the user to view additional information [CHAR LIMIT=NONE BACKUP_MESSAGE_ID=2416766240581561009] -->
     <string name="learn_more">Learn more</string>
 
diff --git a/Settings/src/com/android/tv/settings/TvSettingsApplication.java b/Settings/src/com/android/tv/settings/TvSettingsApplication.java
index 5a62e5b..2e88e29 100644
--- a/Settings/src/com/android/tv/settings/TvSettingsApplication.java
+++ b/Settings/src/com/android/tv/settings/TvSettingsApplication.java
@@ -20,6 +20,8 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.tv.settings.device.eco.EnergyModesStatsLogJobService;
+
 /**
  * Application class that instantiates system sound player singleton so sound effects are only
  * loaded once and shared between components.
@@ -33,6 +35,8 @@
         if (getResources().getBoolean(R.bool.config_enableSystemSounds)) {
             mSystemSoundsPlayer = new SystemSoundsPlayer(this);
         }
+
+        EnergyModesStatsLogJobService.scheduleEnergyModesStatsLog(this);
     }
 
     @Nullable
diff --git a/Settings/src/com/android/tv/settings/device/apps/specialaccess/NotificationAccess.java b/Settings/src/com/android/tv/settings/device/apps/specialaccess/NotificationAccess.java
index 2caef3d..df13211 100644
--- a/Settings/src/com/android/tv/settings/device/apps/specialaccess/NotificationAccess.java
+++ b/Settings/src/com/android/tv/settings/device/apps/specialaccess/NotificationAccess.java
@@ -49,6 +49,8 @@
 public class NotificationAccess extends SettingsPreferenceFragment {
     private static final String TAG = "NotificationAccess";
 
+    private static final int MAX_CN_LENGTH = 500;
+
     private static final String HEADER_KEY = "header";
 
     private static final String DEFAULT_PACKAGES_SEPARATOR = ":";
@@ -76,6 +78,12 @@
                 .setIntentAction(NotificationListenerService.SERVICE_INTERFACE)
                 .setPermission(android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE)
                 .setNoun("notification listener")
+                .setValidator(info -> {
+                    if (info.getComponentName().flattenToString().length() > MAX_CN_LENGTH) {
+                        return false;
+                    }
+                    return true;
+                 })
                 .build();
         mServiceListing.addCallback(this::updateList);
     }
diff --git a/Settings/src/com/android/tv/settings/device/displaysound/ResolutionSelectionFragment.java b/Settings/src/com/android/tv/settings/device/displaysound/ResolutionSelectionFragment.java
index b7426e7..db515ba 100644
--- a/Settings/src/com/android/tv/settings/device/displaysound/ResolutionSelectionFragment.java
+++ b/Settings/src/com/android/tv/settings/device/displaysound/ResolutionSelectionFragment.java
@@ -169,7 +169,8 @@
                         mode.getPhysicalWidth(), mode.getPhysicalHeight()),
                 ResolutionSelectionUtils.getRefreshRateString(mode.getRefreshRate()));
 
-        String summary = mode.getPhysicalWidth() + " x " + mode.getPhysicalHeight();
+        String summary = ResolutionSelectionUtils.getResolutionSummary(mode.getPhysicalWidth(),
+                mode.getPhysicalHeight());
         RadioPreference pref = new RadioPreference(getContext());
         pref.setTitle(title);
         pref.setSummary(summary);
diff --git a/Settings/src/com/android/tv/settings/device/displaysound/ResolutionSelectionInfo.java b/Settings/src/com/android/tv/settings/device/displaysound/ResolutionSelectionInfo.java
index 73463b2..3f3c9ff 100644
--- a/Settings/src/com/android/tv/settings/device/displaysound/ResolutionSelectionInfo.java
+++ b/Settings/src/com/android/tv/settings/device/displaysound/ResolutionSelectionInfo.java
@@ -44,28 +44,32 @@
     public static class HDRInfoFragment extends ResolutionSelectionInfo.BaseInfoFragment {
 
         private String hdrTypesAsString(int[] hdrTypes) {
-            StringBuilder supportedTypes = new StringBuilder(
-                    getResources().getString(R.string.hdr_format_sdr));
+            StringBuilder supportedTypes = new StringBuilder(System.lineSeparator()).append(
+                    getResources().getString(R.string.hdr_capability,
+                            getResources().getString(R.string.hdr_format_sdr)));
             for (int supportedType : hdrTypes) {
-                supportedTypes.append(getResources().getString(R.string.word_separator))
-                        .append(getResources().getString(
-                                R.string.space_separator));
+                supportedTypes.append(System.lineSeparator());
                 switch (supportedType) {
                     case HDR_TYPE_DOLBY_VISION:
                         supportedTypes
-                                .append(getResources().getString(R.string.hdr_format_dolby_vision));
+                                .append(getResources().getString(R.string.hdr_capability,
+                                        getResources().getString(
+                                                R.string.hdr_format_dolby_vision)));
                         break;
                     case HDR_TYPE_HDR10:
                         supportedTypes
-                                .append(getResources().getString(R.string.hdr_format_hdr10));
+                                .append(getResources().getString(R.string.hdr_capability,
+                                        getResources().getString(R.string.hdr_format_hdr10)));
                         break;
                     case HDR_TYPE_HLG:
                         supportedTypes
-                                .append(getResources().getString(R.string.hdr_format_hlg));
+                                .append(getResources().getString(R.string.hdr_capability,
+                                        getResources().getString(R.string.hdr_format_hlg)));
                         break;
                     case HDR_TYPE_HDR10_PLUS:
                         supportedTypes
-                                .append(getResources().getString(R.string.hdr_format_hdr10plus));
+                                .append(getResources().getString(R.string.hdr_capability,
+                                        getResources().getString(R.string.hdr_format_hdr10plus)));
                         break;
                 }
             }
diff --git a/Settings/src/com/android/tv/settings/device/eco/EnergyModesContentProvider.java b/Settings/src/com/android/tv/settings/device/eco/EnergyModesContentProvider.java
new file mode 100644
index 0000000..18265cd
--- /dev/null
+++ b/Settings/src/com/android/tv/settings/device/eco/EnergyModesContentProvider.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+package com.android.tv.settings.device.eco;
+
+import static android.Manifest.permission.MANAGE_LOW_POWER_STANDBY;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.tv.settings.device.eco.EnergyModesHelper.EnergyMode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * ContentProvider that provides methods to query and set Energy Modes.
+ */
+public class EnergyModesContentProvider extends ContentProvider {
+    /** Method to get available energy modes, default mode, and selected mode */
+    private static final String METHOD_GET_ENERGY_MODES = "getEnergyModes";
+
+    /**
+     * Method to set the selected energy mode.
+     * Requires permission MANAGE_LOW_POWER_STANDBY.
+     */
+    private static final String METHOD_SET_ENERGY_MODE = "setEnergyMode";
+
+    /** Key for a String representing the identifier of the default mode (may be null). */
+    private static final String KEY_DEFAULT_MODE = "default_mode";
+
+    /** Key for a String representing the currently selected mode. */
+    private static final String KEY_SELECTED_MODE = "selected_mode";
+
+    /** Key for a List of Bundle representing the available energy modes. */
+    private static final String KEY_ENERGY_MODES = "energy_modes";
+
+    /** Key for a String representing the identifier for an energy mode. */
+    private static final String KEY_IDENTIFIER = "identifier";
+
+    /** Key for an Icon representing the icon of an energy mode. */
+    private static final String KEY_ICON = "icon";
+
+    /** Key for an int representing the color of an energy mode (in ARGB). */
+    private static final String KEY_COLOR = "color";
+
+    /** Key for a String representing the title of an energy mode. */
+    private static final String KEY_TITLE = "title";
+
+    /** Key for a String representing the subtitle of an energy mode. */
+    private static final String KEY_SUBTITLE = "subtitle";
+
+    /** Key for a String representing the description of an energy mode. */
+    private static final String KEY_DESCRIPTION = "description";
+
+    /** Key for a String representing a short description of an energy mode. */
+    private static final String KEY_SHORT_DESCRIPTION = "short_description";
+
+    /** Key for a String array representing the (human-friendly) features of an energy mode. */
+    private static final String KEY_FEATURES_LIST = "features_list";
+
+    /** Key for a String array representing the features of an energy mode. */
+    private static final String KEY_FEATURES = "features";
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Nullable
+    @Override
+    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
+            @Nullable String[] selectionArgs, @Nullable String sortOrder) {
+        throw new UnsupportedOperationException("query operation not supported currently.");
+    }
+
+    @Nullable
+    @Override
+    public String getType(@NonNull Uri uri) {
+        throw new UnsupportedOperationException("getType operation not supported currently.");
+    }
+
+    @Nullable
+    @Override
+    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
+        throw new UnsupportedOperationException("insert operation not supported currently.");
+    }
+
+    @Override
+    public int delete(@NonNull Uri uri, @Nullable String selection,
+            @Nullable String[] selectionArgs) {
+        throw new UnsupportedOperationException("delete operation not supported currently.");
+    }
+
+    @Override
+    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
+            @Nullable String[] selectionArgs) {
+        throw new UnsupportedOperationException("update operation not supported currently.");
+    }
+
+    @Override
+    public Bundle call(String method, String arg, Bundle extras) {
+        if (METHOD_GET_ENERGY_MODES.equals(method)) {
+            return getEnergyModesFromBinder();
+        } else if (METHOD_SET_ENERGY_MODE.equals(method)) {
+            return setEnergyModeFromBinder(arg);
+        }
+
+        throw new IllegalArgumentException("Unknown method name");
+    }
+
+    private Bundle getEnergyModesFromBinder() {
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            return getEnergyModes();
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private Bundle getEnergyModes() {
+        EnergyModesHelper energyModesHelper = new EnergyModesHelper(getContext());
+
+        final EnergyMode defaultMode = energyModesHelper.getDefaultEnergyMode();
+        final EnergyMode currentMode = energyModesHelper.updateEnergyMode();
+
+        Bundle bundle = new Bundle();
+        bundle.putString(KEY_DEFAULT_MODE, getModeIdentifier(defaultMode));
+        bundle.putString(KEY_SELECTED_MODE, getModeIdentifier(currentMode));
+        bundle.putParcelableList(KEY_ENERGY_MODES, getModes(energyModesHelper));
+        return bundle;
+    }
+
+    private Bundle setEnergyModeFromBinder(String identifier) {
+        getContext().enforceCallingOrSelfPermission(MANAGE_LOW_POWER_STANDBY, null);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            EnergyModesHelper energyModesHelper = new EnergyModesHelper(getContext());
+            EnergyMode energyMode = energyModesHelper.getEnergyMode(/* identifier= */ identifier);
+            if (energyMode == null) {
+                throw new IllegalArgumentException("Unknown energy mode: " + identifier);
+            }
+
+            energyModesHelper.setEnergyMode(energyMode);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+
+        return null;
+    }
+
+    @Nullable
+    private String getModeIdentifier(@Nullable EnergyMode mode) {
+        if (mode == null) {
+            return null;
+        }
+
+        return getContext().getString(mode.identifierRes);
+    }
+
+    @NonNull
+    private List<Bundle> getModes(@NonNull EnergyModesHelper helper) {
+        final List<Bundle> result = new ArrayList<>();
+        final List<EnergyMode> energyModes = helper.getEnergyModes();
+
+        for (EnergyMode mode : energyModes) {
+            result.add(convertEnergyModeToBundle(helper, mode));
+        }
+
+        return result;
+    }
+
+    @NonNull
+    private Bundle convertEnergyModeToBundle(
+            @NonNull EnergyModesHelper helper, @NonNull EnergyMode mode) {
+        Context context = getContext();
+        Bundle bundle = new Bundle();
+
+        bundle.putString(KEY_IDENTIFIER, getModeIdentifier(mode));
+        bundle.putParcelable(KEY_ICON, Icon.createWithResource(context, mode.iconRes));
+        bundle.putInt(KEY_COLOR, context.getColor(mode.colorRes));
+        bundle.putString(KEY_TITLE, context.getString(mode.titleRes));
+        bundle.putString(KEY_SUBTITLE, context.getString(mode.subtitleRes));
+        bundle.putString(KEY_DESCRIPTION, context.getString(mode.infoTextRes));
+        bundle.putString(KEY_SHORT_DESCRIPTION, context.getString(mode.infoTextRes));
+        bundle.putStringArray(KEY_FEATURES_LIST,
+                context.getResources().getStringArray(mode.featuresRes));
+        bundle.putStringArray(KEY_FEATURES,
+                helper.getAllowedFeatures(mode).toArray(new String[0]));
+
+        return bundle;
+    }
+}
diff --git a/Settings/src/com/android/tv/settings/device/eco/EnergyModesHelper.java b/Settings/src/com/android/tv/settings/device/eco/EnergyModesHelper.java
index ffa32c7..e8ce5ef 100644
--- a/Settings/src/com/android/tv/settings/device/eco/EnergyModesHelper.java
+++ b/Settings/src/com/android/tv/settings/device/eco/EnergyModesHelper.java
@@ -330,7 +330,7 @@
         return string.split(",");
     }
 
-    private LowPowerStandbyPolicy getPolicy(EnergyMode mode) {
+    LowPowerStandbyPolicy getPolicy(EnergyMode mode) {
         if (!mode.enableLowPowerStandby) {
             return new LowPowerStandbyPolicy(
                     mContext.getString(mode.identifierRes),
@@ -410,7 +410,7 @@
         if (!areEnergyModesAvailable()) {
             return null;
         }
-        return getEnergyModes().get(0);
+        return getEnergyMode(mContext.getString(R.string.default_energy_mode));
     }
 
     /**
@@ -466,6 +466,10 @@
                 targetEnergyMode = MODE_HIGH_ENERGY;
             } else {
                 targetEnergyMode = getDefaultEnergyMode();
+                if (targetEnergyMode == null) {
+                    // Fall back to lowest energy mode if default is not set or invalid
+                    targetEnergyMode = getEnergyModes().get(0);
+                }
             }
         }
 
diff --git a/Settings/src/com/android/tv/settings/device/eco/EnergyModesService.java b/Settings/src/com/android/tv/settings/device/eco/EnergyModesService.java
deleted file mode 100644
index 7f089a5..0000000
--- a/Settings/src/com/android/tv/settings/device/eco/EnergyModesService.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2023 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.
- */
-
-package com.android.tv.settings.device.eco;
-
-import static android.Manifest.permission.MANAGE_LOW_POWER_STANDBY;
-
-import android.annotation.EnforcePermission;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.Icon;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.IBinder;
-
-import com.android.tv.settings.device.eco.EnergyModesHelper.EnergyMode;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Service that provides methods to query and set Energy Modes.
- */
-public class EnergyModesService extends Service {
-
-    private BinderService mService;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mService = new BinderService(getApplication());
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mService;
-    }
-
-    private class BinderService extends IEnergyModesService.Stub {
-        private final Context mContext;
-        private final EnergyModesHelper mHelper;
-
-        BinderService(Context context) {
-            mContext = context;
-            mHelper = new EnergyModesHelper(context);
-        }
-
-        @Override
-        public List<Bundle> getModes() {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                final List<Bundle> result = new ArrayList<>();
-                final List<EnergyMode> energyModes = mHelper.getEnergyModes();
-                final EnergyMode currentMode = mHelper.updateEnergyMode();
-
-                for (EnergyMode mode : energyModes) {
-                    result.add(convertToBundle(mode, mode == currentMode));
-                }
-
-                return result;
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-
-        private Bundle convertToBundle(EnergyMode mode, boolean selected) {
-            Bundle bundle = new Bundle();
-
-            bundle.putBoolean(KEY_SELECTED, selected);
-            bundle.putString(KEY_IDENTIFIER, getString(mode.identifierRes));
-            bundle.putParcelable(KEY_ICON, Icon.createWithResource(mContext, mode.iconRes));
-            bundle.putInt(KEY_COLOR, getColor(mode.colorRes));
-            bundle.putString(KEY_TITLE, getString(mode.titleRes));
-            bundle.putString(KEY_SUBTITLE, getString(mode.subtitleRes));
-            bundle.putString(KEY_DESCRIPTION, getString(mode.infoTextRes));
-            bundle.putString(KEY_SHORT_DESCRIPTION, getString(mode.infoTextRes));
-            bundle.putStringArray(KEY_FEATURES_LIST,
-                    getResources().getStringArray(mode.featuresRes));
-            bundle.putStringArray(KEY_FEATURES,
-                    mHelper.getAllowedFeatures(mode).toArray(new String[0]));
-
-            return bundle;
-        }
-
-        @EnforcePermission(MANAGE_LOW_POWER_STANDBY)
-        @Override
-        public void setMode(String identifier) {
-            super.setMode_enforcePermission();
-
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                final EnergyMode mode = mHelper.getEnergyMode(identifier);
-                if (mode == null) {
-                    throw new IllegalArgumentException("Unknown energy mode: " + identifier);
-                }
-                mHelper.setEnergyMode(mode);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-
-        @Override
-        public String getDefaultMode() {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                final EnergyMode mode = mHelper.getDefaultEnergyMode();
-                if (mode == null) {
-                    return null;
-                }
-                return getString(mode.identifierRes);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-}
diff --git a/Settings/src/com/android/tv/settings/device/eco/EnergyModesStatsLogJobService.java b/Settings/src/com/android/tv/settings/device/eco/EnergyModesStatsLogJobService.java
new file mode 100644
index 0000000..f3c4163
--- /dev/null
+++ b/Settings/src/com/android/tv/settings/device/eco/EnergyModesStatsLogJobService.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+package com.android.tv.settings.device.eco;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.PowerManager.LowPowerStandbyPolicy;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.settingslib.utils.ThreadUtils;
+import com.android.tv.settings.R;
+import com.android.tv.settings.device.eco.EnergyModesHelper.EnergyMode;
+import com.android.tv.twopanelsettings.slices.TvSettingsStatsLog;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * JobService to log available energy mode policies.
+ */
+public class EnergyModesStatsLogJobService extends JobService {
+    private static final String TAG = "EnergyModesStatsLogJobService";
+    private static final long WRITE_STATS_FREQUENCY_MS = TimeUnit.DAYS.toMillis(6);
+
+    /** Schedule a periodic job to log available energy mode policies. */
+    public static void scheduleEnergyModesStatsLog(Context context) {
+        final EnergyModesHelper energyModesHelper = new EnergyModesHelper(context);
+        if (!energyModesHelper.areEnergyModesAvailable()) {
+            return;
+        }
+
+        final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+        final ComponentName component =
+                new ComponentName(context, EnergyModesStatsLogJobService.class);
+        final JobInfo job =
+                new JobInfo.Builder(R.integer.job_energy_modes_stats_log, component)
+                        .setPeriodic(WRITE_STATS_FREQUENCY_MS)
+                        .setRequiresDeviceIdle(true)
+                        .setPersisted(true)
+                        .build();
+        final JobInfo pending = jobScheduler.getPendingJob(R.integer.job_energy_modes_stats_log);
+
+        // Don't schedule it if it already exists, to make sure it runs periodically even after
+        // reboot
+        if (pending == null && jobScheduler.schedule(job) != JobScheduler.RESULT_SUCCESS) {
+            Log.i(TAG, "Energy Modes stats log job service schedule failed.");
+        }
+    }
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        ThreadUtils.postOnBackgroundThread(() -> {
+            writePoliciesStatsLog(getApplicationContext());
+            jobFinished(params, false /* wantsReschedule */);
+        });
+
+        return true;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters jobParameters) {
+        return false;
+    }
+
+    /** Writes available energy mode policies to stats log */
+    public static void writePoliciesStatsLog(Context context) {
+        final EnergyModesHelper energyModesHelper = new EnergyModesHelper(context);
+        final EnergyMode current = energyModesHelper.updateEnergyMode();
+        final List<EnergyMode> energyModes = energyModesHelper.getEnergyModes();
+
+        for (EnergyMode energyMode : energyModes) {
+            final LowPowerStandbyPolicy policy = energyModesHelper.getPolicy(energyMode);
+            TvSettingsStatsLog.write(
+                    TvSettingsStatsLog.TV_LOW_POWER_STANDBY_POLICY,
+                    policy.getIdentifier(),
+                    getExemptPackageUids(context, policy),
+                    policy.getAllowedReasons(),
+                    policy.getAllowedFeatures().toArray(new String[0]),
+                    energyMode == current
+            );
+        }
+    }
+
+    private static int[] getExemptPackageUids(Context context, LowPowerStandbyPolicy policy) {
+        final PackageManager packageManager = context.getPackageManager();
+        final ArraySet<Integer> exemptUids = new ArraySet<>();
+        for (String exemptPackage : policy.getExemptPackages()) {
+            try {
+                int uid = packageManager.getPackageUid(exemptPackage, PackageManager.MATCH_ALL);
+                exemptUids.add(uid);
+            } catch (PackageManager.NameNotFoundException e) {
+            }
+        }
+
+        final int[] exemptUidsArray = new int[exemptUids.size()];
+        int i = 0;
+        for (Integer uid : exemptUids) {
+            exemptUidsArray[i++] = uid;
+        }
+
+        Arrays.sort(exemptUidsArray);
+        return exemptUidsArray;
+    }
+}
diff --git a/Settings/src/com/android/tv/settings/device/eco/IEnergyModesService.aidl b/Settings/src/com/android/tv/settings/device/eco/IEnergyModesService.aidl
deleted file mode 100644
index 6f5c926..0000000
--- a/Settings/src/com/android/tv/settings/device/eco/IEnergyModesService.aidl
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2023 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.
- */
-
-package com.android.tv.settings.device.eco;
-
-import android.os.Bundle;
-
-/** {@hide} */
-interface IEnergyModesService {
-    /** Key for a boolean representing whether the energy mode is currently selected. */
-    const String KEY_SELECTED = "selected";
-
-    /** Key for a String representing the identifier for an energy mode. */
-    const String KEY_IDENTIFIER = "identifier";
-
-    /** Key for an Icon representing the icon of an energy mode. */
-    const String KEY_ICON = "icon";
-
-    /** Key for an int representing the color of an energy mode (in ARGB). */
-    const String KEY_COLOR = "color";
-
-    /** Key for a String representing the title of an energy mode. */
-    const String KEY_TITLE = "title";
-
-    /** Key for a String representing the subtitle of an energy mode. */
-    const String KEY_SUBTITLE = "subtitle";
-
-    /** Key for a String representing the description of an energy mode. */
-    const String KEY_DESCRIPTION = "description";
-
-    /** Key for a String representing a short description of an energy mode. */
-    const String KEY_SHORT_DESCRIPTION = "short_description";
-
-    /** Key for a String array representing the (human-friendly) features of an energy mode. */
-    const String KEY_FEATURES_LIST = "features_list";
-
-    /** Key for a String array representing the features of an energy mode. */
-    const String KEY_FEATURES = "features";
-
-    /** Returns the list of available energy modes */
-    List<Bundle> getModes();
-
-    /** Sets the selected energy mode by the given identifier */
-    @EnforcePermission("MANAGE_LOW_POWER_STANDBY")
-    void setMode(String identifier);
-
-    /** Returns the identifier of the default energy mode */
-    String getDefaultMode();
-}
diff --git a/Settings/src/com/android/tv/settings/util/ResolutionSelectionUtils.java b/Settings/src/com/android/tv/settings/util/ResolutionSelectionUtils.java
index 3c2069a..7215040 100644
--- a/Settings/src/com/android/tv/settings/util/ResolutionSelectionUtils.java
+++ b/Settings/src/com/android/tv/settings/util/ResolutionSelectionUtils.java
@@ -16,25 +16,35 @@
 
 package com.android.tv.settings.util;
 
+import static java.math.RoundingMode.HALF_UP;
+
 import android.content.Context;
+import android.icu.number.LocalizedNumberFormatter;
+import android.icu.number.NumberFormatter;
 import android.view.Display;
 
 import com.android.tv.settings.R;
 
+import java.util.Locale;
+
 
 /** This utility class for Resolution Setting **/
 public class ResolutionSelectionUtils {
 
     /**
-     * Returns the refresh rate converted to a string. If the refresh rate has only 0s after the
-     * floating point, they are removed. The unit "Hz" is added to end of refresh rate.
+     * Returns the refresh rate converted to a string in the local language. If the refresh rate has
+     * only 0s after the floating point, they are removed.
+     * The unit "Hz" is added to end of refresh rate.
      */
     public static String getRefreshRateString(float refreshRate) {
-        float roundedRefreshRate = Math.round(refreshRate * 100.0f) / 100.0f;
+        LocalizedNumberFormatter localizedNumberFormatter = NumberFormatter.with().roundingMode(
+                HALF_UP).locale(Locale.getDefault());
+        double roundedRefreshRate = Math.round(refreshRate * 100.0f) / 100.0f;
         if (roundedRefreshRate % 1 == 0) {
-            return Integer.toString((int) roundedRefreshRate);
+            return localizedNumberFormatter.format(roundedRefreshRate).toString();
         } else {
-            return Float.toString(roundedRefreshRate);
+            return String.format(Locale.getDefault(), "%.2f",
+                    localizedNumberFormatter.format(roundedRefreshRate).toBigDecimal());
         }
     }
 
@@ -64,4 +74,15 @@
                 ResolutionSelectionUtils.getRefreshRateString(mode.getRefreshRate()));
         return modeString;
     }
+
+    /**
+     * Returns the resolution mode converted to a string in the local language.
+     * Format: width + " x " + height
+     */
+    public static String getResolutionSummary(int physicalWidth, int physicalHeight) {
+        LocalizedNumberFormatter localizedNumberFormatter = NumberFormatter.with().locale(
+                Locale.getDefault());
+        return localizedNumberFormatter.format(physicalWidth).toString() + " x "
+                + localizedNumberFormatter.format(physicalHeight).toString();
+    }
 }
diff --git a/TwoPanelSettingsLib/Android.bp b/TwoPanelSettingsLib/Android.bp
index fa0056b..a944c5d 100644
--- a/TwoPanelSettingsLib/Android.bp
+++ b/TwoPanelSettingsLib/Android.bp
@@ -47,8 +47,6 @@
         "androidx.cardview_cardview",
         "statslog-tvsettings",
     ],
-
-    min_sdk_version: "30",
 }
 
 // For the test package.