Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev
diff --git a/res/values-ca/arrays.xml b/res/values-ca/arrays.xml
index 15671a2..2495ad6 100644
--- a/res/values-ca/arrays.xml
+++ b/res/values-ca/arrays.xml
@@ -368,7 +368,7 @@
     <item msgid="2262719249581510939">"Desconnectada"</item>
     <item msgid="9141074028293812365">"S\'està inicialitzant..."</item>
     <item msgid="2234425878608626285">"S\'està connectant..."</item>
-    <item msgid="27547778933579155">"Connectat"</item>
+    <item msgid="27547778933579155">"Connectada"</item>
     <item msgid="893506841727300393">"Temps d\'espera esgotat"</item>
     <item msgid="2974952010554140659">"Incorrecte"</item>
   </string-array>
diff --git a/res/values-ne/arrays.xml b/res/values-ne/arrays.xml
index f128b3b..99afd0e 100644
--- a/res/values-ne/arrays.xml
+++ b/res/values-ne/arrays.xml
@@ -404,7 +404,7 @@
     <item msgid="9080166583718385565">"0"</item>
   </string-array>
   <string-array name="wifi_metered_entries">
-    <item msgid="3237321077949659241">"स्वतः पत्ता लगाइयोस्"</item>
+    <item msgid="3237321077949659241">"स्वतः पत्ता लगाउनुहोस्"</item>
     <item msgid="3779092145391320375">"सशुल्क Wi-Fi का रूपमा लिइयोस्"</item>
     <item msgid="2047166446768045816">"नि:शुल्क Wi-Fi का रूपमा लिइयोस्"</item>
   </string-array>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9e1ab8a..1e76855 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4478,8 +4478,14 @@
     <!-- Summary for the modifier key picker dialog page [CHAR LIMIT=35] -->
     <string name="modifier_keys_picker_summary">Choose a new key for <xliff:g id="modifier_key_default_name">%1$s</xliff:g>:</string>
 
+    <!-- Title text for per IME subtype keyboard layout. [CHAR LIMIT=35] -->
+    <string name="ime_label_title"><xliff:g id="ime_label" example="Gboard">%s</xliff:g> layout</string>
     <!-- Summary text for keyboards when no layout has been selected. [CHAR LIMIT=35] -->
     <string name="default_keyboard_layout">Default</string>
+    <!-- Summary text for keyboards when a layout is automatically selected. [CHAR LIMIT=35] -->
+    <string name="automatic_keyboard_layout_label">Automatic: <xliff:g id="layout_label" example="English(US)">%s</xliff:g></string>
+    <!-- Summary text for keyboards when a layout is user selected. [CHAR LIMIT=35] -->
+    <string name="user_selected_keyboard_layout_label">User selected: <xliff:g id="layout_label" example="English(US)">%s</xliff:g></string>
 
     <!-- Title for the 'Speech' preference category. [CHAR LIMIT=45] -->
     <string name="speech_category_title">Speech</string>
diff --git a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
index 6a65dc0..ec63c9a 100644
--- a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java
@@ -88,6 +88,14 @@
             return info.statusLabel;
         } else if (info.statusLabel != null && !info.discharging) {
             // Charging state
+            if (com.android.settingslib.fuelgauge.BatteryUtils.isChargingStringV2Enabled()) {
+                return info.isFastCharging
+                        ? mContext.getString(
+                                R.string.battery_state_and_duration,
+                                info.statusLabel,
+                                info.remainingLabel)
+                        : info.remainingLabel;
+            }
             return mContext.getString(
                     R.string.battery_state_and_duration, info.statusLabel, info.remainingLabel);
         } else if (mPowerManager.isPowerSaveMode()) {
diff --git a/src/com/android/settings/fuelgauge/BatteryInfo.java b/src/com/android/settings/fuelgauge/BatteryInfo.java
index ea8ef8d..904923a 100644
--- a/src/com/android/settings/fuelgauge/BatteryInfo.java
+++ b/src/com/android/settings/fuelgauge/BatteryInfo.java
@@ -37,6 +37,8 @@
 import com.android.settings.Utils;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.widget.UsageView;
+import com.android.settingslib.R;
+import com.android.settingslib.fuelgauge.BatteryStatus;
 import com.android.settingslib.fuelgauge.Estimate;
 import com.android.settingslib.fuelgauge.EstimateKt;
 import com.android.settingslib.utils.PowerUtil;
@@ -52,6 +54,7 @@
     public int pluggedStatus;
     public boolean discharging = true;
     public boolean isBatteryDefender;
+    public boolean isFastCharging;
     public long remainingTimeUs = 0;
     public long averageTimeToDischarge = EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN;
     public String batteryPercentString;
@@ -143,13 +146,13 @@
         parseBatteryHistory(parserList);
         String timeString =
                 context.getString(
-                        com.android.settingslib.R.string.charge_length_format,
+                        R.string.charge_length_format,
                         Formatter.formatShortElapsedTime(context, timePeriod));
         String remaining = "";
         if (remainingTimeUs != 0) {
             remaining =
                     context.getString(
-                            com.android.settingslib.R.string.remaining_length_format,
+                            R.string.remaining_length_format,
                             Formatter.formatShortElapsedTime(context, remainingTimeUs / 1000));
         }
         view.setBottomLabels(new CharSequence[] {timeString, remaining});
@@ -291,8 +294,8 @@
             @NonNull BatteryUsageStats batteryUsageStats,
             Estimate estimate,
             long elapsedRealtimeUs,
-            boolean shortString) {
-        final long startTime = System.currentTimeMillis();
+            boolean shortString,
+            long currentTimeMs) {
         final boolean isCompactStatus =
                 context.getResources()
                         .getBoolean(com.android.settings.R.bool.config_use_compact_battery_status);
@@ -313,22 +316,51 @@
         info.batteryStatus =
                 batteryBroadcast.getIntExtra(
                         BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
+        info.isFastCharging =
+                BatteryStatus.getChargingSpeed(context, batteryBroadcast)
+                        == BatteryStatus.CHARGING_FAST;
         if (!info.mCharging) {
             updateBatteryInfoDischarging(context, shortString, estimate, info);
         } else {
             updateBatteryInfoCharging(
-                    context, batteryBroadcast, batteryUsageStats, info, isCompactStatus);
+                    context,
+                    batteryBroadcast,
+                    batteryUsageStats,
+                    info,
+                    isCompactStatus,
+                    currentTimeMs);
         }
-        BatteryUtils.logRuntime(LOG_TAG, "time for getBatteryInfo", startTime);
+        BatteryUtils.logRuntime(LOG_TAG, "time for getBatteryInfo", currentTimeMs);
         return info;
     }
 
