Merge "Revert "Reorganize MeasuredText API""
diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java b/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java
similarity index 73%
rename from apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
rename to apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java
index 73e1724..fc6302e 100644
--- a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
+++ b/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java
@@ -45,7 +45,7 @@
 
 @LargeTest
 @RunWith(AndroidJUnit4.class)
-public class PrecomputedTextMemoryUsageTest {
+public class MeasuredTextMemoryUsageTest {
     private static final int WORD_LENGTH = 9;  // Random word has 9 characters.
     private static final boolean NO_STYLE_TEXT = false;
 
@@ -53,7 +53,7 @@
 
     private static int TRIAL_COUNT = 100;
 
-    public PrecomputedTextMemoryUsageTest() {}
+    public MeasuredTextMemoryUsageTest() {}
 
     private TextPerfUtils mTextUtil = new TextPerfUtils();
 
@@ -77,16 +77,13 @@
     @Test
     public void testMemoryUsage_NoHyphenation() {
         int[] memories = new int[TRIAL_COUNT];
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+        // Report median of randomly generated MeasuredText.
+        for (int i = 0; i < TRIAL_COUNT; ++i) {
+            memories[i] = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                 .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                 .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
-                .build();
-
-        // Report median of randomly generated PrecomputedText.
-        for (int i = 0; i < TRIAL_COUNT; ++i) {
-            memories[i] = PrecomputedText.create(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), param)
-                .getMemoryUsage();
+                .build().getMemoryUsage();
         }
         reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation");
     }
@@ -94,16 +91,13 @@
     @Test
     public void testMemoryUsage_Hyphenation() {
         int[] memories = new int[TRIAL_COUNT];
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+        // Report median of randomly generated MeasuredText.
+        for (int i = 0; i < TRIAL_COUNT; ++i) {
+            memories[i] = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                 .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
                 .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
-                .build();
-
-        // Report median of randomly generated PrecomputedText.
-        for (int i = 0; i < TRIAL_COUNT; ++i) {
-            memories[i] = PrecomputedText.create(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), param)
-                .getMemoryUsage();
+                .build().getMemoryUsage();
         }
         reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation");
     }
@@ -111,16 +105,13 @@
     @Test
     public void testMemoryUsage_NoHyphenation_WidthOnly() {
         int[] memories = new int[TRIAL_COUNT];
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+        // Report median of randomly generated MeasuredText.
+        for (int i = 0; i < TRIAL_COUNT; ++i) {
+            memories[i] = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                 .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                 .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
-                .build();
-
-        // Report median of randomly generated PrecomputedText.
-        for (int i = 0; i < TRIAL_COUNT; ++i) {
-            CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
-            memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length())
-                .getMemoryUsage();
+                .build(false /* width only */).getMemoryUsage();
         }
         reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation_WidthOnly");
     }
@@ -128,16 +119,13 @@
     @Test
     public void testMemoryUsage_Hyphenatation_WidthOnly() {
         int[] memories = new int[TRIAL_COUNT];
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+        // Report median of randomly generated MeasuredText.
+        for (int i = 0; i < TRIAL_COUNT; ++i) {
+            memories[i] = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                 .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
                 .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
-                .build();
-
-        // Report median of randomly generated PrecomputedText.
-        for (int i = 0; i < TRIAL_COUNT; ++i) {
-            CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
-            memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length())
-                .getMemoryUsage();
+                .build(false /* width only */).getMemoryUsage();
         }
         reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation_WidthOnly");
     }
diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java b/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java
similarity index 66%
rename from apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
rename to apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java
index 1cd0ae1..98f2bd5 100644
--- a/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java
@@ -42,7 +42,7 @@
 
 @LargeTest
 @RunWith(AndroidJUnit4.class)
-public class PrecomputedTextPerfTest {
+public class MeasuredTextPerfTest {
     private static final int WORD_LENGTH = 9;  // Random word has 9 characters.
     private static final int WORDS_IN_LINE = 8;  // Roughly, 8 words in a line.
     private static final boolean NO_STYLE_TEXT = false;
@@ -51,7 +51,7 @@
     private static TextPaint PAINT = new TextPaint();
     private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
 
-    public PrecomputedTextPerfTest() {}
+    public MeasuredTextPerfTest() {}
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -66,136 +66,120 @@
     @Test
     public void testCreate_NoStyled_Hyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
-                .build();
-
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             state.resumeTiming();
 
-            PrecomputedText.create(text, param);
+            new MeasuredText.Builder(text, PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                    .build(true /* do full layout */);
         }
     }
 
     @Test
     public void testCreate_NoStyled_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
-                .build();
-
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             state.resumeTiming();
 
-            PrecomputedText.create(text, param);
+            new MeasuredText.Builder(text, PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .build(true /* do full layout */);
         }
     }
 
     @Test
     public void testCreate_NoStyled_Hyphenation_WidthOnly() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
-                .build();
-
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             state.resumeTiming();
 
-            PrecomputedText.create(text, param);
+            new MeasuredText.Builder(text, PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                    .build(false /* width only */);
         }
     }
 
     @Test
     public void testCreate_NoStyled_NoHyphenation_WidthOnly() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
-                .build();
-
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             state.resumeTiming();
 
-            PrecomputedText.create(text, param);
+            new MeasuredText.Builder(text, PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .build(false /* width only */);
         }
     }
 
     @Test
     public void testCreate_Styled_Hyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
-                .build();
-
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             state.resumeTiming();
 
-            PrecomputedText.create(text, param);
+            new MeasuredText.Builder(text, PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                    .build(true /* do full layout */);
         }
     }
 
     @Test
     public void testCreate_Styled_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
-                .build();
-
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             state.resumeTiming();
 
-            PrecomputedText.create(text, param);
+            new MeasuredText.Builder(text, PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .build(true /* do full layout */);
         }
     }
 
     @Test
     public void testCreate_Styled_Hyphenation_WidthOnly() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
-                .build();
-
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             state.resumeTiming();
 
-            PrecomputedText.create(text, param);
+            new MeasuredText.Builder(text, PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                    .build(false /* width only */);
         }
     }
 
     @Test
     public void testCreate_Styled_NoHyphenation_WidthOnly() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
-                .build();
-
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             state.resumeTiming();
 
-            PrecomputedText.create(text, param);
+            new MeasuredText.Builder(text, PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .build(false /* width only */);
         }
     }
 }
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index 8823af1..231aaf2 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -63,18 +63,6 @@
         mTextUtil.resetRandom(0 /* seed */);
     }
 
-    private PrecomputedText makeMeasured(CharSequence text, TextPaint paint) {
-        PrecomputedText.Params param = new PrecomputedText.Params.Builder(paint).build();
-        return PrecomputedText.create(text, param);
-    }
-
-    private PrecomputedText makeMeasured(CharSequence text, TextPaint paint, int strategy,
-                                      int frequency) {
-        PrecomputedText.Params param = new PrecomputedText.Params.Builder(paint)
-                .setHyphenationFrequency(frequency).setBreakStrategy(strategy).build();
-        return PrecomputedText.create(text, param);
-    }
-
     @Test
     public void testCreate_FixedText_NoStyle_Greedy_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
@@ -163,16 +151,18 @@
     }
 
     @Test
-    public void testCreate_PrecomputedText_NoStyled_Greedy_NoHyphenation() {
+    public void testCreate_MeasuredText_NoStyled_Greedy_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
-                    Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .build();
             state.resumeTiming();
 
-            StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH)
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                     .build();
@@ -180,16 +170,18 @@
     }
 
     @Test
-    public void testCreate_PrecomputedText_NoStyled_Greedy_Hyphenation() {
+    public void testCreate_MeasuredText_NoStyled_Greedy_Hyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
-                    Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NORMAL);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                    .build();
             state.resumeTiming();
 
-            StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH)
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                     .build();
@@ -197,16 +189,18 @@
     }
 
     @Test
