Merge "Updating CTS to account for behavior change in LauncherApps" into pi-dev
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
index e4dee54..7296513 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
@@ -66,16 +66,6 @@
     private static final String TAG = AtomTests.class.getSimpleName();
 
     @Test
-    public void testAppStart() throws Exception {
-        Context context = InstrumentationRegistry.getContext();
-        Intent intent = new Intent(context, StatsdCtsForegroundActivity.class);
-        intent.putExtra(StatsdCtsForegroundActivity.KEY_ACTION,
-                StatsdCtsForegroundActivity.ACTION_SLEEP_WHILE_TOP);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        context.startActivity(intent);
-    }
-
-    @Test
     public void testAudioState() {
         // TODO: This should surely be getTargetContext(), here and everywhere, but test first.
         Context context = InstrumentationRegistry.getContext();
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
index 14f3dba..615bbc0 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
@@ -38,6 +38,7 @@
     public static final String ACTION_END_IMMEDIATELY = "action.end_immediately";
     public static final String ACTION_SLEEP_WHILE_TOP = "action.sleep_top";
     public static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
+    public static final String ACTION_CRASH = "action.crash";
 
     public static final int SLEEP_OF_ACTION_SLEEP_WHILE_TOP = 2_000;
     public static final int SLEEP_OF_ACTION_SHOW_APPLICATION_OVERLAY = 2_000;
@@ -65,6 +66,9 @@
             case ACTION_SHOW_APPLICATION_OVERLAY:
                 doShowApplicationOverlay();
                 break;
+            case ACTION_CRASH:
+                doCrash();
+                break;
             default:
                 Log.e(TAG, "Intent had invalid action " + action);
                 finish();
@@ -116,4 +120,8 @@
         AtomTests.sleep(SLEEP_OF_ACTION_SHOW_APPLICATION_OVERLAY);
         finish();
     }
+
+    private void doCrash() {
+        Log.e(TAG, "About to crash the app with 1/0 " + (long)1/0);
+    }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index a9d5163..b5677ce 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -16,6 +16,8 @@
 package android.cts.statsd.atom;
 
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
 
 import android.os.WakeLockLevelEnum;
 
@@ -30,6 +32,7 @@
 import com.android.os.AtomsProto.CameraStateChanged;
 import com.android.os.AtomsProto.CpuTimePerUid;
 import com.android.os.AtomsProto.CpuTimePerUidFreq;
+import com.android.os.AtomsProto.DropboxErrorChanged;
 import com.android.os.AtomsProto.FlashlightStateChanged;
 import com.android.os.AtomsProto.ForegroundServiceStateChanged;
 import com.android.os.AtomsProto.GpsScanStateChanged;
@@ -73,19 +76,17 @@
 
     public void testAppStartChanged() throws Exception {
         final int atomTag = Atom.APP_START_CHANGED_FIELD_NUMBER;
-        final String name = "testAppStart";
 
         createAndUploadConfig(atomTag, false);
         Thread.sleep(WAIT_TIME_SHORT);
 
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
+        runActivity("StatsdCtsForegroundActivity", "action", "action.sleep_top");
 
         // Sorted list of events in order in which they occurred.
         List<EventMetricData> data = getEventMetricDataList();
 
         AppStartChanged atom = data.get(0).getAtom().getAppStartChanged();
         assertEquals("com.android.server.cts.device.statsd", atom.getPkgName());
-        assertEquals(AppStartChanged.TransitionType.WARM, atom.getType());
         assertEquals("com.android.server.cts.device.statsd.StatsdCtsForegroundActivity",
                 atom.getActivityName());
         assertFalse(atom.getIsInstantApp());
@@ -601,4 +602,20 @@
         assertStatesOccurred(stateSet, data, 1_000,
                 atom -> atom.getOverlayStateChanged().getState().getNumber());
     }