+    /** Returns a {@code BatteryInfo} with battery and charging relative information. */
+    @WorkerThread
+    public static BatteryInfo getBatteryInfo(
+            Context context,
+            Intent batteryBroadcast,
+            BatteryUsageStats batteryUsageStats,
+            Estimate estimate,
+            long elapsedRealtimeUs,
+            boolean shortString) {
+        long currentTimeMs = System.currentTimeMillis();
+        return getBatteryInfo(
+                context,
+                batteryBroadcast,
+                batteryUsageStats,
+                estimate,
+                elapsedRealtimeUs,
+                shortString,
+                currentTimeMs);
+    }
+
     private static void updateBatteryInfoCharging(
             Context context,
             Intent batteryBroadcast,
             BatteryUsageStats stats,
             BatteryInfo info,
-            boolean compactStatus) {
+            boolean compactStatus,
+            long currentTimeMs) {
         final Resources resources = context.getResources();
         final long chargeTimeMs = stats.getChargeTimeRemainingMs();
         if (getSettingsChargeTimeRemaining(context) != chargeTimeMs) {
@@ -350,7 +382,7 @@
                 || dockDefenderMode == BatteryUtils.DockDefenderMode.ACTIVE) {
             // Battery defender active, battery charging paused
             info.remainingLabel = null;
-            int chargingLimitedResId = com.android.settingslib.R.string.power_charging_limited;
+            int chargingLimitedResId = R.string.power_charging_limited;
             info.chargeLabel = context.getString(chargingLimitedResId, info.batteryPercentString);
         } else if ((chargeTimeMs > 0
                         && status != BatteryManager.BATTERY_STATUS_FULL
@@ -358,30 +390,29 @@
                 || dockDefenderMode == BatteryUtils.DockDefenderMode.TEMPORARILY_BYPASSED) {
             // Battery is charging to full
             info.remainingTimeUs = PowerUtil.convertMsToUs(chargeTimeMs);
-            final CharSequence timeString =
-                    StringUtil.formatElapsedTime(
-                            context,
-                            (double) PowerUtil.convertUsToMs(info.remainingTimeUs),
-                            false /* withSeconds */,
-                            true /* collapseTimeUnit */);
-            int resId = com.android.settingslib.R.string.power_charging_duration;
+
+            int resId = getChargingDurationResId(info.isFastCharging);
             info.remainingLabel =
                     chargeTimeMs <= 0
                             ? null
-                            : context.getString(
-                                    com.android.settingslib.R.string
-                                            .power_remaining_charging_duration_only,
-                                    timeString);
+                            : getPowerRemainingChargingLabel(
+                                    context, chargeTimeMs, info.isFastCharging, currentTimeMs);
+
             info.chargeLabel =
                     chargeTimeMs <= 0
                             ? info.batteryPercentString
-                            : context.getString(resId, info.batteryPercentString, timeString);
+                            : getChargeLabelWithTimeToFull(
+                                    context,
+                                    resId,
+                                    info.batteryPercentString,
+                                    chargeTimeMs,
+                                    info.isFastCharging,
+                                    currentTimeMs);
         } else if (dockDefenderMode == BatteryUtils.DockDefenderMode.FUTURE_BYPASS) {
             // Dock defender will be triggered in the future, charging will be optimized.
             info.chargeLabel =
                     context.getString(
-                            com.android.settingslib.R.string.power_charging_future_paused,
-                            info.batteryPercentString);
+                            R.string.power_charging_future_paused, info.batteryPercentString);
         } else {
             final String chargeStatusLabel =
                     Utils.getBatteryStatus(context, batteryBroadcast, compactStatus);
@@ -390,12 +421,70 @@
                     info.batteryLevel == 100
                             ? info.batteryPercentString
                             : resources.getString(
-                                    com.android.settingslib.R.string.power_charging,
+                                    R.string.power_charging,
                                     info.batteryPercentString,
                                     chargeStatusLabel);
         }
     }
 
+    private static CharSequence getPowerRemainingChargingLabel(
+            Context context, long remainingTimeMs, boolean isFastCharging, long currentTimeMs) {
+        if (com.android.settingslib.fuelgauge.BatteryUtils.isChargingStringV2Enabled()) {
+            int chargeLabelResId =
+                    isFastCharging
+                            ? R.string.power_remaining_fast_charging_duration_only_v2
+                            : R.string.power_remaining_charging_duration_only_v2;
+            String timeString =
+                    PowerUtil.getTargetTimeShortString(context, remainingTimeMs, currentTimeMs);
+            return context.getString(chargeLabelResId, timeString);
+        }
+        final CharSequence timeString =
+                StringUtil.formatElapsedTime(
+                        context,
+                        remainingTimeMs,
+                        /* withSeconds= */ false,
+                        /* collapseTimeUnit= */ true);
+        return context.getString(R.string.power_remaining_charging_duration_only, timeString);
+    }
+
+    private static CharSequence getChargeLabelWithTimeToFull(
+            Context context,
+            int chargeLabelResId,
+            String batteryPercentString,
+            long chargeTimeMs,
+            boolean isFastCharging,
+            long currentTimeMs) {
+        if (com.android.settingslib.fuelgauge.BatteryUtils.isChargingStringV2Enabled()) {
+            var timeString =
+                    PowerUtil.getTargetTimeShortString(context, chargeTimeMs, currentTimeMs);
+
+            return isFastCharging
+                    ? context.getString(
+                            chargeLabelResId,
+                            batteryPercentString,
+                            context.getString(R.string.battery_info_status_charging_fast_v2),
+                            timeString)
+                    : context.getString(chargeLabelResId, batteryPercentString, timeString);
+        } else {
+            var timeString =
+                    StringUtil.formatElapsedTime(
+                            context,
+                            (double) chargeTimeMs,
+                            /* withSeconds= */ false,
+                            /* collapseTimeUnit= */ true);
+            return context.getString(chargeLabelResId, batteryPercentString, timeString);
+        }
+    }
+
+    private static int getChargingDurationResId(boolean isFastCharging) {
+        if (com.android.settingslib.fuelgauge.BatteryUtils.isChargingStringV2Enabled()) {
+            return isFastCharging
+                    ? R.string.power_fast_charging_duration_v2
+                    : R.string.power_charging_duration_v2;
+        }
+        return R.string.power_charging_duration;
+    }
+
     private static void updateBatteryInfoDischarging(
             Context context, boolean shortString, Estimate estimate, BatteryInfo info) {
         final long drainTimeUs = PowerUtil.convertMsToUs(estimate.getEstimateMillis());
diff --git a/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java b/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java
index 05dc5be..2bbb567 100644
--- a/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java
+++ b/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java
@@ -21,6 +21,7 @@
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
 import android.hardware.input.KeyboardLayout;
+import android.hardware.input.KeyboardLayoutSelectionResult;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -180,7 +181,7 @@
                     mapLanguageWithLayout(info, subtype);
                 }
             }