-    public void testCreate_PrecomputedText_NoStyled_Balanced_NoHyphenation() {
+    public void testCreate_MeasuredText_NoStyled_Balanced_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
-                    Layout.BREAK_STRATEGY_BALANCED, Layout.HYPHENATION_FREQUENCY_NONE);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .build();
             state.resumeTiming();
 
-            StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH)
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
                     .build();
@@ -214,16 +208,18 @@
     }
 
     @Test
-    public void testCreate_PrecomputedText_NoStyled_Balanced_Hyphenation() {
+    public void testCreate_MeasuredText_NoStyled_Balanced_Hyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
-                    Layout.BREAK_STRATEGY_BALANCED, Layout.HYPHENATION_FREQUENCY_NORMAL);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                    .build();
             state.resumeTiming();
 
-            StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH)
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
                     .build();
@@ -231,16 +227,18 @@
     }
 
     @Test
-    public void testCreate_PrecomputedText_Styled_Greedy_NoHyphenation() {
+    public void testCreate_MeasuredText_Styled_Greedy_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT,
-                    Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .build();
             state.resumeTiming();
 
-            StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH)
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                     .build();
@@ -330,16 +328,15 @@
     }
 
     @Test
-    public void testDraw_PrecomputedText_Styled() {
+    public void testDraw_MeasuredText_Styled() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
             final StaticLayout layout =
-                    StaticLayout.Builder.obtain(
-                            text, 0, text.getText().length(), PAINT, TEXT_WIDTH).build();
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
             state.resumeTiming();
 
@@ -348,16 +345,15 @@
     }
 
     @Test
-    public void testDraw_PrecomputedText_NoStyled() {
+    public void testDraw_MeasuredText_NoStyled() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
             final StaticLayout layout =
-                    StaticLayout.Builder.obtain(
-                            text, 0, text.getText().length(), PAINT, TEXT_WIDTH).build();
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
             state.resumeTiming();
 
@@ -366,16 +362,15 @@
     }
 
     @Test
-    public void testDraw_PrecomputedText_Styled_WithoutCache() {
+    public void testDraw_MeasuredText_Styled_WithoutCache() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
             final StaticLayout layout =
-                    StaticLayout.Builder.obtain(
-                            text, 0, text.getText().length(), PAINT, TEXT_WIDTH).build();
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
@@ -385,16 +380,15 @@
     }
 
     @Test
-    public void testDraw_PrecomputedText_NoStyled_WithoutCache() {
+    public void testDraw_MeasuredText_NoStyled_WithoutCache() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
             final StaticLayout layout =
-                    StaticLayout.Builder.obtain(
-                            text, 0, text.getText().length(), PAINT, TEXT_WIDTH).build();
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
diff --git a/api/current.txt b/api/current.txt
index 53f503a..78d3d74 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13839,7 +13839,6 @@
     method public int breakText(java.lang.String, boolean, float, float[]);
     method public void clearShadowLayer();
     method public float descent();
-    method public boolean equalsForTextMeasurement(android.graphics.Paint);
     method public int getAlpha();
     method public int getColor();
     method public android.graphics.ColorFilter getColorFilter();
@@ -43229,6 +43228,36 @@
     method public boolean isAllowed(char);
   }
 
+  public class MeasuredText implements android.text.Spanned {
+    method public char charAt(int);
+    method public int getBreakStrategy();
+    method public int getEnd();
+    method public int getHyphenationFrequency();
+    method public android.text.TextPaint getPaint();
+    method public int getParagraphCount();
+    method public int getParagraphEnd(int);
+    method public int getParagraphStart(int);
+    method public int getSpanEnd(java.lang.Object);
+    method public int getSpanFlags(java.lang.Object);
+    method public int getSpanStart(java.lang.Object);
+    method public <T> T[] getSpans(int, int, java.lang.Class<T>);
+    method public int getStart();
+    method public java.lang.CharSequence getText();
+    method public android.text.TextDirectionHeuristic getTextDir();
+    method public int length();
+    method public int nextSpanTransition(int, int, java.lang.Class);
+    method public java.lang.CharSequence subSequence(int, int);
+  }
+
+  public static final class MeasuredText.Builder {
+    ctor public MeasuredText.Builder(java.lang.CharSequence, android.text.TextPaint);
+    method public android.text.MeasuredText build();
+    method public android.text.MeasuredText.Builder setBreakStrategy(int);
+    method public android.text.MeasuredText.Builder setHyphenationFrequency(int);
+    method public android.text.MeasuredText.Builder setRange(int, int);
+    method public android.text.MeasuredText.Builder setTextDirection(android.text.TextDirectionHeuristic);
+  }
+
   public abstract interface NoCopySpan {
   }
 
@@ -43240,31 +43269,6 @@
     method public abstract int getSpanTypeId();
   }
 
