diff --git a/core/api/current.txt b/core/api/current.txt
index 639a99134d3dbf3bdcbb6c29cf465ddf98701c5d..9c408bb794493605e6fbe03d4ad0dbf9b1557470 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -16259,6 +16259,8 @@ package android.graphics {
 
   public static class Paint.FontMetricsInt {
     ctor public Paint.FontMetricsInt();
+    method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public void set(@NonNull android.graphics.Paint.FontMetricsInt);
+    method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public void set(@NonNull android.graphics.Paint.FontMetrics);
     field public int ascent;
     field public int bottom;
     field public int descent;
@@ -46718,6 +46720,7 @@ package android.text {
     method @NonNull public android.text.DynamicLayout.Builder setJustificationMode(int);
     method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.text.DynamicLayout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig);
     method @NonNull public android.text.DynamicLayout.Builder setLineSpacing(float, @FloatRange(from=0.0) float);
+    method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @NonNull public android.text.DynamicLayout.Builder setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics);
     method @NonNull public android.text.DynamicLayout.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic);
     method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.DynamicLayout.Builder setUseBoundsForWidth(boolean);
     method @NonNull public android.text.DynamicLayout.Builder setUseLineSpacingFromFallbacks(boolean);
@@ -46906,6 +46909,7 @@ package android.text {
     method public int getLineVisibleEnd(int);
     method public float getLineWidth(int);
     method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @IntRange(from=1) public final int getMaxLines();
+    method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @Nullable public android.graphics.Paint.FontMetrics getMinimumFontMetrics();
     method public int getOffsetForHorizontal(int, float);
     method public int getOffsetToLeftOf(int);
     method public int getOffsetToRightOf(int);
@@ -46972,6 +46976,7 @@ package android.text {
     method @NonNull public android.text.Layout.Builder setLineSpacingAmount(float);
     method @NonNull public android.text.Layout.Builder setLineSpacingMultiplier(@FloatRange(from=0) float);
     method @NonNull public android.text.Layout.Builder setMaxLines(@IntRange(from=1) int);
+    method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @NonNull public android.text.Layout.Builder setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics);
     method @NonNull public android.text.Layout.Builder setRightIndents(@Nullable int[]);
     method @NonNull public android.text.Layout.Builder setTextDirectionHeuristic(@NonNull android.text.TextDirectionHeuristic);
     method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.Layout.Builder setUseBoundsForWidth(boolean);
@@ -47243,6 +47248,7 @@ package android.text {
     method @NonNull public android.text.StaticLayout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig);
     method @NonNull public android.text.StaticLayout.Builder setLineSpacing(float, @FloatRange(from=0.0) float);
     method @NonNull public android.text.StaticLayout.Builder setMaxLines(@IntRange(from=0) int);
+    method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @NonNull public android.text.StaticLayout.Builder setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics);
     method public android.text.StaticLayout.Builder setText(CharSequence);
     method @NonNull public android.text.StaticLayout.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic);
     method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.StaticLayout.Builder setUseBoundsForWidth(boolean);
@@ -59928,6 +59934,7 @@ package android.widget {
     method public int getMinHeight();
     method public int getMinLines();
     method public int getMinWidth();
+    method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @Nullable public android.graphics.Paint.FontMetrics getMinimumFontMetrics();
     method public final android.text.method.MovementMethod getMovementMethod();
     method public int getOffsetForPosition(float, float);
     method public android.text.TextPaint getPaint();
@@ -60064,6 +60071,7 @@ package android.widget {
     method public void setMinHeight(int);
     method public void setMinLines(int);
     method public void setMinWidth(int);
+    method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") public void setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics);
     method public final void setMovementMethod(android.text.method.MovementMethod);
     method public void setOnEditorActionListener(android.widget.TextView.OnEditorActionListener);
     method public void setPaintFlags(int);
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 65a1da6b81b84a883c2c72e9f7b052896d7debb3..4c8188801eff6b49e8cb5386cb83f38a362b30f4 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -190,7 +190,8 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
             @Nullable TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth,
             boolean useFallbackLineSpacing) {
         return replaceOrMake(source, paint, outerWidth, align, 1.0f, 0.0f, metrics, includePad,
-                ellipsize, ellipsizedWidth, useFallbackLineSpacing, false /* useBoundsForWidth */);
+                ellipsize, ellipsizedWidth, useFallbackLineSpacing, false /* useBoundsForWidth */,
+                null /* minimumFontMetrics */);
     }
 
     /** @hide */
@@ -199,7 +200,8 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
             @NonNull Alignment align, float spacingMultiplier, float spacingAmount,
             @NonNull BoringLayout.Metrics metrics, boolean includePad,
             @Nullable TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth,