-            updatePreferenceLayout(preferenceScreen, info);
+            updatePreferenceLayout(preferenceScreen, info, infoList.size() > 1);
         }
     }
 
@@ -189,14 +190,15 @@
         KeyboardLayout[] keyboardLayouts =
                 NewKeyboardSettingsUtils.getKeyboardLayouts(
                         mIm, mUserId, mInputDeviceIdentifier, info, subtype);
-        String layout = NewKeyboardSettingsUtils.getKeyboardLayout(
+        KeyboardLayoutSelectionResult result = NewKeyboardSettingsUtils.getKeyboardLayout(
                 mIm, mUserId, mInputDeviceIdentifier, info, subtype);
-        if (layout != null) {
+        if (result.getLayoutDescriptor() != null) {
             for (int i = 0; i < keyboardLayouts.length; i++) {
-                if (keyboardLayouts[i].getDescriptor().equals(layout)) {
+                if (keyboardLayouts[i].getDescriptor().equals(result.getLayoutDescriptor())) {
                     KeyboardInfo keyboardInfo = new KeyboardInfo(
                             subtypeLabel,
                             keyboardLayouts[i].getLabel(),
+                            result.getSelectionCriteria(),
                             info,
                             subtype);
                     mKeyboardInfoList.add(keyboardInfo);
@@ -208,18 +210,22 @@
             KeyboardInfo keyboardInfo = new KeyboardInfo(
                     subtypeLabel,
                     mContext.getString(R.string.keyboard_default_layout),
+                    KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_UNSPECIFIED,
                     info,
                     subtype);
             mKeyboardInfoList.add(keyboardInfo);
         }
     }
 
-    private void updatePreferenceLayout(PreferenceScreen preferenceScreen, InputMethodInfo info) {
+    private void updatePreferenceLayout(PreferenceScreen preferenceScreen, InputMethodInfo info,
+            boolean hasMultipleImes) {
         if (mKeyboardInfoList.isEmpty()) {
             return;
         }
         PreferenceCategory preferenceCategory = new PreferenceCategory(mContext);
-        preferenceCategory.setTitle(info.loadLabel(mContext.getPackageManager()));
+        preferenceCategory.setTitle(hasMultipleImes ? mContext.getString(R.string.ime_label_title,
+                info.loadLabel(mContext.getPackageManager()))
+                : mContext.getString(R.string.enabled_locales_keyboard_layout));
         preferenceCategory.setKey(info.getPackageName());
         preferenceScreen.addPreference(preferenceCategory);
         Collections.sort(mKeyboardInfoList, new Comparator<KeyboardInfo>() {
@@ -234,7 +240,7 @@
             final Preference pref = new Preference(mContext);
             pref.setKey(keyboardInfo.getPrefId());
             pref.setTitle(keyboardInfo.getSubtypeLabel());
-            pref.setSummary(keyboardInfo.getLayout());
+            pref.setSummary(keyboardInfo.getLayoutSummaryText(mContext));
             pref.setOnPreferenceClickListener(
                     preference -> {
                         showKeyboardLayoutPicker(
diff --git a/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerController.java b/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerController.java
index ac8037f..ec727e8 100644
--- a/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerController.java
+++ b/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerController.java
@@ -21,6 +21,7 @@
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
 import android.hardware.input.KeyboardLayout;
+import android.hardware.input.KeyboardLayoutSelectionResult;
 import android.os.Bundle;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
@@ -201,13 +202,13 @@
 
     private String getSelectedLayoutLabel() {
         String label = mContext.getString(R.string.keyboard_default_layout);
-        String layout = NewKeyboardSettingsUtils.getKeyboardLayout(
+        KeyboardLayoutSelectionResult result = NewKeyboardSettingsUtils.getKeyboardLayout(
                 mIm, mUserId, mInputDeviceIdentifier, mInputMethodInfo, mInputMethodSubtype);
         KeyboardLayout[] keyboardLayouts = NewKeyboardSettingsUtils.getKeyboardLayouts(
                 mIm, mUserId, mInputDeviceIdentifier, mInputMethodInfo, mInputMethodSubtype);
-        if (layout != null) {
+        if (result.getLayoutDescriptor() != null) {
             for (KeyboardLayout keyboardLayout : keyboardLayouts) {
-                if (keyboardLayout.getDescriptor().equals(layout)) {
+                if (keyboardLayout.getDescriptor().equals(result.getLayoutDescriptor())) {
                     label = keyboardLayout.getLabel();
                     break;
                 }
diff --git a/src/com/android/settings/inputmethod/NewKeyboardSettingsUtils.java b/src/com/android/settings/inputmethod/NewKeyboardSettingsUtils.java
index a927165..8f1e5c8 100644
--- a/src/com/android/settings/inputmethod/NewKeyboardSettingsUtils.java
+++ b/src/com/android/settings/inputmethod/NewKeyboardSettingsUtils.java
@@ -16,20 +16,30 @@
 
 package com.android.settings.inputmethod;
 
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE;
+import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.UserIdInt;
 import android.content.Context;
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
 import android.hardware.input.KeyboardLayout;
+import android.hardware.input.KeyboardLayoutSelectionResult;
+import android.hardware.input.KeyboardLayoutSelectionResult.LayoutSelectionCriteria;
 import android.os.UserHandle;
 import android.view.InputDevice;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
 
-import java.util.ArrayList;
+import com.android.settings.R;
+
 import java.util.Arrays;
 import java.util.Comparator;
-import java.util.List;
 
 /**
  * Utilities of keyboard settings
@@ -56,36 +66,47 @@
         return false;
     }
 
-    static List<String> getSuitableImeLabels(Context context, InputMethodManager imm, int userId) {
-        List<String> suitableInputMethodInfoLabels = new ArrayList<>();
-        List<InputMethodInfo> infoList = imm.getEnabledInputMethodListAsUser(UserHandle.of(userId));
-        for (InputMethodInfo info : infoList) {
-            List<InputMethodSubtype> subtypes =
-                    imm.getEnabledInputMethodSubtypeList(info, true);
-            for (InputMethodSubtype subtype : subtypes) {
-                if (subtype.isSuitableForPhysicalKeyboardLayoutMapping()) {
-                    suitableInputMethodInfoLabels.add(
-                            info.loadLabel(context.getPackageManager()).toString());
-                    break;
+    @SuppressLint("MissingPermission")
+    @Nullable
+    static String getSelectedKeyboardLayoutLabelForUser(Context context, @UserIdInt int userId,
+            InputDeviceIdentifier inputDeviceIdentifier) {
+        InputMethodManager imm = context.getSystemService(InputMethodManager.class);
+        InputManager im = context.getSystemService(InputManager.class);
+        if (imm == null || im == null) {
+            return null;
+        }
+        InputMethodInfo imeInfo = imm.getCurrentInputMethodInfoAsUser(UserHandle.of(userId));
+        InputMethodSubtype subtype = imm.getCurrentInputMethodSubtype();
+        KeyboardLayout[] keyboardLayouts = getKeyboardLayouts(im, userId, inputDeviceIdentifier,
+                imeInfo, subtype);
+        KeyboardLayoutSelectionResult result = getKeyboardLayout(im, userId, inputDeviceIdentifier,
+                imeInfo, subtype);
+        if (result != null) {
+            for (KeyboardLayout keyboardLayout : keyboardLayouts) {
+                if (keyboardLayout.getDescriptor().equals(result.getLayoutDescriptor())) {
+                    return keyboardLayout.getLabel();
                 }
             }
         }
-        return suitableInputMethodInfoLabels;
+        return null;
     }
 
     static class KeyboardInfo {
         CharSequence mSubtypeLabel;
         String mLayout;
+        @LayoutSelectionCriteria int mSelectionCriteria;
         InputMethodInfo mInputMethodInfo;
         InputMethodSubtype mInputMethodSubtype;
 
         KeyboardInfo(
                 CharSequence subtypeLabel,
                 String layout,
+                @LayoutSelectionCriteria int selectionCriteria,
                 InputMethodInfo inputMethodInfo,
                 InputMethodSubtype inputMethodSubtype) {
             mSubtypeLabel = subtypeLabel;
             mLayout = layout;
+            mSelectionCriteria = selectionCriteria;
             mInputMethodInfo = inputMethodInfo;
             mInputMethodSubtype = inputMethodSubtype;
         }
@@ -102,6 +123,17 @@
             return mLayout;
         }
 
+        String getLayoutSummaryText(Context context) {
+            if (isAutomaticSelection(mSelectionCriteria)) {
+                return context.getResources().getString(R.string.automatic_keyboard_layout_label,
+                        mLayout);
+            } else if (isUserSelection(mSelectionCriteria)) {
+                return context.getResources().getString(
+                        R.string.user_selected_keyboard_layout_label, mLayout);
+            }
+            return mLayout;
+        }
+
         InputMethodInfo getInputMethodInfo() {
             return mInputMethodInfo;
         }
@@ -121,11 +153,21 @@
         return inputManager.getKeyboardLayoutListForInputDevice(identifier, userId, info, subtype);
     }
 
-    static String getKeyboardLayout(InputManager inputManager, int userId,
+    @NonNull
+    static KeyboardLayoutSelectionResult getKeyboardLayout(InputManager inputManager, int userId,
             InputDeviceIdentifier identifier, InputMethodInfo info, InputMethodSubtype subtype) {
         return inputManager.getKeyboardLayoutForInputDevice(identifier, userId, info, subtype);
     }
 
+    static boolean isAutomaticSelection(@LayoutSelectionCriteria int criteria) {
+        return criteria == LAYOUT_SELECTION_CRITERIA_DEVICE
+                || criteria == LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
+    }
+
+    static boolean isUserSelection(@LayoutSelectionCriteria int criteria) {
+        return criteria == LAYOUT_SELECTION_CRITERIA_USER;
+    }
+
     static void sortKeyboardLayoutsByLabel(KeyboardLayout[] keyboardLayouts) {
         Arrays.sort(
                 keyboardLayouts,
diff --git a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
index f2ac550..e102241 100644
--- a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
@@ -288,19 +288,11 @@
             final Preference pref = new Preference(getPrefContext());
             pref.setTitle(hardKeyboardDeviceInfo.mDeviceName);
             if (mIsNewKeyboardSettings) {
-                List<String> suitableImes = new ArrayList<>();
-                suitableImes.addAll(
-                        NewKeyboardSettingsUtils.getSuitableImeLabels(
-                                getContext(), mImm, UserHandle.myUserId()));
-                if (!suitableImes.isEmpty()) {
-                    String summary = suitableImes.get(0);
-                    StringBuilder result = new StringBuilder(summary);
-                    for (int i = 1; i < suitableImes.size(); i++) {
-                        result.append(", ").append(suitableImes.get(i));
-                    }
-                    pref.setSummary(result.toString());
-                } else {
-                    pref.setSummary(hardKeyboardDeviceInfo.mLayoutLabel);
+                String currentLayout =
+                        NewKeyboardSettingsUtils.getSelectedKeyboardLayoutLabelForUser(getContext(),
+                                UserHandle.myUserId(), hardKeyboardDeviceInfo.mDeviceIdentifier);
+                if (currentLayout != null) {
+                    pref.setSummary(currentLayout);
                 }
                 pref.setOnPreferenceClickListener(
                         preference -> {
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
index 1df8a40..9fa9651 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java
@@ -44,6 +44,7 @@
 import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
 import com.android.settings.testutils.shadow.ShadowUtils;
 import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.fuelgauge.BatteryUtils;
 import com.android.settingslib.widget.UsageProgressBarPreference;
 
 import org.junit.After;
@@ -107,6 +108,8 @@
         mController = spy(new BatteryHeaderPreferenceController(mContext, PREF_KEY));
         mController.mBatteryUsageProgressBarPref = mBatteryUsageProgressBarPref;
         mController.mBatteryStatusFeatureProvider = mBatteryStatusFeatureProvider;
+
+        BatteryUtils.setChargingStringV2Enabled(null);
     }
 
     @After
@@ -226,6 +229,65 @@
     }
 
     @Test
+    public void updateBatteryStatus_chargingString_statusWithRemainingLabel() {
+        var batteryInfo =
+                arrangeUpdateBatteryStatusTestWithRemainingLabel(
+                        /* remainingLabel= */ "1 hr, 40 min left until full",
+                        /* statusLabel= */ "Charging rapidly",
+                        /* isFastCharging= */ true,
+                        /* isChargingStringV2= */ false);
+        var expectedChargingString = batteryInfo.statusLabel + " • " + batteryInfo.remainingLabel;
+
+        mController.updateBatteryStatus(/* label= */ null, batteryInfo);
+
+        verify(mBatteryUsageProgressBarPref).setBottomSummary(expectedChargingString);
+    }
+
+    @Test
+    public void updateBatteryStatus_chargingStringV2FastCharging_statusWithRemainingLabel() {
+        var batteryInfo =
+                arrangeUpdateBatteryStatusTestWithRemainingLabel(
+                        /* remainingLabel= */ "Full by 1:30 PM",
+                        /* statusLabel= */ "Fast Charging",
+                        /* isFastCharging= */ true,
+                        /* isChargingStringV2= */ true);
+        var expectedChargingString = batteryInfo.statusLabel + " • " + batteryInfo.remainingLabel;
+
+        mController.updateBatteryStatus(/* label= */ null, batteryInfo);
+
+        verify(mBatteryUsageProgressBarPref).setBottomSummary(expectedChargingString);
+    }
+
+    @Test
+    public void updateBatteryStatus_chargingStringV2NonFastCharging_remainingLabel() {
+        var batteryInfo =
+                arrangeUpdateBatteryStatusTestWithRemainingLabel(
+                        /* remainingLabel= */ "Fully charged by 11:10 PM",
+                        /* statusLabel= */ "Charging",
+                        /* isFastCharging= */ false,
+                        /* isChargingStringV2= */ true);
+        var expectedChargingString = batteryInfo.remainingLabel;
+
+        mController.updateBatteryStatus(/* label= */ null, batteryInfo);
+
+        verify(mBatteryUsageProgressBarPref).setBottomSummary(expectedChargingString);
+    }
+
+    private BatteryInfo arrangeUpdateBatteryStatusTestWithRemainingLabel(
+            String remainingLabel,
+            String statusLabel,
+            boolean isFastCharging,
+            boolean isChargingStringV2) {
+        BatteryUtils.setChargingStringV2Enabled(isChargingStringV2);
+        mBatteryInfo.isBatteryDefender = false;
+        mBatteryInfo.remainingLabel = remainingLabel;
+        mBatteryInfo.statusLabel = statusLabel;
+        mBatteryInfo.discharging = false;
+        mBatteryInfo.isFastCharging = isFastCharging;
+        return mBatteryInfo;
+    }
+
+    @Test
     public void updateHeaderByBatteryTips_lowBatteryTip_showLowBattery() {
         setChargingState(/* isDischarging */ true, /* updatedByStatusFeature */ false);
         BatteryTip lowBatteryTip =
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
index e99c4e0..e632596 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
@@ -17,6 +17,7 @@
 package com.android.settings.fuelgauge;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
@@ -30,12 +31,14 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.AlarmManager;
 import android.content.Context;
 import android.content.Intent;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.BatteryUsageStats;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.provider.Settings;
 import android.util.SparseIntArray;
 
@@ -56,6 +59,9 @@
 import org.robolectric.RuntimeEnvironment;
 
 import java.time.Duration;
+import java.time.Instant;
+import java.util.Locale;
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
 @RunWith(RobolectricTestRunner.class)
@@ -80,11 +86,23 @@
                     1000, /* estimateMillis */
                     false, /* isBasedOnUsage */
                     1000 /* averageDischargeTime */);
+    private static final Map<ChargingType, Integer> CHARGING_TYPE_MAP =
+            Map.of(
+                    ChargingType.WIRED, BatteryManager.BATTERY_PLUGGED_AC,
+                    ChargingType.WIRELESS, BatteryManager.BATTERY_PLUGGED_WIRELESS,
+                    ChargingType.DOCKED, BatteryManager.BATTERY_PLUGGED_DOCK);
+    private static final Map<ChargingSpeed, Integer> CHARGING_SPEED_MAP =
+            Map.of(
+                    ChargingSpeed.FAST, 1501000,
+                    ChargingSpeed.REGULAR, 1500000,
+                    ChargingSpeed.SLOW, 999999);
+    private static final long UNUSED_TIME_MS = -1L;
 
     private Intent mDisChargingBatteryBroadcast;
     private Intent mChargingBatteryBroadcast;
     private Context mContext;
     private FakeFeatureFactory mFeatureFactory;
+
     @Mock private BatteryUsageStats mBatteryUsageStats;
 
     @Before
@@ -102,10 +120,13 @@
                 mContext.getContentResolver(),
                 BatteryUtils.SETTINGS_GLOBAL_DOCK_DEFENDER_BYPASS,
                 0);
+
+        // Reset static cache for testing purpose.
+        com.android.settingslib.fuelgauge.BatteryUtils.setChargingStringV2Enabled(null);
     }
 
     @Test
-    public void testGetBatteryInfo_hasStatusLabel() {
+    public void getBatteryInfo_hasStatusLabel() {
         doReturn(REMAINING_TIME_NULL).when(mBatteryUsageStats).getBatteryTimeRemainingMs();
         BatteryInfo info =
                 BatteryInfo.getBatteryInfoOld(
@@ -119,7 +140,7 @@
     }
 
     @Test
-    public void testGetBatteryInfo_doNotShowChargingMethod_hasRemainingTime() {
+    public void getBatteryInfo_doNotShowChargingMethod_hasRemainingTime() {
         doReturn(REMAINING_TIME).when(mBatteryUsageStats).getChargeTimeRemainingMs();
         BatteryInfo info =
                 BatteryInfo.getBatteryInfoOld(
@@ -133,7 +154,7 @@
     }
 
     @Test
-    public void testGetBatteryInfo_doNotShowChargingMethod_noRemainingTime() {
+    public void getBatteryInfo_doNotShowChargingMethod_noRemainingTime() {
         doReturn(REMAINING_TIME_NULL).when(mBatteryUsageStats).getChargeTimeRemainingMs();
         BatteryInfo info =
                 BatteryInfo.getBatteryInfoOld(
@@ -147,7 +168,7 @@
     }
 
     @Test
-    public void testGetBatteryInfo_pluggedInUsingShortString_usesCorrectData() {
+    public void getBatteryInfo_pluggedInUsingShortString_usesCorrectData() {
         doReturn(TEST_CHARGE_TIME_REMAINING / 1000)
                 .when(mBatteryUsageStats)
                 .getChargeTimeRemainingMs();
@@ -164,7 +185,7 @@
     }
 
     @Test
-    public void testGetBatteryInfo_basedOnUsageTrueMoreThanFifteenMinutes_usesCorrectString() {
+    public void getBatteryInfo_basedOnUsageTrueMoreThanFifteenMinutes_usesCorrectString() {
         Estimate estimate =
                 new Estimate(
                         Duration.ofHours(4).toMillis(),
@@ -215,7 +236,7 @@
     }
 
     @Test
-    public void testGetBatteryInfo_basedOnUsageFalse_usesDefaultString() {
+    public void getBatteryInfo_basedOnUsageFalse_usesDefaultString() {
         BatteryInfo info =
                 BatteryInfo.getBatteryInfo(
                         mContext,
@@ -238,7 +259,7 @@
     }
 
     @Test
-    public void testGetBatteryInfo_charging_usesChargeTime() {
+    public void getBatteryInfo_charging_usesChargeTime() {
         doReturn(TEST_CHARGE_TIME_REMAINING / 1000)
                 .when(mBatteryUsageStats)
                 .getChargeTimeRemainingMs();
@@ -258,7 +279,7 @@
     }
 
     @Test
-    public void testGetBatteryInfo_pluggedInWithFullBattery_onlyShowBatteryLevel() {
+    public void getBatteryInfo_pluggedInWithFullBattery_onlyShowBatteryLevel() {
         mChargingBatteryBroadcast.putExtra(BatteryManager.EXTRA_LEVEL, 100);
 
         BatteryInfo info =
@@ -274,7 +295,7 @@
     }
 
     @Test
-    public void testGetBatteryInfo_chargingWithDefender_updateChargeLabel() {
+    public void getBatteryInfo_chargingWithDefender_updateChargeLabel() {
         doReturn(TEST_CHARGE_TIME_REMAINING).when(mBatteryUsageStats).getChargeTimeRemainingMs();
         mChargingBatteryBroadcast.putExtra(
                 BatteryManager.EXTRA_CHARGING_STATUS,
@@ -294,7 +315,7 @@
     }
 
     @Test
-    public void testGetBatteryInfo_getChargeTimeRemaining_updateSettingsGlobal() {
+    public void getBatteryInfo_getChargeTimeRemaining_updateSettingsGlobal() {
         doReturn(TEST_CHARGE_TIME_REMAINING).when(mBatteryUsageStats).getChargeTimeRemainingMs();
 
         BatteryInfo.getBatteryInfo(
@@ -310,7 +331,7 @@
     }
 
     @Test
-    public void testGetBatteryInfo_differentChargeTimeRemaining_updateSettingsGlobal() {
+    public void getBatteryInfo_differentChargeTimeRemaining_updateSettingsGlobal() {
         doReturn(TEST_CHARGE_TIME_REMAINING).when(mBatteryUsageStats).getChargeTimeRemainingMs();
         final long newTimeToFull = 300L;
         doReturn(newTimeToFull).when(mBatteryUsageStats).getChargeTimeRemainingMs();
@@ -327,16 +348,15 @@
     }
 
     @Test
-    public void testGetBatteryInfo_dockDefenderActive_updateChargeString() {
+    public void getBatteryInfo_dockDefenderActive_updateChargeString() {
         doReturn(TEST_CHARGE_TIME_REMAINING / 1000)
                 .when(mBatteryUsageStats)
                 .getChargeTimeRemainingMs();
         doReturn(true).when(mFeatureFactory.powerUsageFeatureProvider).isExtraDefend();
         Intent intent =
-                BatteryTestUtils.getCustomBatteryIntent(
+                createBatteryIntent(
                                 BatteryManager.BATTERY_PLUGGED_DOCK,
-                                50 /* level */,
-                                100 /* scale */,
+                                /* level= */ 50,
                                 BatteryManager.BATTERY_STATUS_CHARGING)
                         .putExtra(
                                 BatteryManager.EXTRA_CHARGING_STATUS,
@@ -355,7 +375,7 @@
     }
 
     @Test
-    public void testGetBatteryInfo_dockDefenderTemporarilyBypassed_updateChargeLabel() {
+    public void getBatteryInfo_dockDefenderTemporarilyBypassed_updateChargeLabel() {
         doReturn(REMAINING_TIME).when(mBatteryUsageStats).getChargeTimeRemainingMs();
         mChargingBatteryBroadcast.putExtra(
                 BatteryManager.EXTRA_CHARGING_STATUS, BatteryManager.CHARGING_POLICY_DEFAULT);
@@ -367,10 +387,9 @@
         BatteryInfo info =
                 BatteryInfo.getBatteryInfo(
                         mContext,
-                        BatteryTestUtils.getCustomBatteryIntent(
+                        createBatteryIntent(
                                 BatteryManager.BATTERY_PLUGGED_DOCK,
-                                50 /* level */,
-                                100 /* scale */,
+                                /* level= */ 50,
                                 BatteryManager.BATTERY_STATUS_CHARGING),
                         mBatteryUsageStats,
                         MOCK_ESTIMATE,
@@ -381,7 +400,7 @@
     }
 
     @Test
-    public void testGetBatteryInfo_dockDefenderFutureBypass_updateChargeLabel() {
+    public void getBatteryInfo_dockDefenderFutureBypass_updateChargeLabel() {
         doReturn(false).when(mFeatureFactory.powerUsageFeatureProvider).isExtraDefend();
         mChargingBatteryBroadcast.putExtra(
                 BatteryManager.EXTRA_CHARGING_STATUS, BatteryManager.CHARGING_POLICY_DEFAULT);
@@ -389,10 +408,9 @@
         BatteryInfo info =
                 BatteryInfo.getBatteryInfo(
                         mContext,
-                        BatteryTestUtils.getCustomBatteryIntent(
+                        createBatteryIntent(
                                 BatteryManager.BATTERY_PLUGGED_DOCK,
-                                50 /* level */,
-                                100 /* scale */,
+                                /* level= */ 50,
                                 BatteryManager.BATTERY_STATUS_CHARGING),
                         mBatteryUsageStats,
                         MOCK_ESTIMATE,
@@ -402,6 +420,284 @@
         assertThat(info.chargeLabel.toString()).contains(STATUS_CHARGING_FUTURE_BYPASS);
     }
 
+    @Test
+    public void getBatteryInfo_fastCharging_updateRemainingLabelAndStatusLabel() {
+        prepareTestGetBatteryInfoEnvironment(
+                /* remainingTimeMs= */ Duration.ofMinutes(90).toMillis(),
+                /* chargingStringV2Enabled= */ false);
+        Intent batteryIntent =
+                createIntentForGetBatteryInfoTest(
+                        ChargingType.WIRED, ChargingSpeed.FAST, /* batteryLevel= */ 61);
+        var expectedStatusLabel = "Charging rapidly";
+        var expectedRemainingLabel = "1 hr, 30 min left until full";
+        var expectedChargeLabel = "61% - " + expectedRemainingLabel;
+
+        assertGetBatteryInfo(
+                batteryIntent,
+                /* currentTimeMillis= */ UNUSED_TIME_MS,
+                expectedStatusLabel,
+                expectedRemainingLabel,
+                expectedChargeLabel);
+    }
+
+    @Test
+    public void getBatteryInfo_regularCharging_updateRemainingLabelAndStatusLabel() {
+        prepareTestGetBatteryInfoEnvironment(
+                /* remainingTimeMs= */ Duration.ofMinutes(80).toMillis(),
+                /* chargingStringV2Enabled= */ false);
+        Intent batteryIntent =
+                createIntentForGetBatteryInfoTest(
+                        ChargingType.WIRED, ChargingSpeed.REGULAR, /* batteryLevel= */ 33);
+        var expectedStatusLabel = "Charging";
+        var expectedRemainingLabel = "1 hr, 20 min left until full";
+        var expectedChargeLabel = "33% - " + expectedRemainingLabel;
+
+        assertGetBatteryInfo(
+                batteryIntent,
+                /* currentTimeMillis= */ UNUSED_TIME_MS,
+                expectedStatusLabel,
+                expectedRemainingLabel,
+                expectedChargeLabel);
+    }
+
+    @Test
+    public void getBatteryInfo_slowCharging_updateRemainingLabelAndStatusLabel() {
+        prepareTestGetBatteryInfoEnvironment(
+                /* remainingTimeMs= */ Duration.ofMinutes(100).toMillis(),
+                /* chargingStringV2Enabled= */ false);
+        Intent batteryIntent =
+                createIntentForGetBatteryInfoTest(
+                        ChargingType.WIRED, ChargingSpeed.SLOW, /* batteryLevel= */ 53);
+        var expectedStatusLabel = "Charging slowly";
+        var expectedRemainingLabel = "1 hr, 40 min left until full";
+        var expectedChargeLabel = "53% - " + expectedRemainingLabel;
+
+        assertGetBatteryInfo(
+                batteryIntent,
+                /* currentTimeMillis= */ UNUSED_TIME_MS,
+                expectedStatusLabel,
+                expectedRemainingLabel,
+                expectedChargeLabel);
+    }
+
+    @Test
+    public void getBatteryInfo_wirelessCharging_updateRemainingLabelAndStatusLabel() {
+        prepareTestGetBatteryInfoEnvironment(
+                /* remainingTimeMs= */ Duration.ofMinutes(130).toMillis(),
+                /* chargingStringV2Enabled= */ false);
+        Intent batteryIntent =
+                createIntentForGetBatteryInfoTest(
+                        ChargingType.WIRELESS, ChargingSpeed.REGULAR, /* batteryLevel= */ 10);
+        var expectedStatusLabel = "Charging wirelessly";
+        var expectedRemainingLabel = "2 hr, 10 min left until full";
+        var expectedChargeLabel = "10% - " + expectedRemainingLabel;
+
+        assertGetBatteryInfo(
+                batteryIntent,
+                /* currentTimeMillis= */ UNUSED_TIME_MS,
+                expectedStatusLabel,
+                expectedRemainingLabel,
+                expectedChargeLabel);
+    }
+
+    @Test
+    public void getBatteryInfo_dockedCharging_updateRemainingLabelAndStatusLabel() {
+        prepareTestGetBatteryInfoEnvironment(
+                /* remainingTimeMs= */ Duration.ofMinutes(30).toMillis(),
+                /* chargingStringV2Enabled= */ false);
+        Intent batteryIntent =
+                createIntentForGetBatteryInfoTest(
+                        ChargingType.DOCKED, ChargingSpeed.REGULAR, /* batteryLevel= */ 51);
+        var expectedStatusLabel = "Charging";
+        var expectedRemainingLabel = "30 min left until full";
+        var expectedChargeLabel = "51% - " + expectedRemainingLabel;
+
+        assertGetBatteryInfo(
+                batteryIntent,
+                /* currentTimeMillis= */ UNUSED_TIME_MS,
+                expectedStatusLabel,
+                expectedRemainingLabel,
+                expectedChargeLabel);
+    }
+
+    @Test
+    public void getBatteryInfo_fastChargingV2_updateRemainingLabelAndStatusLabel() {
+        prepareTestGetBatteryInfoEnvironment(
+                /* remainingTimeMs= */ Duration.ofMinutes(30).toMillis(),
+                /* chargingStringV2Enabled= */ true);
+        Intent batteryIntent =
+                createIntentForGetBatteryInfoTest(
+                        ChargingType.WIRED, ChargingSpeed.FAST, /* batteryLevel= */ 56);
+        var expectedStatusLabel = "Fast charging";
+        var expectedRemainingLabel = "Full by 1:30 PM";
+        var expectedChargeLabel = "56% - " + expectedStatusLabel + " - " + expectedRemainingLabel;
+        var currentTimeMillis = Instant.parse("2024-04-01T13:00:00Z").toEpochMilli();
+
+        assertGetBatteryInfo(
+                batteryIntent,
+                currentTimeMillis,
+                expectedStatusLabel,
+                expectedRemainingLabel,
+                expectedChargeLabel);
+    }
+
+    @Test
+    public void getBatteryInfo_regularChargingV2_updateRemainingLabelAndStatusLabel() {
+        prepareTestGetBatteryInfoEnvironment(
+                /* remainingTimeMs= */ Duration.ofHours(1).toMillis(),
+                /* chargingStringV2Enabled= */ true);
+        Intent batteryIntent =
+                createIntentForGetBatteryInfoTest(
+                        ChargingType.WIRED, ChargingSpeed.REGULAR, /* batteryLevel= */ 12);
+        var expectedStatusLabel = "Charging";
+        var expectedRemainingLabel = "Fully charged by 2:00 PM";
+        var expectedChargeLabel = "12% - " + expectedRemainingLabel;
+        var currentTimeMillis = Instant.parse("2024-04-01T13:00:00Z").toEpochMilli();
+
+        assertGetBatteryInfo(
+                batteryIntent,
+                currentTimeMillis,
+                expectedStatusLabel,
+                expectedRemainingLabel,
+                expectedChargeLabel);
+    }
+
+    @Test
+    public void getBatteryInfo_slowChargingV2_updateRemainingLabelAndStatusLabel() {
+        prepareTestGetBatteryInfoEnvironment(
+                /* remainingTimeMs= */ Duration.ofHours(2).toMillis(),
+                /* chargingStringV2Enabled= */ true);
+        Intent batteryIntent =
+                createIntentForGetBatteryInfoTest(
+                        ChargingType.WIRED, ChargingSpeed.SLOW, /* batteryLevel= */ 18);
+        var expectedStatusLabel = "Charging";
+        var expectedRemainingLabel = "Fully charged by 3:00 PM";
+        var expectedChargeLabel = "18% - " + expectedRemainingLabel;
+        var currentTimeMillis = Instant.parse("2024-04-01T13:00:00Z").toEpochMilli();
+
+        assertGetBatteryInfo(
+                batteryIntent,
+                currentTimeMillis,
+                expectedStatusLabel,
+                expectedRemainingLabel,
+                expectedChargeLabel);
+    }
+
+    @Test
+    public void getBatteryInfo_wirelessChargingV2_updateRemainingLabelAndStatusLabel() {
+        prepareTestGetBatteryInfoEnvironment(
+                /* remainingTimeMs= */ Duration.ofHours(1).toMillis(),
+                /* chargingStringV2Enabled= */ true);
+        Intent batteryIntent =
+                createIntentForGetBatteryInfoTest(
+                        ChargingType.WIRELESS, ChargingSpeed.REGULAR, /* batteryLevel= */ 45);
+        var expectedStatusLabel = "Charging";
+        var expectedRemainingLabel = "Fully charged by 4:00 PM";
+        var expectedChargeLabel = "45% - " + expectedRemainingLabel;
+        var currentTimeMillis = Instant.parse("2024-04-01T15:00:00Z").toEpochMilli();
+
+        assertGetBatteryInfo(
+                batteryIntent,
+                currentTimeMillis,
+                expectedStatusLabel,
+                expectedRemainingLabel,
+                expectedChargeLabel);
+    }
+
+    @Test
+    public void getBatteryInfo_dockedChargingV2_updateRemainingLabelAndStatusLabel() {
+        prepareTestGetBatteryInfoEnvironment(
+                /* remainingTimeMs= */ Duration.ofHours(1).toMillis(),
+                /* chargingStringV2Enabled= */ true);
+        Intent batteryIntent =
+                createIntentForGetBatteryInfoTest(
+                        ChargingType.DOCKED, ChargingSpeed.REGULAR, /* batteryLevel= */ 66);
+        var expectedStatusLabel = "Charging";
+        var expectedRemainingLabel = "Fully charged by 2:00 PM";
+        var expectedChargeLabel = "66% - " + expectedRemainingLabel;
+        var currentTimeMillis = Instant.parse("2021-02-09T13:00:00.00Z").toEpochMilli();
+
+        assertGetBatteryInfo(
+                batteryIntent,
+                currentTimeMillis,
+                expectedStatusLabel,
+                expectedRemainingLabel,
+                expectedChargeLabel);
+    }
+
+    private enum ChargingSpeed {
+        FAST,
+        REGULAR,
+        SLOW
+    }
+
+    private enum ChargingType {
+        WIRED,
+        WIRELESS,
+        DOCKED
+    }
+
+    private Intent createIntentForGetBatteryInfoTest(
+            ChargingType chargingType, ChargingSpeed chargingSpeed, int batteryLevel) {
+        return createBatteryIntent(
+                        CHARGING_TYPE_MAP.get(chargingType),
+                        batteryLevel,
+                        BatteryManager.BATTERY_STATUS_CHARGING)
+                .putExtra(
+                        BatteryManager.EXTRA_MAX_CHARGING_CURRENT,
+                        CHARGING_SPEED_MAP.get(chargingSpeed))
+                .putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, 5000000);
+    }
+
+    private void prepareTestGetBatteryInfoEnvironment(
+            long remainingTimeMs, boolean chargingStringV2Enabled) {
+        when(mBatteryUsageStats.getChargeTimeRemainingMs()).thenReturn(remainingTimeMs);
+        SystemProperties.set(
+                com.android.settingslib.fuelgauge.BatteryUtils.PROPERTY_CHARGING_STRING_V2_KEY,
+                String.valueOf(chargingStringV2Enabled));
+        Settings.Global.putInt(
+                mContext.getContentResolver(),
+                BatteryUtils.SETTINGS_GLOBAL_DOCK_DEFENDER_BYPASS,
+                1);
+    }
+
+    private void assertGetBatteryInfo(
+            Intent batteryIntent,
+            long currentTimeMillis,
+            String expectedStatusLabel,
+            String expectedRemainingLabel,
+            String expectedChargeLabel) {
+        mContext.getResources().getConfiguration().setLocale(Locale.US);
+        mContext.getSystemService(AlarmManager.class).setTimeZone("UTC");
+        var info =
+                BatteryInfo.getBatteryInfo(
+                        mContext,
+                        batteryIntent,
+                        mBatteryUsageStats,
+                        MOCK_ESTIMATE,
+                        /* elapsedRealtimeUs= */ UNUSED_TIME_MS,
+                        /* shortString= */ false,
+                        /* currentTimeMillis= */ currentTimeMillis);
+
+        assertWithMessage("statusLabel is incorrect")
+                .that(info.statusLabel)
+                .isEqualTo(expectedStatusLabel);
+        assertWithMessage("remainingLabel is incorrect")
+                .that(info.remainingLabel.toString())
+                .isEqualTo(expectedRemainingLabel);
+        assertWithMessage("chargeLabel is incorrect")
+                .that(info.chargeLabel.toString())
+                .isEqualTo(expectedChargeLabel);
+    }
+
+    private static Intent createBatteryIntent(int plugged, int level, int status) {
+        return new Intent()
+                .putExtra(BatteryManager.EXTRA_PLUGGED, plugged)
+                .putExtra(BatteryManager.EXTRA_LEVEL, level)
+                .putExtra(BatteryManager.EXTRA_SCALE, 100)
+                .putExtra(BatteryManager.EXTRA_STATUS, status);
+    }
+
     // Make our battery stats return a sequence of battery events.
     private void mockBatteryStatsHistory() {
         // Mock out new data every time iterateBatteryStatsHistory is called.