-  public class PrecomputedText {
-    method public static android.text.PrecomputedText create(java.lang.CharSequence, android.text.PrecomputedText.Params);
-    method public int getParagraphCount();
-    method public int getParagraphEnd(int);
-    method public int getParagraphStart(int);
-    method public android.text.PrecomputedText.Params getParams();
-    method public java.lang.CharSequence getText();
-  }
-
-  public static class PrecomputedText.Params {
-    method public int getBreakStrategy();
-    method public int getHyphenationFrequency();
-    method public android.text.TextDirectionHeuristic getTextDirection();
-    method public android.text.TextPaint getTextPaint();
-    method public boolean sameTextMetrics(android.text.PrecomputedText.Params);
-  }
-
-  public static class PrecomputedText.Params.Builder {
-    ctor public PrecomputedText.Params.Builder(android.text.TextPaint);
-    method public android.text.PrecomputedText.Params build();
-    method public android.text.PrecomputedText.Params.Builder setBreakStrategy(int);
-    method public android.text.PrecomputedText.Params.Builder setHyphenationFrequency(int);
-    method public android.text.PrecomputedText.Params.Builder setTextDirection(android.text.TextDirectionHeuristic);
-  }
-
   public class Selection {
     method public static boolean extendDown(android.text.Spannable, android.text.Layout);
     method public static boolean extendLeft(android.text.Spannable, android.text.Layout);
@@ -43396,7 +43400,6 @@
 
   public static final class StaticLayout.Builder {
     method public android.text.StaticLayout build();
-    method public static android.text.StaticLayout.Builder obtain(android.text.PrecomputedText, int, int, android.text.TextPaint, int);
     method public static android.text.StaticLayout.Builder obtain(java.lang.CharSequence, int, int, android.text.TextPaint, int);
     method public android.text.StaticLayout.Builder setAlignment(android.text.Layout.Alignment);
     method public android.text.StaticLayout.Builder setBreakStrategy(int);
@@ -53648,7 +53651,6 @@
     method public final android.content.res.ColorStateList getTextColors();
     method public java.util.Locale getTextLocale();
     method public android.os.LocaleList getTextLocales();
-    method public android.text.PrecomputedText.Params getTextMetricsParams();
     method public float getTextScaleX();
     method public float getTextSize();
     method public int getTotalPaddingBottom();
@@ -53754,8 +53756,6 @@
     method public final void setMovementMethod(android.text.method.MovementMethod);
     method public void setOnEditorActionListener(android.widget.TextView.OnEditorActionListener);
     method public void setPaintFlags(int);
-    method public void setPrecomputedTextAndParams(android.text.PrecomputedText);
-    method public void setPrecomputedTextOrThrow(android.text.PrecomputedText);
     method public void setPrivateImeOptions(java.lang.String);
     method public void setRawInputType(int);
     method public void setScroller(android.widget.Scroller);
@@ -53780,7 +53780,6 @@
     method public final void setTextKeepState(java.lang.CharSequence, android.widget.TextView.BufferType);
     method public void setTextLocale(java.util.Locale);
     method public void setTextLocales(android.os.LocaleList);
-    method public void setTextMetricsParams(android.text.PrecomputedText.Params);
     method public void setTextScaleX(float);
     method public void setTextSize(float);
     method public void setTextSize(int, float);
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index dbe4157..6fa5312 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -322,12 +322,6 @@
      */
     public static Metrics isBoring(CharSequence text, TextPaint paint,
             TextDirectionHeuristic textDir, Metrics metrics) {
-        return isBoring(text, null /* precomputed */, paint, textDir, metrics);
-    }
-
-    /** @hide */
-    public static Metrics isBoring(CharSequence text, PrecomputedText precomputed, TextPaint paint,
-            TextDirectionHeuristic textDir, Metrics metrics) {
         final int textLength = text.length();
         if (hasAnyInterestingChars(text, textLength)) {
            return null;  // There are some interesting characters. Not boring.
@@ -350,17 +344,18 @@
             fm.reset();
         }
 
-        if (precomputed != null) {
+        TextLine line = TextLine.obtain();
+        line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
+                Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
+        if (text instanceof MeasuredText) {
+            MeasuredText mt = (MeasuredText) text;
             // Reaching here means there is only one paragraph.
-            MeasuredParagraph mp = precomputed.getMeasuredParagraph(0);
+            MeasuredParagraph mp = mt.getMeasuredParagraph(0);
             fm.width = (int) Math.ceil(mp.getWidth(0, mp.getTextLength()));
         } else {
-            TextLine line = TextLine.obtain();
-            line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
-                    Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
             fm.width = (int) Math.ceil(line.metrics(fm));
-            TextLine.recycle(line);
         }
+        TextLine.recycle(line);
 
         return fm;
     }
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 10444f0..18431ca 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -363,7 +363,7 @@
                          @JustificationMode int justificationMode,
                          @Nullable TextUtils.TruncateAt ellipsize,
                          @IntRange(from = 0) int ellipsizedWidth) {
-        super(createEllipsizer(ellipsize, display), null /* precomputed */,
+        super(createEllipsizer(ellipsize, display),
               paint, width, align, textDir, spacingmult, spacingadd);
 
         final Builder b = Builder.obtain(base, paint, width)
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index d5d3590..aa97b2a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -245,13 +245,6 @@
     protected Layout(CharSequence text, TextPaint paint,
                      int width, Alignment align, TextDirectionHeuristic textDir,
                      float spacingMult, float spacingAdd) {
-        this(text, null /* precomputed */, paint, width, align, textDir, spacingMult, spacingAdd);
-    }
-
-    /** @hide */
-    protected Layout(CharSequence text, PrecomputedText precomputed, TextPaint paint,
-                     int width, Alignment align, TextDirectionHeuristic textDir,
-                     float spacingMult, float spacingAdd) {
 
         if (width < 0)
             throw new IllegalArgumentException("Layout: " + width + " < 0");
@@ -266,7 +259,6 @@
         }
 
         mText = text;
-        mPrecomputed = precomputed;
         mPaint = paint;
         mWidth = width;
         mAlignment = align;
@@ -570,7 +562,7 @@
                 // XXX: assumes there's nothing additional to be done
                 canvas.drawText(buf, start, end, x, lbaseline, paint);
             } else {
-                tl.set(paint, buf, mPrecomputed, start, end, dir, directions, hasTab, tabStops);
+                tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops);
                 if (justify) {
                     tl.justify(right - left - indentWidth);
                 }
@@ -2272,7 +2264,6 @@
     }
 
     private CharSequence mText;
-    private PrecomputedText mPrecomputed;
     private TextPaint mPaint;
     private TextPaint mWorkPaint = new TextPaint();
     private int mWidth;
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
new file mode 100644
index 0000000..bb7a9e0
--- /dev/null
+++ b/core/java/android/text/MeasuredText.java
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.util.IntArray;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+
+/**
+ * A text which has already been measured.
+ */
+public class MeasuredText implements Spanned {
+    private static final char LINE_FEED = '\n';
+
+    // The original text.
+    private final @NonNull CharSequence mText;
+
+    // The inclusive start offset of the measuring target.
+    private final @IntRange(from = 0) int mStart;
+
+    // The exclusive end offset of the measuring target.
+    private final @IntRange(from = 0) int mEnd;
+
+    // The TextPaint used for measurement.
+    private final @NonNull TextPaint mPaint;
+
+    // The requested text direction.
+    private final @NonNull TextDirectionHeuristic mTextDir;
+
+    // The measured paragraph texts.
+    private final @NonNull MeasuredParagraph[] mMeasuredParagraphs;
+
+    // The sorted paragraph end offsets.
+    private final @NonNull int[] mParagraphBreakPoints;
+
+    // The break strategy for this measured text.
+    private final @Layout.BreakStrategy int mBreakStrategy;
+
+    // The hyphenation frequency for this measured text.
+    private final @Layout.HyphenationFrequency int mHyphenationFrequency;
+
+    /**
+     * A Builder for MeasuredText
+     */
+    public static final class Builder {
+        // Mandatory parameters.
+        private final @NonNull CharSequence mText;
+        private final @NonNull TextPaint mPaint;
+
+        // Members to be updated by setters.
+        private @IntRange(from = 0) int mStart;
+        private @IntRange(from = 0) int mEnd;
+        private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
+        private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;
+        private @Layout.HyphenationFrequency int mHyphenationFrequency =
+                Layout.HYPHENATION_FREQUENCY_NORMAL;
+
+
+        /**
+         * Builder constructor
+         *
+         * @param text The text to be measured.
+         * @param paint The paint to be used for drawing.
+         */
+        public Builder(@NonNull CharSequence text, @NonNull TextPaint paint) {
+            Preconditions.checkNotNull(text);
+            Preconditions.checkNotNull(paint);
+
+            mText = text;
+            mPaint = paint;
+            mStart = 0;
+            mEnd = text.length();
+        }
+
+        /**
+         * Set the range of measuring target.
+         *
+         * @param start The measuring target start offset in the text.
+         * @param end The measuring target end offset in the text.
+         */
+        public @NonNull Builder setRange(@IntRange(from = 0) int start,
+                                         @IntRange(from = 0) int end) {
+            Preconditions.checkArgumentInRange(start, 0, mText.length(), "start");
+            Preconditions.checkArgumentInRange(end, 0, mText.length(), "end");
+            Preconditions.checkArgument(start <= end, "The range is reversed.");
+
+            mStart = start;
+            mEnd = end;
+            return this;
+        }
+
+        /**
+         * Set the text direction heuristic
+         *
+         * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
+         *
+         * @param textDir The text direction heuristic for resolving bidi behavior.
+         * @return this builder, useful for chaining.
+         */
+        public @NonNull Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) {
+            Preconditions.checkNotNull(textDir);
+            mTextDir = textDir;
+            return this;
+        }
+
+        /**
+         * Set the break strategy
+         *
+         * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}.
+         *
+         * @param breakStrategy The break strategy.
+         * @return this builder, useful for chaining.
+         */
+        public @NonNull Builder setBreakStrategy(@Layout.BreakStrategy int breakStrategy) {
+            mBreakStrategy = breakStrategy;
+            return this;
+        }
+
+        /**
+         * Set the hyphenation frequency
+         *
+         * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}.
+         *
+         * @param hyphenationFrequency The hyphenation frequency.
+         * @return this builder, useful for chaining.
+         */
+        public @NonNull Builder setHyphenationFrequency(
+                @Layout.HyphenationFrequency int hyphenationFrequency) {
+            mHyphenationFrequency = hyphenationFrequency;
+            return this;
+        }
+
+        /**
+         * Build the measured text
+         *
+         * @return the measured text.
+         */
+        public @NonNull MeasuredText build() {
+            return build(true /* build full layout result */);
+        }
+
+        /** @hide */
+        public @NonNull MeasuredText build(boolean computeLayout) {
+            final boolean needHyphenation = mBreakStrategy != Layout.BREAK_STRATEGY_SIMPLE
+                    && mHyphenationFrequency != Layout.HYPHENATION_FREQUENCY_NONE;
+
+            final IntArray paragraphEnds = new IntArray();
+            final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
+
+            int paraEnd = 0;
+            for (int paraStart = mStart; paraStart < mEnd; paraStart = paraEnd) {
+                paraEnd = TextUtils.indexOf(mText, LINE_FEED, paraStart, mEnd);
+                if (paraEnd < 0) {
+                    // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph
+                    // end.
+                    paraEnd = mEnd;
+                } else {
+                    paraEnd++;  // Includes LINE_FEED(U+000A) to the prev paragraph.
+                }
+
+                paragraphEnds.add(paraEnd);
+                measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
+                        mPaint, mText, paraStart, paraEnd, mTextDir, needHyphenation,
+                        computeLayout, null /* no recycle */));
+            }
+
+            return new MeasuredText(mText, mStart, mEnd, mPaint, mTextDir, mBreakStrategy,
+                                    mHyphenationFrequency, measuredTexts.toArray(
+                                            new MeasuredParagraph[measuredTexts.size()]),
+                                    paragraphEnds.toArray());
+        }
+    };
+
+    // Use MeasuredText.Builder instead.
+    private MeasuredText(@NonNull CharSequence text,
+                         @IntRange(from = 0) int start,
+                         @IntRange(from = 0) int end,
+                         @NonNull TextPaint paint,
+                         @NonNull TextDirectionHeuristic textDir,
+                         @Layout.BreakStrategy int breakStrategy,
+                         @Layout.HyphenationFrequency int frequency,
+                         @NonNull MeasuredParagraph[] measuredTexts,
+                         @NonNull int[] paragraphBreakPoints) {
+        mText = text;
+        mStart = start;
+        mEnd = end;
+        // Copy the paint so that we can keep the reference of typeface in native layout result.
+        mPaint = new TextPaint(paint);
+        mMeasuredParagraphs = measuredTexts;
+        mParagraphBreakPoints = paragraphBreakPoints;
+        mTextDir = textDir;
+        mBreakStrategy = breakStrategy;
+        mHyphenationFrequency = frequency;
+    }
+
+    /**
+     * Return the underlying text.
+     */
+    public @NonNull CharSequence getText() {
+        return mText;
+    }
+
+    /**
+     * Returns the inclusive start offset of measured region.
+     */
+    public @IntRange(from = 0) int getStart() {
+        return mStart;
+    }
+
+    /**
+     * Returns the exclusive end offset of measured region.
+     */
+    public @IntRange(from = 0) int getEnd() {
+        return mEnd;
+    }
+
+    /**
+     * Returns the text direction associated with char sequence.
+     */
+    public @NonNull TextDirectionHeuristic getTextDir() {
+        return mTextDir;
+    }
+
+    /**
+     * Returns the paint used to measure this text.
+     */
+    public @NonNull TextPaint getPaint() {
+        return mPaint;
+    }
+
+    /**
+     * Returns the length of the paragraph of this text.
+     */
+    public @IntRange(from = 0) int getParagraphCount() {
+        return mParagraphBreakPoints.length;
+    }
+
+    /**
+     * Returns the paragraph start offset of the text.
+     */
+    public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
+        Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
+        return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
+    }
+
+    /**
+     * Returns the paragraph end offset of the text.
+     */
+    public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
+        Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
+        return mParagraphBreakPoints[paraIndex];
+    }
+
+    /** @hide */
+    public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) {
+        return mMeasuredParagraphs[paraIndex];
+    }
+
+    /**
+     * Returns the break strategy for this text.
+     */
+    public @Layout.BreakStrategy int getBreakStrategy() {
+        return mBreakStrategy;
+    }
+
+    /**
+     * Returns the hyphenation frequency for this text.
+     */
+    public @Layout.HyphenationFrequency int getHyphenationFrequency() {
+        return mHyphenationFrequency;
+    }
+
+    /**
+     * Returns true if the given TextPaint gives the same result of text layout for this text.
+     * @hide
+     */
+    public boolean canUseMeasuredResult(@NonNull TextPaint paint) {
+        return mPaint.getTextSize() == paint.getTextSize()
+            && mPaint.getTextSkewX() == paint.getTextSkewX()
+            && mPaint.getTextScaleX() == paint.getTextScaleX()
+            && mPaint.getLetterSpacing() == paint.getLetterSpacing()
+            && mPaint.getWordSpacing() == paint.getWordSpacing()
+            && mPaint.getFlags() == paint.getFlags()  // Maybe not all flag affects text layout.
+            && mPaint.getTextLocales() == paint.getTextLocales()  // need to be equals?
+            && mPaint.getFontVariationSettings() == paint.getFontVariationSettings()
+            && mPaint.getTypeface() == paint.getTypeface()
+            && TextUtils.equals(mPaint.getFontFeatureSettings(), paint.getFontFeatureSettings());
+    }
+
+    /** @hide */
+    public int findParaIndex(@IntRange(from = 0) int pos) {
+        // TODO: Maybe good to remove paragraph concept from MeasuredText and add substring layout
+        //       support to StaticLayout.
+        for (int i = 0; i < mParagraphBreakPoints.length; ++i) {
+            if (pos < mParagraphBreakPoints[i]) {
+                return i;
+            }
+        }
+        throw new IndexOutOfBoundsException(
+            "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1]
+            + ", gave " + pos);
+    }
+
+    /** @hide */
+    public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
+        final int paraIndex = findParaIndex(start);
+        final int paraStart = getParagraphStart(paraIndex);
+        final int paraEnd = getParagraphEnd(paraIndex);
+        if (start < paraStart || paraEnd < end) {
+            throw new RuntimeException("Cannot measured across the paragraph:"
+                + "para: (" + paraStart + ", " + paraEnd + "), "
+                + "request: (" + start + ", " + end + ")");
+        }
+        return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart);
+    }
+
+    /**
+     * Returns the size of native MeasuredText memory usage
+     *
+     * Note that this may not be aculate. Must be used only for testing purposes.
+     * @hide
+     */
+    public int getMemoryUsage() {
+        int r = 0;
+        for (int i = 0; i < getParagraphCount(); ++i) {
+            r += getMeasuredParagraph(i).getMemoryUsage();
+        }
+        return r;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // Spanned overrides
+    //
+    // Just proxy for underlying mText if appropriate.
+
+    @Override
+    public <T> T[] getSpans(int start, int end, Class<T> type) {
+        if (mText instanceof Spanned) {
+            return ((Spanned) mText).getSpans(start, end, type);
+        } else {
+            return ArrayUtils.emptyArray(type);
+        }
+    }
+
+    @Override
+    public int getSpanStart(Object tag) {
+        if (mText instanceof Spanned) {
+            return ((Spanned) mText).getSpanStart(tag);
+        } else {
+            return -1;
+        }
+    }
+
+    @Override
+    public int getSpanEnd(Object tag) {
+        if (mText instanceof Spanned) {
+            return ((Spanned) mText).getSpanEnd(tag);
+        } else {
+            return -1;
+        }
+    }
+
+    @Override
+    public int getSpanFlags(Object tag) {
+        if (mText instanceof Spanned) {
+            return ((Spanned) mText).getSpanFlags(tag);
+        } else {
+            return 0;
+        }
+    }
+
+    @Override
+    public int nextSpanTransition(int start, int limit, Class type) {
+        if (mText instanceof Spanned) {
+            return ((Spanned) mText).nextSpanTransition(start, limit, type);
+        } else {
+            return mText.length();
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // CharSequence overrides.
+    //
+    // Just proxy for underlying mText.
+
+    @Override
+    public int length() {
+        return mText.length();
+    }
+
+    @Override
+    public char charAt(int index) {
+        // TODO: Should this be index + mStart ?
+        return mText.charAt(index);
+    }
+
+    @Override
+    public CharSequence subSequence(int start, int end) {
+        // TODO: return MeasuredText.
+        // TODO: Should this be index + mStart, end + mStart ?
+        return mText.subSequence(start, end);
+    }
+
+    @Override
+    public String toString() {
+        return mText.toString();
+    }
+}
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
deleted file mode 100644
index 39fc2bd..0000000
--- a/core/java/android/text/PrecomputedText.java
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.text;
-
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.util.IntArray;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.ArrayList;
-
-/**
- * A text which has the character metrics data
- *
- * This text holds a part of the text layout result. You can accelerate
- * {@link android.widget.TextView} or {@link StaticLayout} by using this text.
- *
- * <pre>
- * Example of background measurement.
- * <code>
- *  void asyncSetText(final TextView textView, final String expensiveLongString, Handler handler) {
- *      final PrecomputedText.Params params = textView.getTextParams();
- *      handler.post(() -> {
- *          final PrecomputedText precomputedText
- *                  = PrecomputedText.create(expensiveLongString, params);
- *          textView.post(() -> {
- *              textView.setPrecomputedTextOrThrow(precomputedText);
- *          });
- *      });
- *  }
- * </code>
- * </pre>
- *
- * Note that the {@link PrecomputedText} created from different parameters of the target
- * {@link android.widget.TextView} will be rejected internally and compute the text layout again
- * with the current {@link android.widget.TextView} parameters.
- */
-public class PrecomputedText {
-    private static final char LINE_FEED = '\n';
-
-    /**
-     * The information required for building {@link PrecomputedText}.
-     *
-     * Contains information required for precomputing text measurement metadata, so it can be done
-     * in isolation of a {@link android.widget.TextView} or {@link StaticLayout}, when final layout
-     * constraints are not known.
-     */
-    public static class Params {
-        // The TextPaint used for measurement.
-        private final @NonNull TextPaint mPaint;
-
-        // The requested text direction.
-        private final @NonNull TextDirectionHeuristic mTextDir;
-
-        // The break strategy for this measured text.
-        private final @Layout.BreakStrategy int mBreakStrategy;
-
-        // The hyphenation frequency for this measured text.
-        private final @Layout.HyphenationFrequency int mHyphenationFrequency;
-
-        /**
-         * A builder for creating {@link Params}.
-         */
-        public static class Builder {
-            // The TextPaint used for measurement.
-            private final @NonNull TextPaint mPaint;
-
-            // The requested text direction.
-            private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
-
-            // The break strategy for this measured text.
-            private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;
-
-            // The hyphenation frequency for this measured text.
-            private @Layout.HyphenationFrequency int mHyphenationFrequency =
-                    Layout.HYPHENATION_FREQUENCY_NORMAL;
-
-            /**
-             * Builder constructor
-             *
-             * @param paint The paint to be used for drawing
-             */
-            public Builder(@NonNull TextPaint paint) {
-                mPaint = paint;
-            }
-
-            /**
-             * Set the line break strategy
-             *
-             * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}.
-             *
-             * @param strategy The break strategy
-             * @return this builder, useful for chaining
-             */
-            public Builder setBreakStrategy(@Layout.BreakStrategy int strategy) {
-                mBreakStrategy = strategy;
-                return this;
-            }
-
-            /**
-             * Set the hyphenation frequency
-             *
-             * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}.
-             *
-             * @param frequency The hyphenation frequency
-             * @return this builder, useful for chaining
-             */
-            public Builder setHyphenationFrequency(@Layout.HyphenationFrequency int frequency) {
-                mHyphenationFrequency = frequency;
-                return this;
-            }
-
-            /**
-             * Set the text direction heuristic
-             *
-             * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
-             *
-             * @param textDir The text direction heuristic for resolving bidi behavior
-             * @return this builder, useful for chaining
-             */
-            public Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) {
-                mTextDir = textDir;
-                return this;
-            }
-
-            /**
-             * Build the {@link Params}
-             *
-             * @return the layout parameter
-             */
-            public @NonNull Params build() {
-                return new Params(mPaint, mTextDir, mBreakStrategy, mHyphenationFrequency);
-            }
-        }
-
-        // Use Builder instead.
-        /** @hide */
-        public Params(@NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir,
-                @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) {
-            mPaint = paint;
-            mTextDir = textDir;
-            mBreakStrategy = strategy;
-            mHyphenationFrequency = frequency;
-        }
-
-        /**
-         * Returns the {@link TextPaint} for this text
-         *
-         * @return A {@link TextPaint}
-         */
-        public @NonNull TextPaint getTextPaint() {
-            return mPaint;
-        }
-
-        /**
-         * Returns the {@link TextDirectionHeuristic} for this text
-         *
-         * @return A {@link TextDirectionHeuristic}
-         */
-        public @NonNull TextDirectionHeuristic getTextDirection() {
-            return mTextDir;
-        }
-
-        /**
-         * Returns the break strategy for this text
-         *
-         * @return A line break strategy
-         */
-        public @Layout.BreakStrategy int getBreakStrategy() {
-            return mBreakStrategy;
-        }
-
-        /**
-         * Returns the hyphenation frequency for this text
-         *
-         * @return A hyphenation frequency
-         */
-        public @Layout.HyphenationFrequency int getHyphenationFrequency() {
-            return mHyphenationFrequency;
-        }
-
-        private boolean sameTextMetricsInternal(@NonNull TextPaint paint,
-                @NonNull TextDirectionHeuristic textDir, @Layout.BreakStrategy int strategy,
-                @Layout.HyphenationFrequency int frequency) {
-            return mTextDir == textDir
-                && mBreakStrategy == strategy
-                && mHyphenationFrequency == frequency
-                && mPaint.equalsForTextMeasurement(paint);
-        }
-
-        /**
-         * Check if the same text layout.
-         *
-         * @return true if this and the given param result in the same text layout
-         */
-        public boolean sameTextMetrics(@NonNull Params param) {
-            return sameTextMetricsInternal(param.mPaint, param.mTextDir, param.mBreakStrategy,
-                    param.mHyphenationFrequency);
-        }
-    };
-
-    // The original text.
-    private final @NonNull CharSequence mText;
-
-    // The inclusive start offset of the measuring target.
-    private final @IntRange(from = 0) int mStart;
-
-    // The exclusive end offset of the measuring target.
-    private final @IntRange(from = 0) int mEnd;
-
-    private final @NonNull Params mParams;
-
-    // The measured paragraph texts.
-    private final @NonNull MeasuredParagraph[] mMeasuredParagraphs;
-
-    // The sorted paragraph end offsets.
-    private final @NonNull int[] mParagraphBreakPoints;
-
-    /**
-     * Create a new {@link PrecomputedText} which will pre-compute text measurement and glyph
-     * positioning information.
-     * <p>
-     * This can be expensive, so computing this on a background thread before your text will be
-     * presented can save work on the UI thread.
-     * </p>
-     *
-     * @param text The text to be measured
-     * @param param Parameters that define how text will be precomputed
-     * @return A {@link PrecomputedText}
-     */
-    public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params param) {
-        return createInternal(text, param, 0, text.length(), true /* compute full Layout */);
-    }
-
-
-    /** @hide */
-    public static PrecomputedText createWidthOnly(@NonNull CharSequence text, @NonNull Params param,
-            @IntRange(from = 0) int start, @IntRange(from = 0) int end) {
-        return createInternal(text, param, start, end, false /* compute width only */);
-    }
-
-    private static PrecomputedText createInternal(@NonNull CharSequence text, @NonNull Params param,
-            @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean computeLayout) {
-        Preconditions.checkNotNull(text);
-        Preconditions.checkNotNull(param);
-        final boolean needHyphenation = param.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE
-                && param.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE;
-
-        final IntArray paragraphEnds = new IntArray();
-        final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
-
-        int paraEnd = 0;
-        for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
-            paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end);
-            if (paraEnd < 0) {
-                // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph
-                // end.
-                paraEnd = end;
-            } else {
-                paraEnd++;  // Includes LINE_FEED(U+000A) to the prev paragraph.
-            }
-
-            paragraphEnds.add(paraEnd);
-            measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
-                    param.getTextPaint(), text, paraStart, paraEnd, param.getTextDirection(),
-                    needHyphenation, computeLayout, null /* no recycle */));
-        }
-
-        return new PrecomputedText(text, start, end, param,
-                                measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]),
-                                paragraphEnds.toArray());
-    }
-
-    // Use PrecomputedText.create instead.
-    private PrecomputedText(@NonNull CharSequence text, @IntRange(from = 0) int start,
-            @IntRange(from = 0) int end, @NonNull Params param,
-            @NonNull MeasuredParagraph[] measuredTexts, @NonNull int[] paragraphBreakPoints) {
-        mText = TextUtils.stringOrSpannedString(text);
-        mStart = start;
-        mEnd = end;
-        mParams = param;
-        mMeasuredParagraphs = measuredTexts;
-        mParagraphBreakPoints = paragraphBreakPoints;
-    }
-
-    /**
-     * Return the underlying text.
-     */
-    public @NonNull CharSequence getText() {
-        return mText;
-    }
-
-    /**
-     * Returns the inclusive start offset of measured region.
-     * @hide
-     */
-    public @IntRange(from = 0) int getStart() {
-        return mStart;
-    }
-
-    /**
-     * Returns the exclusive end offset of measured region.
-     * @hide
-     */
-    public @IntRange(from = 0) int getEnd() {
-        return mEnd;
-    }
-
-    /**
-     * Returns the layout parameters used to measure this text.
-     */
-    public @NonNull Params getParams() {
-        return mParams;
-    }
-
-    /**
-     * Returns the length of the paragraph of this text.
-     */
-    public @IntRange(from = 0) int getParagraphCount() {
-        return mParagraphBreakPoints.length;
-    }
-
-    /**
-     * Returns the paragraph start offset of the text.
-     */
-    public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
-        Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
-        return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
-    }
-
-    /**
-     * Returns the paragraph end offset of the text.
-     */
-    public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
-        Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
-        return mParagraphBreakPoints[paraIndex];
-    }
-
-    /** @hide */
-    public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) {
-        return mMeasuredParagraphs[paraIndex];
-    }
-
-    /**
-     * Returns true if the given TextPaint gives the same result of text layout for this text.
-     * @hide
-     */
-    public boolean canUseMeasuredResult(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
-            @NonNull TextDirectionHeuristic textDir, @NonNull TextPaint paint,
-            @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) {
-        final TextPaint mtPaint = mParams.getTextPaint();
-        return mStart == start
-            && mEnd == end
-            && mParams.sameTextMetricsInternal(paint, textDir, strategy, frequency);
-    }
-
-    /** @hide */
-    public int findParaIndex(@IntRange(from = 0) int pos) {
-        // TODO: Maybe good to remove paragraph concept from PrecomputedText and add substring
-        //       layout support to StaticLayout.
-        for (int i = 0; i < mParagraphBreakPoints.length; ++i) {
-            if (pos < mParagraphBreakPoints[i]) {
-                return i;
-            }
-        }
-        throw new IndexOutOfBoundsException(
-            "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1]
-            + ", gave " + pos);
-    }
-
-    /** @hide */
-    public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
-        final int paraIndex = findParaIndex(start);
-        final int paraStart = getParagraphStart(paraIndex);
-        final int paraEnd = getParagraphEnd(paraIndex);
-        if (start < paraStart || paraEnd < end) {
-            throw new RuntimeException("Cannot measured across the paragraph:"
-                + "para: (" + paraStart + ", " + paraEnd + "), "
-                + "request: (" + start + ", " + end + ")");
-        }
-        return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart);
-    }
-
-    /**
-     * Returns the size of native PrecomputedText memory usage
-     *
-     * Note that this is not guaranteed to be accurate. Must be used only for testing purposes.
-     * @hide
-     */
-    public int getMemoryUsage() {
-        int r = 0;
-        for (int i = 0; i < getParagraphCount(); ++i) {
-            r += getMeasuredParagraph(i).getMemoryUsage();
-        }
-        return r;
-    }
-}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 5869802..e62f421 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -78,23 +78,6 @@
         /**
          * Obtain a builder for constructing StaticLayout objects.
          *
-         * @param source The precomputed text.
-         * @param start The index of the start of the text
-         * @param end The index + 1 of the end of the text
-         * @param paint The base paint used for layout
-         * @param width The width in pixels
-         * @return a builder object used for constructing the StaticLayout
-         */
-        @NonNull
-        public static Builder obtain(@NonNull PrecomputedText source, @IntRange(from = 0) int start,
-                @IntRange(from = 0) int end, @NonNull TextPaint paint,
-                @IntRange(from = 0) int width) {
-            return obtain(source.getText(), source, start, end, paint, width);
-        }
-
-        /**
-         * Obtain a builder for constructing StaticLayout objects.
-         *
          * @param source The text to be laid out, optionally with spans
          * @param start The index of the start of the text
          * @param end The index + 1 of the end of the text
@@ -106,12 +89,6 @@
         public static Builder obtain(@NonNull CharSequence source, @IntRange(from = 0) int start,
                 @IntRange(from = 0) int end, @NonNull TextPaint paint,
                 @IntRange(from = 0) int width) {
-            return obtain(source, null, start, end, paint, width);
-        }
-
-        private static Builder obtain(@NonNull CharSequence source, @Nullable PrecomputedText text,
-                @IntRange(from = 0) int start, @IntRange(from = 0) int end,
-                @NonNull TextPaint paint, @IntRange(from = 0) int width) {
             Builder b = sPool.acquire();
             if (b == null) {
                 b = new Builder();
@@ -119,7 +96,6 @@
 
             // set default initial values
             b.mText = source;
-            b.mPrecomputed = text;
             b.mStart = start;
             b.mEnd = end;
             b.mPaint = paint;
@@ -452,7 +428,6 @@
         }
 
         private CharSequence mText;
-        private PrecomputedText mPrecomputed;
         private int mStart;
         private int mEnd;
         private TextPaint mPaint;
@@ -515,7 +490,7 @@
             float spacingmult, float spacingadd,
             boolean includepad,
             TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
-        this(source, null /* precomputed */, bufstart, bufend, paint, outerwidth, align,
+        this(source, bufstart, bufend, paint, outerwidth, align,
                 TextDirectionHeuristics.FIRSTSTRONG_LTR,
                 spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth, Integer.MAX_VALUE);
     }