-            boolean useFallbackLineSpacing, boolean useBoundsForWidth) {
+            boolean useFallbackLineSpacing, boolean useBoundsForWidth,
+            @Nullable Paint.FontMetrics minimumFontMetrics) {
         boolean trust;
 
         if (ellipsize == null || ellipsize == TextUtils.TruncateAt.MARQUEE) {
@@ -270,7 +272,8 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
                 spacingAdd, includePad, false /* fallbackLineSpacing */,
                 outerwidth /* ellipsizedWidth */, null /* ellipsize */, 1 /* maxLines */,
                 BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null /* leftIndents */,
-                null /* rightIndents */, JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false);
+                null /* rightIndents */, JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false,
+                null);
 
         mEllipsizedWidth = outerwidth;
         mEllipsizedStart = 0;
@@ -343,7 +346,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
                 ellipsizedWidth, ellipsize, 1 /* maxLines */,
                 BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null /* leftIndents */,
                 null /* rightIndents */, JUSTIFICATION_MODE_NONE,
-                LineBreakConfig.NONE, metrics, false /* useBoundsForWidth */);
+                LineBreakConfig.NONE, metrics, false /* useBoundsForWidth */, null);
     }
 
     /** @hide */
@@ -359,12 +362,13 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
             int ellipsizedWidth,
             TextUtils.TruncateAt ellipsize,
             Metrics metrics,
-            boolean useBoundsForWidth) {
+            boolean useBoundsForWidth,
+            @Nullable Paint.FontMetrics minimumFontMetrics) {
         this(text, paint, width, align, TextDirectionHeuristics.LTR,
                 spacingMult, spacingAdd, includePad, fallbackLineSpacing, ellipsizedWidth,
                 ellipsize, 1 /* maxLines */, Layout.BREAK_STRATEGY_SIMPLE,
                 Layout.HYPHENATION_FREQUENCY_NONE, null, null, Layout.JUSTIFICATION_MODE_NONE,
-                LineBreakConfig.NONE, metrics, useBoundsForWidth);
+                LineBreakConfig.NONE, metrics, useBoundsForWidth, minimumFontMetrics);
     }
 
     /* package */ BoringLayout(
@@ -387,12 +391,13 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
             int justificationMode,
             LineBreakConfig lineBreakConfig,
             Metrics metrics,
-            boolean useBoundsForWidth) {
+            boolean useBoundsForWidth,
+            @Nullable Paint.FontMetrics minimumFontMetrics) {
 
         super(text, paint, width, align, textDir, spacingMult, spacingAdd, includePad,
                 fallbackLineSpacing, ellipsizedWidth, ellipsize, maxLines, breakStrategy,
                 hyphenationFrequency, leftIndents, rightIndents, justificationMode,
-                lineBreakConfig, useBoundsForWidth);
+                lineBreakConfig, useBoundsForWidth, minimumFontMetrics);
 
 
         boolean trust;