+
+    public void testDropboxErrorChanged() throws Exception {
+        final int atomTag = Atom.DROPBOX_ERROR_CHANGED_FIELD_NUMBER;
+        createAndUploadConfig(atomTag, false);
+        Thread.sleep(WAIT_TIME_SHORT);
+
+        runActivity("StatsdCtsForegroundActivity", "action", "action.crash");
+
+        Thread.sleep(WAIT_TIME_SHORT);
+        // Sorted list of events in order in which they occurred.
+        List<EventMetricData> data = getEventMetricDataList();
+
+        DropboxErrorChanged atom = data.get(0).getAtom().getDropboxErrorChanged();
+        assertTrue(atom.getIsInstantApp() == 0);
+        assertEquals("data_app_crash", atom.getTag());
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
index 16626a4..7e2947a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
@@ -456,7 +456,6 @@
 
     @Test
     public void testSave_submitButtonClicked() throws Throwable {
-        if (mCompatMode) return; // TODO(b/73649008): implement it
         saveTest(CommitType.SUBMIT_BUTTON_CLICKED);
     }
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
index 8b677b4..a5a991b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
@@ -269,15 +269,12 @@
     void clickLogin() {
         Log.d(TAG, "clickLogin()");
         if (mCompatMode) {
-            // TODO(b/73649008): implement it
-            throw new IllegalArgumentException("clickLogin() on compat mode not implemented yet");
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED, LOGIN_BUTTON_VIRTUAL_ID);
         } else {
             mAfm.notifyViewClicked(this, LOGIN_BUTTON_VIRTUAL_ID);
         }
     }
 
-
-
     private Item getItem(int id) {
         final Item item = mItems.get(id);
         assertWithMessage("No item for id %s", id).that(item).isNotNull();
@@ -303,6 +300,15 @@
         return node;
     }
 
+    private AccessibilityNodeInfo onProvideAutofillCompatModeAccessibilityNodeInfoForLoginButton() {
+        final AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain();
+        node.setSource(this, LOGIN_BUTTON_VIRTUAL_ID);
+        node.setPackageName(getContext().getPackageName());
+        // TODO(b/72811561): ideally this button should be visible / drawn in the canvas and contain
+        // more properties like boundaries, class name, text etc...
+        return node;
+    }
+
     static void assertHtmlInfo(ViewNode node) {
         final String name = node.getText().toString();
         final HtmlInfo info = node.getHtmlInfo();
@@ -341,12 +347,16 @@
                 @Override
                 public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
                     Log.d(TAG, "createAccessibilityNodeInfo(): id=" + virtualViewId);
-                    if (virtualViewId == AccessibilityNodeProvider.HOST_VIEW_ID) {
-                        return onProvideAutofillCompatModeAccessibilityNodeInfo();
+                    switch (virtualViewId) {
+                        case AccessibilityNodeProvider.HOST_VIEW_ID:
+                            return onProvideAutofillCompatModeAccessibilityNodeInfo();
+                        case LOGIN_BUTTON_VIRTUAL_ID:
+                            return onProvideAutofillCompatModeAccessibilityNodeInfoForLoginButton();
+                        default:
+                            final Item item = getItem(virtualViewId);
+                            return item.provideAccessibilityNodeInfo(VirtualContainerView.this,
+                                    getContext());
                     }
-                    final Item item = getItem(virtualViewId);
-                    return item.provideAccessibilityNodeInfo(VirtualContainerView.this,
-                            getContext());
                 }
 
                 @Override
@@ -377,6 +387,16 @@
         mOverrideDispatchProvideAutofillStructure = flag;
     }
 
+    private void sendAccessibilityEvent(int eventType, int virtualId) {
+        final AccessibilityEvent event = AccessibilityEvent.obtain();
+        event.setEventType(eventType);
+        event.setSource(VirtualContainerView.this, virtualId);
+        event.setEnabled(true);
+        event.setPackageName(getContext().getPackageName());
+        // TODO(b/72811561): recycle event?
+        getContext().getSystemService(AccessibilityManager.class).sendAccessibilityEvent(event);
+    }
+
     private static int nextId;
 
     final class Line {
@@ -405,14 +425,7 @@
             }
 
             if (mCompatMode) {
-                final AccessibilityEvent event = AccessibilityEvent.obtain();
-                event.setEventType(AccessibilityEvent.TYPE_VIEW_FOCUSED);
-                event.setSource(VirtualContainerView.this, text.id);
-                event.setEnabled(true);
-                event.setPackageName(getContext().getPackageName());
-                // TODO(b/72811561): recycle event?
-                getContext().getSystemService(AccessibilityManager.class)
-                        .sendAccessibilityEvent(event);
+                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED, text.id);
                 return;
             }
 
diff --git a/tests/tests/display/AndroidManifest.xml b/tests/tests/display/AndroidManifest.xml
index 7b1e371..5651f47 100644
--- a/tests/tests/display/AndroidManifest.xml
+++ b/tests/tests/display/AndroidManifest.xml
@@ -23,6 +23,8 @@
     <!-- For testing brightness slider tracking. -->
     <uses-permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <!-- For testing pushing brightness curves. -->