@@ -525,16 +500,7 @@
      * @deprecated Use {@link Builder} instead.
      */
     @Deprecated
-    public StaticLayout(CharSequence source, int bufstart, int bufend, TextPaint paint,
-                        int outerwidth, Alignment align, TextDirectionHeuristic textDir,
-                        float spacingmult, float spacingadd, boolean includepad,
-                        TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines) {
-        this(source, null /* precomputed */, bufstart, bufend, paint, outerwidth, align, textDir,
-                spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth, maxLines);
-    }
-
-    /** @hide */
-    public StaticLayout(CharSequence source, PrecomputedText precomputed, int bufstart, int bufend,
+    public StaticLayout(CharSequence source, int bufstart, int bufend,
                         TextPaint paint, int outerwidth,
                         Alignment align, TextDirectionHeuristic textDir,
                         float spacingmult, float spacingadd,
@@ -545,7 +511,6 @@
                 : (source instanceof Spanned)
                     ? new SpannedEllipsizer(source)
                     : new Ellipsizer(source),
-              (ellipsize == null) ? precomputed : null,
               paint, outerwidth, align, textDir, spacingmult, spacingadd);
 
         Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth)
@@ -686,20 +651,43 @@
                 b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
                 indents, mLeftPaddings, mRightPaddings);
 