@@ -548,6 +553,15 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
     public static @Nullable Metrics isBoring(@NonNull CharSequence text, @NonNull TextPaint paint,
             @NonNull TextDirectionHeuristic textDir, boolean useFallbackLineSpacing,
             @Nullable Metrics metrics) {
+        return isBoring(text, paint, textDir, useFallbackLineSpacing, null, metrics);
+    }
+
+    /**
+     * @hide
+     */
+    public static @Nullable Metrics isBoring(@NonNull CharSequence text, @NonNull TextPaint paint,
+            @NonNull TextDirectionHeuristic textDir, boolean useFallbackLineSpacing,
+            @Nullable Paint.FontMetrics minimumFontMetrics, @Nullable Metrics metrics) {
         final int textLength = text.length();
         if (hasAnyInterestingChars(text, textLength)) {
            return null;  // There are some interesting characters. Not boring.
@@ -570,6 +584,19 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
             fm.reset();
         }
 
+        if (ClientFlags.fixLineHeightForLocale()) {
+            if (minimumFontMetrics == null) {
+                paint.getFontMetricsIntForLocale(fm);
+            } else {
+                fm.set(minimumFontMetrics);
+                // Because the font metrics is provided by public APIs, adjust the top/bottom with
+                // ascent/descent: top must be smaller than ascent, bottom must be larger than
+                // descent.
+                fm.top = Math.min(fm.top, fm.ascent);
+                fm.bottom = Math.max(fm.bottom, fm.descent);
+            }
+        }
+
         TextLine line = TextLine.obtain();
         line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
                 Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null,
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/text/ClientFlags.java
index e17a955121b0df02cd47c3e407f1d1831ac18cd3..0421d5aaa69b11bb13c9134e1bfa5c27cc85664c 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/core/java/android/text/ClientFlags.java
@@ -47,4 +47,11 @@ public class ClientFlags {
     public static boolean useBoundsForWidth() {
         return TextFlags.isFeatureEnabled(Flags.FLAG_USE_BOUNDS_FOR_WIDTH);
     }
+
+    /**
+     * @see Flags#fixLineHeightForLocale()
+     */
+    public static boolean fixLineHeightForLocale() {
+        return TextFlags.isFeatureEnabled(Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE);
+    }
 }
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index a0cd0748f5094c977411dc53518c967c450ffd20..7b9cb6afd6a0d0a4d457d5f589468e26575984a2 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -16,6 +16,7 @@
 
 package android.text;
 
+import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
 import static com.android.text.flags.Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN;
 import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
 
@@ -314,6 +315,43 @@ public class DynamicLayout extends Layout {
             return this;
         }
 
+        /**
+         * Set the minimum font metrics used for line spacing.
+         *
+         * <p>
+         * {@code null} is the default value. If {@code null} is set or left as default, the
+         * font metrics obtained by {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} is
+         * used.
+         *
+         * <p>
+         * The minimum meaning here is the minimum value of line spacing: maximum value of
+         * {@link Paint#ascent()}, minimum value of {@link Paint#descent()}.
+         *
+         * <p>
+         * By setting this value, each line will have minimum line spacing regardless of the text
+         * rendered. For example, usually Japanese script has larger vertical metrics than Latin
+         * script. By setting the metrics obtained by
+         * {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} for Japanese or leave it
+         * {@code null} if the Paint's locale is Japanese, the line spacing for Japanese is reserved
+         * if the text is an English text. If the vertical metrics of the text is larger than
+         * Japanese, for example Burmese, the bigger font metrics is used.
+         *
+         * @param minimumFontMetrics A minimum font metrics. Passing {@code null} for using the
+         *                          value obtained by
+         *                          {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)}
+         * @see android.widget.TextView#setMinimumFontMetrics(Paint.FontMetrics)
+         * @see android.widget.TextView#getMinimumFontMetrics()
+         * @see Layout#getMinimumFontMetrics()
+         * @see Layout.Builder#setMinimumFontMetrics(Paint.FontMetrics)
+         * @see StaticLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics)
+         */
+        @NonNull
+        @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE)
+        public Builder setMinimumFontMetrics(@Nullable Paint.FontMetrics minimumFontMetrics) {
+            mMinimumFontMetrics = minimumFontMetrics;
+            return this;
+        }
+
         /**
          * Build the {@link DynamicLayout} after options have been set.
          *
@@ -347,6 +385,7 @@ public class DynamicLayout extends Layout {
         private int mEllipsizedWidth;
         private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE;
         private boolean mUseBoundsForWidth;
+        private @Nullable Paint.FontMetrics mMinimumFontMetrics;
 
         private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
 
@@ -422,7 +461,7 @@ public class DynamicLayout extends Layout {
                 false /* fallbackLineSpacing */, ellipsizedWidth, ellipsize,
                 Integer.MAX_VALUE /* maxLines */, breakStrategy, hyphenationFrequency,
                 null /* leftIndents */, null /* rightIndents */, justificationMode,
-                lineBreakConfig, false /* useBoundsForWidth */);
+                lineBreakConfig, false /* useBoundsForWidth */, null /* minimumFontMetrics */);
 
         final Builder b = Builder.obtain(base, paint, width)
                 .setAlignment(align)
@@ -448,7 +487,7 @@ public class DynamicLayout extends Layout {
                 b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize,
                 Integer.MAX_VALUE /* maxLines */, b.mBreakStrategy, b.mHyphenationFrequency,
                 null /* leftIndents */, null /* rightIndents */, b.mJustificationMode,
-                b.mLineBreakConfig, b.mUseBoundsForWidth);
+                b.mLineBreakConfig, b.mUseBoundsForWidth, b.mMinimumFontMetrics);
 
         mDisplay = b.mDisplay;
         mIncludePad = b.mIncludePad;