+    <uses-permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/tests/display/src/android/display/cts/BrightnessTest.java b/tests/tests/display/src/android/display/cts/BrightnessTest.java
index 62617d6..65fcb6e 100644
--- a/tests/tests/display/src/android/display/cts/BrightnessTest.java
+++ b/tests/tests/display/src/android/display/cts/BrightnessTest.java
@@ -21,6 +21,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.hardware.display.BrightnessChangeEvent;
+import android.hardware.display.BrightnessConfiguration;
 import android.hardware.display.DisplayManager;
 import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
@@ -49,8 +50,16 @@
                 InstrumentationRegistry.getContext().getSystemService(DisplayManager.class);
         PowerManager pm =
                 InstrumentationRegistry.getContext().getSystemService(PowerManager.class);
+        // Fail early if screen isn't on as wakelock won't wake it up.
+        assertTrue(pm.isInteractive());
+
         mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "BrightnessTest");
         mWakeLock.acquire();
+
+        runShellCommand("pm revoke " + InstrumentationRegistry.getContext().getPackageName()
+                + " android.permission.CONFIGURE_DISPLAY_BRIGHTNESS");
+        runShellCommand("pm revoke " + InstrumentationRegistry.getContext().getPackageName()
+                + " android.permission.BRIGHTNESS_SLIDER_USAGE");
     }
 
     @Override
@@ -111,6 +120,151 @@
         }
     }
 