-        PrecomputedText measured = null;
-        final Spanned spanned = (b.mText instanceof Spanned) ? (Spanned) b.mText : null;
-        if (b.mPrecomputed != null) {
-            if (b.mPrecomputed.canUseMeasuredResult(bufStart, bufEnd, textDir, paint,
-                      b.mBreakStrategy, b.mHyphenationFrequency)) {
-                measured = b.mPrecomputed;
+        MeasuredText measured = null;
+        final Spanned spanned;
+        final boolean canUseMeasuredText;
+        if (source instanceof MeasuredText) {
+            measured = (MeasuredText) source;
+
+            if (bufStart != measured.getStart() || bufEnd != measured.getEnd()) {
+                // The buffer position has changed. Re-measure here.
+                canUseMeasuredText = false;
+            } else if (b.mBreakStrategy != measured.getBreakStrategy()
+                    || b.mHyphenationFrequency != measured.getHyphenationFrequency()) {
+                // The computed hyphenation pieces may not be able to used. Re-measure it.
+                canUseMeasuredText = false;
+            } else {
+                // We can use measured information.
+                canUseMeasuredText = true;
             }
-        }
-        if (measured == null) {
-            final PrecomputedText.Params param = new PrecomputedText.Params(paint, textDir,
-                    b.mBreakStrategy, b.mHyphenationFrequency);
-            measured = PrecomputedText.createWidthOnly(source, param, bufStart, bufEnd);
+        } else {
+            canUseMeasuredText = false;
         }
 
+        if (!canUseMeasuredText) {
+            measured = new MeasuredText.Builder(source, paint)
+                    .setRange(bufStart, bufEnd)
+                    .setTextDirection(textDir)
+                    .setBreakStrategy(b.mBreakStrategy)
+                    .setHyphenationFrequency(b.mHyphenationFrequency)
+                    .build(false /* full layout is not necessary for line breaking */);
+            spanned = (source instanceof Spanned) ? (Spanned) source : null;
+        } else {
+            final CharSequence original = measured.getText();
+            spanned = (original instanceof Spanned) ? (Spanned) original : null;
+            // Overwrite with the one when measured.
+            // TODO: Give an option for developer not to overwrite and measure again here?
+            textDir = measured.getTextDir();
+            paint = measured.getPaint();
+        }
 
         try {
             for (int paraIndex = 0; paraIndex < measured.getParagraphCount(); paraIndex++) {
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index be5bb4d..55367dc 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -16,7 +16,6 @@
 
 package android.text;
 
-import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Canvas;
@@ -61,7 +60,7 @@
     private char[] mChars;
     private boolean mCharsValid;
     private Spanned mSpanned;
-    private PrecomputedText mComputed;
+    private MeasuredText mMeasured;
 
     // Additional width of whitespace for justification. This value is per whitespace, thus
     // the line width will increase by mAddedWidth x (number of stretchable whitespaces).
@@ -120,7 +119,7 @@
         tl.mSpanned = null;
         tl.mTabs = null;
         tl.mChars = null;
-        tl.mComputed = null;
+        tl.mMeasured = null;
 
         tl.mMetricAffectingSpanSpanSet.recycle();
         tl.mCharacterStyleSpanSet.recycle();
@@ -150,31 +149,10 @@
      * @param tabStops the tabStops. Can be null.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public void set(TextPaint paint, CharSequence text, int start,
-            int limit, int dir, Directions directions, boolean hasTabs, TabStops tabStops) {
-        set(paint, text, null, start, limit, dir, directions, hasTabs, tabStops);
-    }
-
-    /**
-     * Initializes a TextLine and prepares it for use.
-     *
-     * @param paint the base paint for the line
-     * @param text the text, can be Styled
-     * @param precomputed the precomputed text
-     * @param start the start of the line relative to the text
-     * @param limit the limit of the line relative to the text
-     * @param dir the paragraph direction of this line
-     * @param directions the directions information of this line
-     * @param hasTabs true if the line might contain tabs
-     * @param tabStops the tabStops.
-     */
-    public void set(@NonNull TextPaint paint, @NonNull CharSequence text,
-            @Nullable PrecomputedText precomputed, @IntRange(from = 0) int start,
-            @IntRange(from = 0) int limit, int dir, @NonNull Directions directions, boolean hasTabs,
-            @Nullable TabStops tabStops) {
+    public void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
+            Directions directions, boolean hasTabs, TabStops tabStops) {
         mPaint = paint;
         mText = text;
-        mComputed = precomputed;
         mStart = start;
         mLen = limit - start;
         mDir = dir;
@@ -192,6 +170,14 @@
             hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0;
         }
 
+        mMeasured = null;
+        if (text instanceof MeasuredText) {
+            MeasuredText mt = (MeasuredText) text;
+            if (mt.canUseMeasuredResult(paint)) {
+                mMeasured = mt;
+            }
+        }
+
         mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
 
         if (mCharsValid) {
@@ -760,12 +746,12 @@
             return wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, runIsRtl, offset);
         } else {
             final int delta = mStart;
-            if (mComputed == null) {
+            if (mMeasured == null) {
                 // TODO: Enable measured getRunAdvance for ReplacementSpan and RTL text.
                 return wp.getRunAdvance(mText, delta + start, delta + end,
                         delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
             } else {
-                return mComputed.getWidth(start + delta, end + delta);
+                return mMeasured.getWidth(start + delta, end + delta);
             }
         }
     }
diff --git a/core/java/android/view/RecordingCanvas.java b/core/java/android/view/RecordingCanvas.java
index fc7d828..fbb862b 100644
--- a/core/java/android/view/RecordingCanvas.java
+++ b/core/java/android/view/RecordingCanvas.java
@@ -34,7 +34,7 @@
 import android.graphics.RectF;
 import android.graphics.TemporaryBuffer;
 import android.text.GraphicsOperations;
-import android.text.PrecomputedText;
+import android.text.MeasuredText;
 import android.text.SpannableString;
 import android.text.SpannedString;
 import android.text.TextUtils;
@@ -507,8 +507,8 @@
             TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
             long measuredTextPtr = 0;
             int measuredTextOffset = 0;
-            if (text instanceof PrecomputedText) {
-                PrecomputedText mt = (PrecomputedText) text;
+            if (text instanceof MeasuredText) {
+                MeasuredText mt = (MeasuredText) text;
                 int paraIndex = mt.findParaIndex(start);
                 if (end <= mt.getParagraphEnd(paraIndex)) {
                     // Only support if the target is in the same paragraph.
@@ -641,7 +641,7 @@
     @FastNative
     private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count,
             int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint,
-            long nativePrecomputedText, int measuredTextOffset);
+            long nativeMeasuredText, int measuredTextOffset);
 
     @FastNative
     private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f6e771a..5710db3 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -80,8 +80,8 @@
 import android.text.InputFilter;
 import android.text.InputType;
 import android.text.Layout;
+import android.text.MeasuredText;
 import android.text.ParcelableSpan;
-import android.text.PrecomputedText;
 import android.text.Selection;
 import android.text.SpanWatcher;
 import android.text.Spannable;
@@ -637,7 +637,6 @@
     private CharSequence mText;
     private CharSequence mTransformed;
     private BufferType mBufferType = BufferType.NORMAL;
-    private PrecomputedText mPrecomputed;
 
     private CharSequence mHint;
     private Layout mHintLayout;
@@ -4086,80 +4085,6 @@
     }
 
     /**
-     * Gets the parameters for text layout precomputation, for use with {@link PrecomputedText}
-     *
-     * @return A current {@link PrecomputedText.Params}
-     */
-    public @NonNull PrecomputedText.Params getTextMetricsParams() {
-        return new PrecomputedText.Params(new TextPaint(mTextPaint), getTextDirectionHeuristic(),
-                mBreakStrategy, mHyphenationFrequency);
-    }
-
-    /**
-     * Apply the text layout parameter.
-     */
-    public void setTextMetricsParams(@NonNull PrecomputedText.Params params) {
-        mTextPaint.set(params.getTextPaint());
-        mTextDir = params.getTextDirection();
-        mBreakStrategy = params.getBreakStrategy();
-        mHyphenationFrequency = params.getHyphenationFrequency();
-        if (mLayout != null) {
-            nullLayouts();
-            requestLayout();
-            invalidate();
-        }
-    }
-
-    /**
-     * Sets the precomputed text.
-     *
-     * If the parameters for the precomputed text is different from current text view parameters,
-     * apply the parameteres to the text view too.
-     *
-     * @param text A precomputed text.
-     */
-    public void setPrecomputedTextAndParams(@NonNull PrecomputedText text) {
-        Preconditions.checkNotNull(text);
-        final PrecomputedText.Params params = text.getParams();
-        if (!params.sameTextMetrics(getTextMetricsParams())) {
-            setTextMetricsParams(params);
-        }
-        setText(text.getText());
-        if (mTransformed != text.getText()) {
-            // setText modified given text for some reasons, selection, transformation, etc.
-            // Can't use computed result.
-            return;
-        } else {
-            mPrecomputed = text;
-        }
-    }
-
-    /**
-     * Sets the precomputed text.
-     *
-     * If the parameters for the precomputed text is different from current text view parameters,
-     * throws {@link IllegalArgumentException}.
-     *
-     * @param text A precomputed text.
-     */
-    public void setPrecomputedTextOrThrow(@NonNull PrecomputedText text) {
-        Preconditions.checkNotNull(text);
-        final PrecomputedText.Params params = text.getParams();
-        if (!params.sameTextMetrics(getTextMetricsParams())) {
-            throw new IllegalArgumentException(
-                "The precomputed configuration is different from this TextView.");
-        }
-        setText(text.getText());
-        if (mTransformed != text.getText()) {
-            // setText modified given text for some reasons, selection, transformation, etc.
-            // Can't use computed result.
-            // TODO: Do we throw an exception here too?
-        } else {
-            mPrecomputed = text;
-        }
-    }
-
-    /**
      * Set justification mode. The default value is {@link Layout#JUSTIFICATION_MODE_NONE}. If the
      * last line is too short for justification, the last line will be displayed with the
      * alignment set by {@link android.view.View#setTextAlignment}.
@@ -5594,7 +5519,6 @@
 
     private void setText(CharSequence text, BufferType type,
                          boolean notifyBefore, int oldlen) {
-        mPrecomputed = null;
         mTextSetFromXmlOrResourceId = false;
         if (text == null) {
             text = "";
@@ -5653,7 +5577,7 @@
             if (imm != null) imm.restartInput(this);
         } else if (type == BufferType.SPANNABLE || mMovement != null) {
             text = mSpannableFactory.newSpannable(text);
-        } else if (!(text instanceof CharWrapper)) {
+        } else if (!(text instanceof MeasuredText || text instanceof CharWrapper)) {
             text = TextUtils.stringOrSpannedString(text);
         }
 
@@ -8320,8 +8244,7 @@
             result = builder.build();
         } else {
             if (boring == UNKNOWN_BORING) {
-                boring = BoringLayout.isBoring(mTransformed, mPrecomputed, mTextPaint, mTextDir,
-                        mBoring);
+                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
                 if (boring != null) {
                     mBoring = boring;
                 }
@@ -8359,15 +8282,9 @@
             }
         }
         if (result == null) {
-            StaticLayout.Builder builder;
-            if (mPrecomputed != null) {
-                builder = StaticLayout.Builder.obtain(mPrecomputed, 0,
-                        mPrecomputed.getText().length(), mTextPaint, wantWidth);
-            } else {
-                builder = StaticLayout.Builder.obtain(mTransformed, 0, mTransformed.length(),
-                        mTextPaint, wantWidth);
-            }
-            builder.setAlignment(alignment)
+            StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,
+                    0, mTransformed.length(), mTextPaint, wantWidth)
+                    .setAlignment(alignment)
                     .setTextDirection(mTextDir)
                     .setLineSpacing(mSpacingAdd, mSpacingMult)
                     .setIncludePad(mIncludePad)
@@ -8494,8 +8411,7 @@
             }
 
             if (des < 0) {
-                boring = BoringLayout.isBoring(mTransformed, mPrecomputed, mTextPaint, mTextDir,
-                        mBoring);
+                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
                 if (boring != null) {
                     mBoring = boring;
                 }
@@ -11780,9 +11696,6 @@
     }
 
     /**
-     * Returns the current {@link TextDirectionHeuristic}
-     *
-     * @return A {@link TextDirectionHeuristic}.
      * @hide
      */
     protected TextDirectionHeuristic getTextDirectionHeuristic() {
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 2c05d0b..482d028 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -1008,23 +1008,6 @@
         return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr);
     }
 
-    static jboolean equalsForTextMeasurement(jlong lPaint, jlong rPaint) {
-        if (lPaint == rPaint) {
-            return true;
-        }
-        Paint* leftPaint = reinterpret_cast<Paint*>(lPaint);
-        Paint* rightPaint = reinterpret_cast<Paint*>(rPaint);
-
-        const Typeface* leftTypeface = Typeface::resolveDefault(leftPaint->getAndroidTypeface());
-        const Typeface* rightTypeface = Typeface::resolveDefault(rightPaint->getAndroidTypeface());
-        minikin::MinikinPaint leftMinikinPaint
-                = MinikinUtils::prepareMinikinPaint(leftPaint, leftTypeface);
-        minikin::MinikinPaint rightMinikinPaint
-                = MinikinUtils::prepareMinikinPaint(rightPaint, rightTypeface);
-
-        return leftMinikinPaint == rightMinikinPaint;
-    }
-
 }; // namespace PaintGlue
 
 static const JNINativeMethod methods[] = {
@@ -1124,8 +1107,7 @@
     {"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition},
     {"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness},
     {"nSetShadowLayer", "(JFFFI)V", (void*)PaintGlue::setShadowLayer},
-    {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer},
-    {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
+    {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer}
 };
 
 int register_android_graphics_Paint(JNIEnv* env) {
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 07df045..eacb727 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -22,7 +22,7 @@
 import android.annotation.Size;
 import android.graphics.Canvas.VertexMode;
 import android.text.GraphicsOperations;
-import android.text.PrecomputedText;
+import android.text.MeasuredText;
 import android.text.SpannableString;
 import android.text.SpannedString;
 import android.text.TextUtils;
@@ -487,8 +487,8 @@
             TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
             long measuredTextPtr = 0;
             int measuredTextOffset = 0;
-            if (text instanceof PrecomputedText) {
-                PrecomputedText mt = (PrecomputedText) text;
+            if (text instanceof MeasuredText) {
+                MeasuredText mt = (MeasuredText) text;
                 int paraIndex = mt.findParaIndex(start);
                 if (end <= mt.getParagraphEnd(paraIndex)) {
                     // Only suppor the same paragraph.
@@ -647,7 +647,7 @@
 
     private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count,
             int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint,
-            long nativePrecomputedText, int measuredTextOffset);
+            long nativeMeasuredText, int measuredTextOffset);
 
     private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
             long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint);
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 42dac38..ed147e9 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -2835,16 +2835,6 @@
         return result;
     }
 
-    /**
-     * Returns true of the passed {@link Paint} will have the same effect on text measurement
-     *
-     * @param other A {@link Paint} object.
-     * @return true if the other {@link Paint} has the same effect on text measurement.
-     */
-    public boolean equalsForTextMeasurement(@NonNull Paint other) {
-        return nEqualsForTextMeasurement(mNativePaint, other.mNativePaint);
-    }
-
     // regular JNI
     private static native long nGetNativeFinalizer();
     private static native long nInit();
@@ -3012,6 +3002,4 @@
     private static native float nGetStrikeThruThickness(long paintPtr);
     @CriticalNative
     private static native void nSetTextSize(long paintPtr, float textSize);
-    @CriticalNative
-    private static native boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr);
 }