@@ -476,6 +515,7 @@ public class DynamicLayout extends Layout {
         mBase = b.mBase;
         mFallbackLineSpacing = b.mFallbackLineSpacing;
         mUseBoundsForWidth = b.mUseBoundsForWidth;
+        mMinimumFontMetrics = b.mMinimumFontMetrics;
         if (b.mEllipsize != null) {
             mInts = new PackedIntVector(COLUMNS_ELLIPSIZE);
             mEllipsizedWidth = b.mEllipsizedWidth;
@@ -672,6 +712,7 @@ public class DynamicLayout extends Layout {
                 .setAddLastLineLineSpacing(!islast)
                 .setIncludePad(false)
                 .setUseBoundsForWidth(mUseBoundsForWidth)
+                .setMinimumFontMetrics(mMinimumFontMetrics)
                 .setCalculateBounds(true);
 
         reflowed = b.buildPartialStaticLayoutForDynamicLayout(true /* trackpadding */, reflowed);
@@ -1324,6 +1365,7 @@ public class DynamicLayout extends Layout {
     private Rect mTempRect = new Rect();
 
     private boolean mUseBoundsForWidth;
+    @Nullable Paint.FontMetrics mMinimumFontMetrics;
 
     @UnsupportedAppUsage
     private static StaticLayout sStaticLayout = null;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 4f4dea7801713ef652b992c19b97a0aa50b73a16..47c29d96855868c527ea4c482bff571d8de6fc65 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -16,6 +16,7 @@
 
 package android.text;
 
+import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
 import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
 
 import android.annotation.FlaggedApi;
@@ -287,7 +288,7 @@ public abstract class Layout {
         this(text, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR,
                 spacingMult, spacingAdd, false, false, 0, null, Integer.MAX_VALUE,
                 BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null, null,
-                JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false);
+                JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false, null);
     }
 
     /**
@@ -336,7 +337,8 @@ public abstract class Layout {
             int[] rightIndents,
             int justificationMode,
             LineBreakConfig lineBreakConfig,
-            boolean useBoundsForWidth
+            boolean useBoundsForWidth,
+            Paint.FontMetrics minimumFontMetrics
     ) {
 
         if (width < 0)
@@ -371,6 +373,7 @@ public abstract class Layout {
         mJustificationMode = justificationMode;
         mLineBreakConfig = lineBreakConfig;
         mUseBoundsForWidth = useBoundsForWidth;
+        mMinimumFontMetrics = minimumFontMetrics;
     }
 
     /**
@@ -3332,6 +3335,7 @@ public abstract class Layout {
     private int mJustificationMode;
     private LineBreakConfig mLineBreakConfig;
     private boolean mUseBoundsForWidth;
+    private @Nullable Paint.FontMetrics mMinimumFontMetrics;
 
     /** @hide */
     @IntDef(prefix = { "DIR_" }, value = {
@@ -3787,12 +3791,48 @@ public abstract class Layout {
             return this;
         }
 
+        /**
+         * Set the minimum font metrics used for line spacing.
+         *
+         * <p>
+         * {@code null} is the default value. If {@code null} is set or left it as default, the font
+         * metrics obtained by {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} is used.
+         *
+         * <p>
+         * The minimum meaning here is the minimum value of line spacing: maximum value of
+         * {@link Paint#ascent()}, minimum value of {@link Paint#descent()}.
+         *
+         * <p>
+         * By setting this value, each line will have minimum line spacing regardless of the text
+         * rendered. For example, usually Japanese script has larger vertical metrics than Latin
+         * script. By setting the metrics obtained by
+         * {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} for Japanese or leave it
+         * {@code null} if the Paint's locale is Japanese, the line spacing for Japanese is reserved
+         * if the text is an English text. If the vertical metrics of the text is larger than
+         * Japanese, for example Burmese, the bigger font metrics is used.
+         *
+         * @param minimumFontMetrics A minimum font metrics. Passing {@code null} for using the
+         *                          value obtained by
+         *                          {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)}
+         * @see android.widget.TextView#setMinimumFontMetrics(Paint.FontMetrics)
+         * @see android.widget.TextView#getMinimumFontMetrics()
+         * @see Layout#getMinimumFontMetrics()
+         * @see StaticLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics)
+         * @see DynamicLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics)
+         */
+        @NonNull
+        @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE)
+        public Builder setMinimumFontMetrics(@Nullable Paint.FontMetrics minimumFontMetrics) {
+            mMinimumFontMetrics = minimumFontMetrics;
+            return this;
+        }
+
         private BoringLayout.Metrics isBoring() {
             if (mStart != 0 || mEnd != mText.length()) {  // BoringLayout only support entire text.
                 return null;
             }
             BoringLayout.Metrics metrics = BoringLayout.isBoring(mText, mPaint, mTextDir,
-                    mFallbackLineSpacing, null);
+                    mFallbackLineSpacing, mMinimumFontMetrics, null);
             if (metrics == null) {
                 return null;
             }
@@ -3833,7 +3873,8 @@ public abstract class Layout {
                         mText, mPaint, mWidth, mAlignment, mTextDir, mSpacingMult, mSpacingAdd,
                         mIncludePad, mFallbackLineSpacing, mEllipsizedWidth, mEllipsize, mMaxLines,
                         mBreakStrategy, mHyphenationFrequency, mLeftIndents, mRightIndents,
-                        mJustificationMode, mLineBreakConfig, metrics, mUseBoundsForWidth);
+                        mJustificationMode, mLineBreakConfig, metrics, mUseBoundsForWidth,
+                        mMinimumFontMetrics);
             }
         }
 
@@ -3858,6 +3899,7 @@ public abstract class Layout {
         private int mJustificationMode = JUSTIFICATION_MODE_NONE;
         private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE;
         private boolean mUseBoundsForWidth;
+        private Paint.FontMetrics mMinimumFontMetrics;
     }
 
     ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -4164,4 +4206,22 @@ public abstract class Layout {
     public boolean getUseBoundsForWidth() {
         return mUseBoundsForWidth;
     }
+
+    /**
+     * Get the minimum font metrics used for line spacing.
+     *
+     * @see android.widget.TextView#setMinimumFontMetrics(Paint.FontMetrics)
+     * @see android.widget.TextView#getMinimumFontMetrics()
+     * @see Layout.Builder#setMinimumFontMetrics(Paint.FontMetrics)
+     * @see StaticLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics)
+     * @see DynamicLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics)
+     *
+     * @return a minimum font metrics. {@code null} for using the value obtained by
+     *         {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)}
+     */
+    @Nullable
+    @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE)
+    public Paint.FontMetrics getMinimumFontMetrics() {
+        return mMinimumFontMetrics;
+    }
 }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 01279cea073f2c3034a760a04b80315ccf35ee03..77e616b358cba716fb805cf57784ef50b4db0ecb 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -16,6 +16,7 @@
 
 package android.text;
 