+    public void testNoTrackingForManualBrightness() throws IOException, InterruptedException {
+        if (!systemAppWithPermission("android.permission.BRIGHTNESS_SLIDER_USAGE",
+                InstrumentationRegistry.getContext())) {
+            // Don't run as there is no app that has permission to access slider usage.
+            return;
+        }
+        int previousBrightness = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS);
+        int previousBrightnessMode =
+                getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
+        try {
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE,
+                    Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+            int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
+            assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, mode);
+
+            runShellCommand("pm grant " + InstrumentationRegistry.getContext().getPackageName()
+                    + " android.permission.BRIGHTNESS_SLIDER_USAGE");
+
+            // Setup and remember some initial state.
+            recordSliderEvents();
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 20);
+            assertTrue(getNewEvents().isEmpty());
+
+            // Then change the brightness
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 80);
+            Thread.sleep(200);
+            // There shouldn't be any events.
+            assertTrue(getNewEvents().isEmpty());
+        } finally {
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, previousBrightness);
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, previousBrightnessMode);
+        }
+    }
+
+    public void testSliderUsagePermission() throws IOException, InterruptedException {
+        runShellCommand("pm revoke " + InstrumentationRegistry.getContext().getPackageName()
+                + " android.permission.BRIGHTNESS_SLIDER_USAGE");
+
+        try {
+            mDisplayManager.getBrightnessEvents();
+        } catch (SecurityException e) {
+            // Expected
+            return;
+        }
+        fail();
+    }
+
+    public void testConfigureBrightnessPermission() throws IOException, InterruptedException {
+        runShellCommand("pm revoke " + InstrumentationRegistry.getContext().getPackageName()
+                + " android.permission.CONFIGURE_DISPLAY_BRIGHTNESS");
+
+        BrightnessConfiguration config =
+            new BrightnessConfiguration.Builder(
+                    new float[]{0.0f, 1000.0f},new float[]{20.0f, 500.0f})
+                .setDescription("some test").build();
+
+        try {
+            mDisplayManager.setBrightnessConfiguration(config);
+        } catch (SecurityException e) {
+            // Expected
+            return;
+        }
+        fail();
+    }
+
+    public void testPushSimpleCurves() throws IOException, InterruptedException {
+        if (!systemAppWithPermission("android.permission.CONFIGURE_DISPLAY_BRIGHTNESS",
+                InstrumentationRegistry.getContext())) {
+            // Don't run as there is no app that has permission to push curves.
+            return;
+        }
+        runShellCommand("pm grant " + InstrumentationRegistry.getContext().getPackageName()
+                + " android.permission.CONFIGURE_DISPLAY_BRIGHTNESS");
+
+        BrightnessConfiguration config =
+                new BrightnessConfiguration.Builder(
+                        new float[]{0.0f, 1000.0f},new float[]{20.0f, 500.0f})
+                        .setDescription("some test").build();
+        mDisplayManager.setBrightnessConfiguration(config);
+        mDisplayManager.setBrightnessConfiguration(null);
+    }
+
+    public void testSliderEventsReflectCurves() throws IOException, InterruptedException {
+        if (!systemAppWithPermission("android.permission.BRIGHTNESS_SLIDER_USAGE",
+                InstrumentationRegistry.getContext())) {
+            // Don't run as there is no app that has permission to access slider usage.
+            return;
+        }
+        if (!systemAppWithPermission("android.permission.CONFIGURE_DISPLAY_BRIGHTNESS",
+                InstrumentationRegistry.getContext())) {
+            // Don't run as there is no app that has permission to push curves.
+            return;
+        }
+
+        BrightnessConfiguration config =
+                new BrightnessConfiguration.Builder(
+                        new float[]{0.0f, 10000.0f},new float[]{15.0f, 400.0f})
+                        .setDescription("model:8").build();
+
+        int previousBrightness = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS);
+        int previousBrightnessMode =
+                getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
+        try {
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE,
+                    Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+            int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
+            assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC, mode);
+
+            runShellCommand("pm grant " + InstrumentationRegistry.getContext().getPackageName()
+                    + " android.permission.BRIGHTNESS_SLIDER_USAGE");
+            runShellCommand("pm grant " + InstrumentationRegistry.getContext().getPackageName()
+                    + " android.permission.CONFIGURE_DISPLAY_BRIGHTNESS");
+
+            // Setup and remember some initial state.
+            recordSliderEvents();
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 20);
+            getNewEvents(1);
+
+            // Update brightness while we have a custom curve.
+            mDisplayManager.setBrightnessConfiguration(config);
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 60);
+
+            // Check we got a slider event for the change.
+            List<BrightnessChangeEvent> newEvents = getNewEvents(1);
+            assertEquals(1, newEvents.size());
+            BrightnessChangeEvent firstEvent = newEvents.get(0);
+            assertValidLuxData(firstEvent);
+            assertFalse(firstEvent.isDefaultBrightnessConfig);
+
+            // Update brightness again now with default curve.
+            mDisplayManager.setBrightnessConfiguration(null);
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 200);
+
+            // Check we get a second slider event.
+            newEvents = getNewEvents(1);
+            assertEquals(1, newEvents.size());
+            BrightnessChangeEvent secondEvent = newEvents.get(0);
+            assertValidLuxData(secondEvent);
+            assertTrue(secondEvent.isDefaultBrightnessConfig);
+        } finally {
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, previousBrightness);
+            setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, previousBrightnessMode);
+        }
+    }
+
     private void assertValidLuxData(BrightnessChangeEvent event) {
         assertNotNull(event.luxTimestamps);
         assertNotNull(event.luxValues);
@@ -142,16 +296,22 @@
             if (i != 0) {
                 Thread.sleep(100);
             }
-            List<BrightnessChangeEvent> events = mDisplayManager.getBrightnessEvents();
-            for (BrightnessChangeEvent event : events) {
-                if (!mLastReadEvents.containsKey(event.timeStamp)) {
-                    newEvents.add(event);
-                }
+            newEvents.addAll(getNewEvents());
+        }
+        return newEvents;
+    }
+
+    private List<BrightnessChangeEvent> getNewEvents() {
+        List<BrightnessChangeEvent> newEvents = new ArrayList<>();
+        List<BrightnessChangeEvent> events = mDisplayManager.getBrightnessEvents();
+        for (BrightnessChangeEvent event : events) {
+            if (!mLastReadEvents.containsKey(event.timeStamp)) {
+                newEvents.add(event);
             }
-            mLastReadEvents = new HashMap<>();
-            for (BrightnessChangeEvent event : events) {
-                mLastReadEvents.put(event.timeStamp, event);
-            }
+        }
+        mLastReadEvents = new HashMap<>();
+        for (BrightnessChangeEvent event : events) {
+            mLastReadEvents.put(event.timeStamp, event);
         }
         return newEvents;
     }
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
index 0d2fd02..fe52159 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
@@ -42,6 +42,7 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
 import android.widget.ImageView;
 
 import com.android.compatibility.common.util.BitmapUtils;
@@ -567,6 +568,51 @@
         assertTrue(drawable.isAutoMirrored());
     }
 
+    private void drawAndCompare(Bitmap expected, Drawable drawable) {
+        Bitmap test = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+                drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(test);
+        drawable.draw(canvas);
+        BitmapUtils.compareBitmaps(expected, test);
+    }
+
+    @Test
+    public void testAutoMirroredDrawing() {
+        AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
+        assertFalse(drawable.isAutoMirrored());
+
+        final int width = drawable.getIntrinsicWidth();
+        final int height = drawable.getIntrinsicHeight();
+        Bitmap normal = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        {
+            Canvas canvas = new Canvas(normal);
+            drawable.draw(canvas);
+        }
+
+        Bitmap flipped = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        {
+            Canvas canvas = new Canvas(flipped);
+            canvas.translate(width, 0);
+            canvas.scale(-1, 1);
+            drawable.draw(canvas);
+        }
+
+        for (int i = 0; i < width; ++i) {
+            for (int j = 0; j < height; ++j) {
+                assertEquals(normal.getPixel(i, j), flipped.getPixel(width - 1 - i, j));
+            }
+        }
+
+        drawable.setAutoMirrored(true);
+        drawAndCompare(normal, drawable);
+
+        drawable.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+        drawAndCompare(flipped, drawable);
+
+        drawable.setAutoMirrored(false);
+        drawAndCompare(normal, drawable);
+    }
+
     @Test
     public void testRepeatCountFromXml() throws XmlPullParserException, IOException {
         XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_loop_count);
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
index 7f588bd..94f6772 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
@@ -115,6 +115,40 @@
     }
 
     @Test