+import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
 import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
 
 import android.annotation.FlaggedApi;
@@ -459,6 +460,43 @@ public class StaticLayout extends Layout {
             return this;
         }
 
+        /**
+         * Set the minimum font metrics used for line spacing.
+         *
+         * <p>
+         * {@code null} is the default value. If {@code null} is set or left as default, the
+         * font metrics obtained by {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} is
+         * used.
+         *
+         * <p>
+         * The minimum meaning here is the minimum value of line spacing: maximum value of
+         * {@link Paint#ascent()}, minimum value of {@link Paint#descent()}.
+         *
+         * <p>
+         * By setting this value, each line will have minimum line spacing regardless of the text
+         * rendered. For example, usually Japanese script has larger vertical metrics than Latin
+         * script. By setting the metrics obtained by
+         * {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} for Japanese or leave it
+         * {@code null} if the Paint's locale is Japanese, the line spacing for Japanese is reserved
+         * if the text is an English text. If the vertical metrics of the text is larger than
+         * Japanese, for example Burmese, the bigger font metrics is used.
+         *
+         * @param minimumFontMetrics A minimum font metrics. Passing {@code null} for using the
+         *                          value obtained by
+         *                          {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)}
+         * @see android.widget.TextView#setMinimumFontMetrics(Paint.FontMetrics)
+         * @see android.widget.TextView#getMinimumFontMetrics()
+         * @see Layout#getMinimumFontMetrics()
+         * @see Layout.Builder#setMinimumFontMetrics(Paint.FontMetrics)
+         * @see DynamicLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics)
+         */
+        @NonNull
+        @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE)
+        public Builder setMinimumFontMetrics(@Nullable Paint.FontMetrics minimumFontMetrics) {
+            mMinimumFontMetrics = minimumFontMetrics;
+            return this;
+        }
+
         /**
          * Build the {@link StaticLayout} after options have been set.
          *
@@ -520,6 +558,7 @@ public class StaticLayout extends Layout {
         private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE;
         private boolean mUseBoundsForWidth;
         private boolean mCalculateBounds;
+        @Nullable private Paint.FontMetrics mMinimumFontMetrics;
 
         private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
 
@@ -550,7 +589,8 @@ public class StaticLayout extends Layout {
                 null,  // rightIndents
                 JUSTIFICATION_MODE_NONE,
                 null,  // lineBreakConfig,
-                false  // useBoundsForWidth
+                false,  // useBoundsForWidth
+                null  // minimumFontMetrics
         );
 
         mColumns = COLUMNS_ELLIPSIZE;
@@ -627,7 +667,8 @@ public class StaticLayout extends Layout {
                 b.mPaint, b.mWidth, b.mAlignment, b.mTextDir, b.mSpacingMult, b.mSpacingAdd,
                 b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize,
                 b.mMaxLines, b.mBreakStrategy, b.mHyphenationFrequency, b.mLeftIndents,
-                b.mRightIndents, b.mJustificationMode, b.mLineBreakConfig, b.mUseBoundsForWidth);
+                b.mRightIndents, b.mJustificationMode, b.mLineBreakConfig, b.mUseBoundsForWidth,
+                b.mMinimumFontMetrics);
 
         mColumns = columnSize;
         if (b.mEllipsize != null) {
@@ -711,6 +752,35 @@ public class StaticLayout extends Layout {
             indents = null;
         }
 
+        int defaultTop;
+        int defaultAscent;
+        int defaultDescent;
+        int defaultBottom;
+        if (ClientFlags.fixLineHeightForLocale()) {
+            if (b.mMinimumFontMetrics != null) {
+                defaultTop = (int) Math.floor(b.mMinimumFontMetrics.top);
+                defaultAscent = Math.round(b.mMinimumFontMetrics.ascent);
+                defaultDescent = Math.round(b.mMinimumFontMetrics.descent);
+                defaultBottom = (int) Math.ceil(b.mMinimumFontMetrics.bottom);
+            } else {
+                paint.getFontMetricsIntForLocale(fm);
+                defaultTop = fm.top;
+                defaultAscent = fm.ascent;
+                defaultDescent = fm.descent;
+                defaultBottom = fm.bottom;
+            }
+
+            // Because the font metrics is provided by public APIs, adjust the top/bottom with
+            // ascent/descent: top must be smaller than ascent, bottom must be larger than descent.
+            defaultTop = Math.min(defaultTop, defaultAscent);
+            defaultBottom = Math.max(defaultBottom, defaultDescent);
+        } else {
+            defaultTop = 0;
+            defaultAscent = 0;
+            defaultDescent = 0;
+            defaultBottom = 0;
+        }
+
         final LineBreaker lineBreaker = new LineBreaker.Builder()
                 .setBreakStrategy(b.mBreakStrategy)
                 .setHyphenationFrequency(getBaseHyphenationFrequency(b.mHyphenationFrequency))
@@ -889,7 +959,10 @@ public class StaticLayout extends Layout {
             // measuring
             int here = paraStart;
 
-            int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
+            int fmTop = defaultTop;
+            int fmBottom = defaultBottom;
+            int fmAscent = defaultAscent;
+            int fmDescent = defaultDescent;
             int fmCacheIndex = 0;
             int spanEndCacheIndex = 0;
             int breakIndex = 0;
@@ -982,7 +1055,15 @@ public class StaticLayout extends Layout {
                 && mLineCount < mMaximumVisibleLineCount) {
             final MeasuredParagraph measuredPara =
                     MeasuredParagraph.buildForBidi(source, bufEnd, bufEnd, textDir, null);
-            paint.getFontMetricsInt(fm);
+            if (ClientFlags.fixLineHeightForLocale()) {
+                fm.top = defaultTop;
+                fm.ascent = defaultAscent;
+                fm.descent = defaultDescent;
+                fm.bottom = defaultBottom;
+            } else {
+                paint.getFontMetricsInt(fm);
+            }
+
             v = out(source,
                     bufEnd, bufEnd, fm.ascent, fm.descent,
                     fm.top, fm.bottom,
diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java
index b8b30c230e5e24c315a3993107d4995fe33cd1f1..24663862400dc8ba5925f3e3d9f470341442c693 100644
--- a/core/java/android/text/TextFlags.java
+++ b/core/java/android/text/TextFlags.java
@@ -58,6 +58,7 @@ public final class TextFlags {
             Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN,
             Flags.FLAG_PHRASE_STRICT_FALLBACK,
             Flags.FLAG_USE_BOUNDS_FOR_WIDTH,
+            Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE,
     };
 
     /**
@@ -69,6 +70,7 @@ public final class TextFlags {
             Flags.noBreakNoHyphenationSpan(),
             Flags.phraseStrictFallback(),
             Flags.useBoundsForWidth(),
+            Flags.fixLineHeightForLocale(),
     };
 
     /**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a0628c4b95804d2ee043d2e0c217e45a4b1339d5..6da6a64dc0423bc93bfa53fdfdad4cad0703c571 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -28,6 +28,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_C
 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
 import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
 
+import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
 import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
 
 import android.R;
@@ -865,6 +866,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
     private final boolean mUseTextPaddingForUiTranslation;
 
     private boolean mUseBoundsForWidth;
+    @Nullable private Paint.FontMetrics mMinimumFontMetrics;
 
     @ViewDebug.ExportedProperty(category = "text")
     @UnsupportedAppUsage
@@ -4900,6 +4902,58 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         return mUseBoundsForWidth;
     }
 
+    /**
+     * Set the minimum font metrics used for line spacing.
+     *
+     * <p>
+     * {@code null} is the default value. If {@code null} is set or left as default, the font
+     * metrics obtained by {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)} is used.
+     *
+     * <p>
+     * The minimum meaning here is the minimum value of line spacing: maximum value of
+     * {@link Paint#ascent()}, minimum value of {@link Paint#descent()}.
+     *
+     * <p>
+     * By setting this value, each line will have minimum line spacing regardless of the text
+     * rendered. For example, usually Japanese script has larger vertical metrics than Latin script.
+     * By setting the metrics obtained by {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)}
+     * for Japanese or leave it {@code null} if the TextView's locale or system locale is Japanese,
+     * the line spacing for Japanese is reserved if the TextView contains English text. If the
+     * vertical metrics of the text is larger than Japanese, for example Burmese, the bigger font
+     * metrics is used.
+     *
+     * @param minimumFontMetrics A minimum font metrics. Passing {@code null} for using the value
+     *                           obtained by
+     *                           {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)}
+     * @see #getMinimumFontMetrics()
+     * @see Layout#getMinimumFontMetrics()
+     * @see Layout.Builder#setMinimumFontMetrics(Paint.FontMetrics)
+     * @see StaticLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics)
+     * @see DynamicLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics)
+     */
+    @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE)
+    public void setMinimumFontMetrics(@Nullable Paint.FontMetrics minimumFontMetrics) {
+        mMinimumFontMetrics = minimumFontMetrics;
+    }
+
+    /**
+     * Get the minimum font metrics used for line spacing.
+     *
+     * @see #setMinimumFontMetrics(Paint.FontMetrics)
+     * @see Layout#getMinimumFontMetrics()
+     * @see Layout.Builder#setMinimumFontMetrics(Paint.FontMetrics)
+     * @see StaticLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics)
+     * @see DynamicLayout.Builder#setMinimumFontMetrics(Paint.FontMetrics)
+     *
+     * @return a minimum font metrics. {@code null} for using the value obtained by
+     *         {@link Paint#getFontMetricsForLocale(Paint.FontMetrics)}
+     */
+    @Nullable
+    @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE)
+    public Paint.FontMetrics getMinimumFontMetrics() {
+        return mMinimumFontMetrics;
+    }
+
     /**
      * @return whether fallback line spacing is enabled, {@code true} by default
      *
@@ -10683,7 +10737,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
 
             if (hintBoring == UNKNOWN_BORING) {
                 hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
-                        isFallbackLineSpacingForBoringLayout(), mHintBoring);
+                        isFallbackLineSpacingForBoringLayout(),
+                        mMinimumFontMetrics, mHintBoring);
                 if (hintBoring != null) {
                     mHintBoring = hintBoring;
                 }
@@ -10732,7 +10787,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                         .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
                         .setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
                                 mLineBreakStyle, mLineBreakWordStyle))
-                        .setUseBoundsForWidth(mUseBoundsForWidth);
+                        .setUseBoundsForWidth(mUseBoundsForWidth)
+                        .setMinimumFontMetrics(mMinimumFontMetrics);
                 if (shouldEllipsize) {
                     builder.setEllipsize(mEllipsize)
                             .setEllipsizedWidth(ellipsisWidth);
@@ -10796,12 +10852,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                             mLineBreakStyle, mLineBreakWordStyle))
                     .setUseBoundsForWidth(mUseBoundsForWidth)
                     .setEllipsize(getKeyListener() == null ? effectiveEllipsize : null)
-                    .setEllipsizedWidth(ellipsisWidth);
+                    .setEllipsizedWidth(ellipsisWidth)
+                    .setMinimumFontMetrics(mMinimumFontMetrics);
             result = builder.build();
         } else {
             if (boring == UNKNOWN_BORING) {
                 boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir,
-                        isFallbackLineSpacingForBoringLayout(), mBoring);
+                        isFallbackLineSpacingForBoringLayout(), mMinimumFontMetrics, mBoring);
                 if (boring != null) {
                     mBoring = boring;
                 }
@@ -10815,7 +10872,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                                 wantWidth, alignment, mSpacingMult, mSpacingAdd,
                                 boring, mIncludePad, null, wantWidth,
                                 isFallbackLineSpacingForBoringLayout(),
-                                mUseBoundsForWidth);
+                                mUseBoundsForWidth, mMinimumFontMetrics);
                     } else {
                         result = new BoringLayout(
                                 mTransformed,
@@ -10829,7 +10886,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                                 wantWidth,
                                 null,
                                 boring,
-                                mUseBoundsForWidth);
+                                mUseBoundsForWidth,
+                                mMinimumFontMetrics);
                     }
 
                     if (useSaved) {
@@ -10841,7 +10899,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                                 wantWidth, alignment, mSpacingMult, mSpacingAdd,
                                 boring, mIncludePad, effectiveEllipsize,
                                 ellipsisWidth, isFallbackLineSpacingForBoringLayout(),
-                                mUseBoundsForWidth);
+                                mUseBoundsForWidth, mMinimumFontMetrics);
                     } else {
                         result = new BoringLayout(
                                 mTransformed,
@@ -10855,7 +10913,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                                 ellipsisWidth,
                                 effectiveEllipsize,
                                 boring,
-                                mUseBoundsForWidth);
+                                mUseBoundsForWidth,
+                                mMinimumFontMetrics);
                     }
                 }
             }
@@ -10874,7 +10933,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
                     .setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
                             mLineBreakStyle, mLineBreakWordStyle))
-                    .setUseBoundsForWidth(mUseBoundsForWidth);
+                    .setUseBoundsForWidth(mUseBoundsForWidth)
+                    .setMinimumFontMetrics(mMinimumFontMetrics);
             if (shouldEllipsize) {
                 builder.setEllipsize(effectiveEllipsize)
                         .setEllipsizedWidth(ellipsisWidth);
@@ -11002,7 +11062,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
 
             if (des < 0) {
                 boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir,
-                        isFallbackLineSpacingForBoringLayout(), mBoring);
+                        isFallbackLineSpacingForBoringLayout(), mMinimumFontMetrics, mBoring);
                 if (boring != null) {
                     mBoring = boring;
                 }
@@ -11042,7 +11102,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
 
                 if (hintDes < 0) {
                     hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
-                            isFallbackLineSpacingForBoringLayout(), mHintBoring);
+                            isFallbackLineSpacingForBoringLayout(), mMinimumFontMetrics,
+                            mHintBoring);
                     if (hintBoring != null) {
                         mHintBoring = hintBoring;
                     }
@@ -11254,7 +11315,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 .setTextDirection(getTextDirectionHeuristic())
                 .setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
                         mLineBreakStyle, mLineBreakWordStyle))
-                .setUseBoundsForWidth(mUseBoundsForWidth);
+                .setUseBoundsForWidth(mUseBoundsForWidth)
+                .setMinimumFontMetrics(mMinimumFontMetrics);
 
         final StaticLayout layout = layoutBuilder.build();
 
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 9fde0fd6e6ab69f72aa4de2e20a154c68dc368e0..4eaa01309ab129a59fc14ec5f7a82f206661fce3 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -2113,6 +2113,31 @@ public class Paint {
          * The recommended additional space to add between lines of text.
          */
         public float   leading;
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || !(o instanceof FontMetrics)) return false;
+            FontMetrics that = (FontMetrics) o;
+            return that.top == top && that.ascent == ascent && that.descent == descent
+                    && that.bottom == bottom && that.leading == leading;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(top, ascent, descent, bottom, leading);
+        }
+
+        @Override
+        public String toString() {
+            return "FontMetrics{"
+                    + "top=" + top
+                    + ", ascent=" + ascent
+                    + ", descent=" + descent
+                    + ", bottom=" + bottom
+                    + ", leading=" + leading
+                    + '}';
+        }
     }
 
     /**
@@ -2309,6 +2334,33 @@ public class Paint {
          */
         public int   leading;
 