+    public void testLayerPaintAlphaChanged() {
+        final CountDownLatch fence = new CountDownLatch(1);
+        createTest()
+            .addLayout(R.layout.frame_layout, view -> {
+                FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout);
+                View child = new View(view.getContext());
+                child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                child.setAlpha(0.0f);
+                // add rendering content
+                child.setBackgroundColor(Color.RED);
+                root.addView(child, new FrameLayout.LayoutParams(TEST_WIDTH, TEST_HEIGHT,
+                        Gravity.TOP | Gravity.LEFT));
+
+                // Post non-zero alpha a few frames in, so that the initial layer draw completes.
+                root.getViewTreeObserver().addOnPreDrawListener(
+                        new ViewTreeObserver.OnPreDrawListener() {
+                            int mDrawCount = 0;
+                            @Override
+                            public boolean onPreDraw() {
+                                if (mDrawCount++ == 5) {
+                                    root.getChildAt(0).setAlpha(1.00f);
+                                    root.getViewTreeObserver().removeOnPreDrawListener(this);
+                                    root.post(fence::countDown);
+                                } else {
+                                    root.postInvalidate();
+                                }
+                                return true;
+                            }
+                        });
+            }, true, fence)
+            .runWithVerifier(new ColorVerifier(Color.RED));
+    }
+
+    @Test
     public void testLayerPaintColorFilter() {
         // Red, fully desaturated. Note that it's not 255/3 in each channel.
         // See ColorMatrix#setSaturation()
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewPrecomputedTextTest.java b/tests/tests/widget/src/android/widget/cts/TextViewPrecomputedTextTest.java
new file mode 100644
index 0000000..282ee5f
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/TextViewPrecomputedTextTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2018 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.widget.cts;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.text.Layout;
+import android.text.PrecomputedText;
+import android.text.PrecomputedText.Params;
+import android.text.PrecomputedText.Params.Builder;
+import android.text.TextDirectionHeuristic;
+import android.text.TextDirectionHeuristics;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.widget.TextView;
+
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Locale;
+
+/**
+ * Tests for TextView with precomputed text.
+ */
+@RunWith(Parameterized.class)
+public class TextViewPrecomputedTextTest {
+    private static final String TEXT = "Hello, World!";
+
+    @Parameterized.Parameter(0)
+    public boolean differentTextSize;
+    @Parameterized.Parameter(1)
+    public boolean differentScaleX;
+    @Parameterized.Parameter(2)
+    public boolean differentSkewX;
+    @Parameterized.Parameter(3)
+    public boolean differentLetterSpacing;
+    @Parameterized.Parameter(4)
+    public boolean differentTextLocale;
+    @Parameterized.Parameter(5)
+    public boolean differentTypeface;
+    @Parameterized.Parameter(6)
+    public boolean differentFontVariationSettings;
+    @Parameterized.Parameter(7)
+    public boolean differentElegantTextHeight;
+    @Parameterized.Parameter(8)
+    public boolean differentBreakStrategy;
+    @Parameterized.Parameter(9)
+    public boolean differentHyphenationFrequency;
+    @Parameterized.Parameter(10)
+    public boolean differentTextDir;
+
+    // text size from the default value.
+    private Pair<Params, String[]> makeDifferentParams(Params params) {
+        final TextPaint paint = new TextPaint(params.getTextPaint());
+        ArrayList<String> differenceList = new ArrayList();
+
+        if (differentTextSize) {
+            paint.setTextSize(paint.getTextSize() * 2.0f + 1.0f);
+            differenceList.add("Text Size");
+        }
+        if (differentScaleX) {
+            paint.setTextScaleX(paint.getTextScaleX() * 2.0f + 1.0f);
+            differenceList.add("Text Scale X");
+        }
+        if (differentSkewX) {
+            paint.setTextSkewX(paint.getTextSkewX() * 2.0f + 1.0f);
+            differenceList.add("Text Skew X");
+        }
+        if (differentLetterSpacing) {
+            paint.setLetterSpacing(paint.getLetterSpacing() * 2.0f + 1.0f);
+            differenceList.add("Letter Spacing");
+        }
+        if (differentTextLocale) {
+            paint.setTextLocale(Locale.US.equals(paint.getTextLocale()) ? Locale.JAPAN : Locale.US);
+        }
+        if (differentTypeface) {
+            final Typeface tf = paint.getTypeface();
+            if (tf == null || tf == Typeface.DEFAULT) {
+                paint.setTypeface(Typeface.SERIF);
+            } else {
+                paint.setTypeface(Typeface.DEFAULT);
+            }
+            differenceList.add("Typeface");
+        }
+        if (differentFontVariationSettings) {
+            final String wght = "'wght' 700";
+            final String wdth = "'wdth' 100";
+
+            final String varSettings = paint.getFontVariationSettings();
+            if (varSettings == null || varSettings.equals(wght)) {
+                paint.setFontVariationSettings(wdth);
+            } else {
+                paint.setFontVariationSettings(wght);
+            }
+            differenceList.add("Font variation settings");
+        }
+        if (differentElegantTextHeight) {
+            paint.setElegantTextHeight(!paint.isElegantTextHeight());
+            differenceList.add("Elegant Text Height");
+        }
+
+        int strategy = params.getBreakStrategy();
+        if (differentBreakStrategy) {
+            strategy = strategy == Layout.BREAK_STRATEGY_SIMPLE
+                    ?  Layout.BREAK_STRATEGY_HIGH_QUALITY : Layout.BREAK_STRATEGY_SIMPLE;
+            differenceList.add("Break strategy");
+        }
+
+        int hyFreq = params.getHyphenationFrequency();
+        if (differentHyphenationFrequency) {
+            hyFreq = hyFreq == Layout.HYPHENATION_FREQUENCY_NONE
+                    ? Layout.HYPHENATION_FREQUENCY_FULL : Layout.HYPHENATION_FREQUENCY_NONE;
+            differenceList.add("Hyphenation Frequency");
+        }
+
+        TextDirectionHeuristic dir = params.getTextDirection();
+        if (differentTextDir) {
+            dir = dir == TextDirectionHeuristics.LTR
+                    ?  TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR;
+            differenceList.add("Text Direction");
+        }
+
+        final Params outParams = new Builder(paint).setBreakStrategy(strategy)
+                .setHyphenationFrequency(hyFreq).setTextDirection(dir).build();
+        return new Pair(outParams, differenceList.toArray(new String[differenceList.size()]));
+    }
+
+    private static Context getContext() {
+        return InstrumentationRegistry.getInstrumentation().getTargetContext();
+    }
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> getParameters() {
+        ArrayList<Object[]> allParams = new ArrayList<>();
+
+        // Compute the powerset except for all false case.
+        int allParameterCount = 11;
+        for (int bits = 1; bits < (1 << allParameterCount); ++bits) {
+            Object[] param = new Object[allParameterCount];
+            for (int j = 0; j < allParameterCount; ++j) {
+                param[j] = new Boolean((bits & (1 << j)) != 0);
+            }
+            allParams.add(param);
+        }
+        return allParams;
+    }
+
+    @SmallTest
+    @Test
+    public void setText() {
+        final TextView tv = new TextView(getContext());
+        final Params tvParams = tv.getTextMetricsParams();
+        final Pair<Params, String[]> testConfig = makeDifferentParams(tvParams);
+        final Params pctParams = testConfig.first;
+
+        final PrecomputedText pct = PrecomputedText.create(TEXT, pctParams);
+        try {
+            tv.setText(pct);
+            fail("Test Case: {" + TextUtils.join(",", testConfig.second) + "}, "
+                    + tvParams.toString() + " vs " + pctParams.toString());
+        } catch (IllegalArgumentException e) {
+            // pass
+        }
+
+        tv.setTextMetricsParams(pctParams);
+        tv.setText(pct);
+    }
+}