+        /**
+         * Set values from {@link FontMetricsInt}.
+         * @param fontMetricsInt a font metrics.
+         */
+        @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE)
+        public void set(@NonNull FontMetricsInt fontMetricsInt) {
+            top = fontMetricsInt.top;
+            ascent = fontMetricsInt.ascent;
+            descent = fontMetricsInt.descent;
+            bottom = fontMetricsInt.bottom;
+            leading = fontMetricsInt.leading;
+        }
+
+        /**
+         * Set values from {@link FontMetrics} with rounding accordingly.
+         * @param fontMetrics a font metrics.
+         */
+        @FlaggedApi(FLAG_FIX_LINE_HEIGHT_FOR_LOCALE)
+        public void set(@NonNull FontMetrics fontMetrics) {
+            // See GraphicsJNI::set_metrics_int method for consistency.
+            top = (int) Math.floor(fontMetrics.top);
+            ascent = Math.round(fontMetrics.ascent);
+            descent = Math.round(fontMetrics.descent);
+            bottom = (int) Math.ceil(fontMetrics.bottom);
+            leading = Math.round(fontMetrics.leading);
+        }
+
         @Override public String toString() {
             return "FontMetricsInt: top=" + top + " ascent=" + ascent +
                     " descent=" + descent + " bottom=" + bottom +