Merge "Made ContentCapture APIs available for testing."
diff --git a/api/current.txt b/api/current.txt
index 9dec22e..50ff701 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -44136,7 +44136,7 @@
   }
 
   public final class AvailableNetworkInfo implements android.os.Parcelable {
-    ctor public AvailableNetworkInfo(int, int, java.util.ArrayList<java.lang.String>);
+    ctor public AvailableNetworkInfo(int, int, java.util.List<java.lang.String>);
     method public int describeContents();
     method public java.util.List<java.lang.String> getMccMncs();
     method public int getPriority();
@@ -45098,7 +45098,7 @@
     method public int getNetworkType();
     method public int getPhoneCount();
     method public int getPhoneType();
-    method public int getPreferredOpportunisticDataSubscription();
+    method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.ServiceState getServiceState();
     method @Nullable public android.telephony.SignalStrength getSignalStrength();
     method public int getSimCarrierId();
@@ -45134,6 +45134,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
     method public boolean isHearingAidCompatibilitySupported();
     method public boolean isNetworkRoaming();
+    method public boolean isRttSupported();
     method public boolean isSmsCapable();
     method @Deprecated public boolean isTtyModeSupported();
     method public boolean isVoiceCapable();
@@ -45666,9 +45667,9 @@
   }
 
   public interface GroupCallCallback {
-    method public void onBroadcastSignalStrengthUpdated(@IntRange(from=0xffffffff, to=4) int);
-    method public void onError(int, @Nullable String);
-    method public void onGroupCallStateChanged(int, int);
+    method public default void onBroadcastSignalStrengthUpdated(@IntRange(from=0xffffffff, to=4) int);
+    method public default void onError(int, @Nullable String);
+    method public default void onGroupCallStateChanged(int, int);
     field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff
   }
 
@@ -45726,10 +45727,10 @@
   }
 
   public interface MbmsGroupCallSessionCallback {
-    method public void onAvailableSaisUpdated(@NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.util.List<java.lang.Integer>>);
-    method public void onError(int, @Nullable String);
-    method public void onMiddlewareReady();
-    method public void onServiceInterfaceAvailable(@NonNull String, int);
+    method public default void onAvailableSaisUpdated(@NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.util.List<java.lang.Integer>>);
+    method public default void onError(int, @Nullable String);
+    method public default void onMiddlewareReady();
+    method public default void onServiceInterfaceAvailable(@NonNull String, int);
   }
 
   public class MbmsStreamingSessionCallback {
@@ -50668,7 +50669,7 @@
     method public void setClickable(boolean);
     method public void setClipBounds(android.graphics.Rect);
     method public void setClipToOutline(boolean);
-    method public void setContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSession);
+    method public void setContentCaptureSession(@Nullable android.view.contentcapture.ContentCaptureSession);
     method public void setContentDescription(CharSequence);
     method public void setContextClickable(boolean);
     method public void setDefaultFocusHighlightEnabled(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index 87c8c70..a9ef144 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -9298,6 +9298,8 @@
     method @Nullable public android.view.contentcapture.ViewNode getViewNode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
+    field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5
+    field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4
     field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
     field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2
     field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3
diff --git a/api/test-current.txt b/api/test-current.txt
index 32b131f..d434129 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1769,6 +1769,7 @@
     method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static long getContributedMediaSize(android.content.Context, String, android.os.UserHandle) throws java.io.IOException;
     method @NonNull public static java.io.File getVolumePath(@NonNull String) throws java.io.FileNotFoundException;
     method @NonNull public static java.util.Collection<java.io.File> getVolumeScanPaths(@NonNull String) throws java.io.FileNotFoundException;
+    field public static final String EXTRA_ORIGINATED_FROM_SHELL = "android.intent.extra.originated_from_shell";
     field public static final String SCAN_FILE_CALL = "scan_file";
     field public static final String SCAN_VOLUME_CALL = "scan_volume";
   }
@@ -2164,7 +2165,6 @@
   public class TelephonyManager {
     method public int checkCarrierPrivilegesForPackage(String);
     method public int getCarrierIdListVersion();
-    method public boolean isRttSupported();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
     method public void setCarrierTestOverride(String, String, String, String, String, String, String);
     field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index eaba9be..40a4070 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -559,7 +559,7 @@
 
                 // Handles the oneof field in KeyValuePair atom.
                 if (isKeyValuePairAtom && depth == 2) {
-                    pos[depth] = 4;
+                    pos[depth] = 5;
                 }
 
                 mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(elem.data.float32)));
@@ -575,7 +575,7 @@
 
                 // Handles the oneof field in KeyValuePair atom.
                 if (isKeyValuePairAtom && depth == 2) {
-                    pos[depth] = 3;
+                    pos[depth] = 4;
                 }
                 mValues.push_back(FieldValue(Field(mTagId, pos, depth),
                                              Value(string(elem.data.string, elem.len))));
@@ -593,7 +593,7 @@
                     }
                     // Handles the oneof field in KeyValuePair atom.
                     if (isKeyValuePairAtom && depth == 2) {
-                        pos[depth] = 2;
+                        pos[depth] = 3;
                     }
                     mValues.push_back(
                             FieldValue(Field(mTagId, pos, depth), Value((int64_t)elem.data.int64)));
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 3a5be43..eec3c73 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -155,7 +155,7 @@
     EXPECT_EQ(33, item5.mValue.int_value);
 
     const FieldValue& item6 = event1.getValues()[6];
-    EXPECT_EQ(0x2010482, item6.mField.getField());
+    EXPECT_EQ(0x2010483, item6.mField.getField());
     EXPECT_EQ(Type::LONG, item6.mValue.getType());
     EXPECT_EQ(678L, item6.mValue.int_value);
 
@@ -165,7 +165,7 @@
     EXPECT_EQ(44, item7.mValue.int_value);
 
     const FieldValue& item8 = event1.getValues()[8];
-    EXPECT_EQ(0x2010582, item8.mField.getField());
+    EXPECT_EQ(0x2010583, item8.mField.getField());
     EXPECT_EQ(Type::LONG, item8.mValue.getType());
     EXPECT_EQ(890L, item8.mValue.int_value);
 
@@ -175,7 +175,7 @@
     EXPECT_EQ(1, item9.mValue.int_value);
 
     const FieldValue& item10 = event1.getValues()[10];
-    EXPECT_EQ(0x2010683, item10.mField.getField());
+    EXPECT_EQ(0x2010684, item10.mField.getField());
     EXPECT_EQ(Type::STRING, item10.mValue.getType());
     EXPECT_EQ("test2", item10.mValue.str_value);
 
@@ -185,7 +185,7 @@
     EXPECT_EQ(2, item11.mValue.int_value);
 
     const FieldValue& item12 = event1.getValues()[12];
-    EXPECT_EQ(0x2010783, item12.mField.getField());
+    EXPECT_EQ(0x2010784, item12.mField.getField());
     EXPECT_EQ(Type::STRING, item12.mValue.getType());
     EXPECT_EQ("test1", item12.mValue.str_value);
 
@@ -195,7 +195,7 @@
     EXPECT_EQ(111, item13.mValue.int_value);
 
     const FieldValue& item14 = event1.getValues()[14];
-    EXPECT_EQ(0x2010884, item14.mField.getField());
+    EXPECT_EQ(0x2010885, item14.mField.getField());
     EXPECT_EQ(Type::FLOAT, item14.mValue.getType());
     EXPECT_EQ(2.2f, item14.mValue.float_value);
 
@@ -205,7 +205,7 @@
     EXPECT_EQ(222, item15.mValue.int_value);
 
     const FieldValue& item16 = event1.getValues()[16];
-    EXPECT_EQ(0x2018984, item16.mField.getField());
+    EXPECT_EQ(0x2018985, item16.mField.getField());
     EXPECT_EQ(Type::FLOAT, item16.mValue.getType());
     EXPECT_EQ(1.1f, item16.mValue.float_value);
 }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index aac8f08..0eadd1d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7155,7 +7155,8 @@
         mInstrumentation.onEnterAnimationComplete();
         onEnterAnimationComplete();
         if (getWindow() != null && getWindow().getDecorView() != null) {
-            getWindow().getDecorView().getViewTreeObserver().dispatchOnEnterAnimationComplete();
+            View decorView = getWindow().getDecorView();
+            decorView.getViewTreeObserver().dispatchOnEnterAnimationComplete();
         }
     }
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 9ee2f03..ea145f0 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -4485,6 +4485,7 @@
      * @hide
      */
     public int noteProxyOpNoThrow(int op, String proxiedPackageName) {
+        logOperationIfNeeded(op, mContext.getOpPackageName(), proxiedPackageName);
         try {
             return mService.noteProxyOperation(op, Process.myUid(), mContext.getOpPackageName(),
                     Binder.getCallingUid(), proxiedPackageName);
@@ -4500,7 +4501,7 @@
      */
     @UnsupportedAppUsage
     public int noteOpNoThrow(int op, int uid, String packageName) {
-        logNoteOpIfNeeded(op, packageName);
+        logOperationIfNeeded(op, packageName, null);
         try {
             return mService.noteOperation(op, uid, packageName);
         } catch (RemoteException e) {
@@ -4608,6 +4609,7 @@
      * @hide
      */
     public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) {
+        logOperationIfNeeded(op, packageName, null);
         try {
             return mService.startOperation(getToken(mService), op, uid, packageName,
                     startIfModeDefault);
@@ -4624,6 +4626,7 @@
      * @hide
      */
     public void finishOp(int op, int uid, String packageName) {
+        logOperationIfNeeded(op, packageName, null);
         try {
             mService.finishOperation(getToken(mService), op, uid, packageName);
         } catch (RemoteException e) {
@@ -4870,7 +4873,7 @@
         return AppOpsManager.MODE_DEFAULT;
     }
 
-    private static void logNoteOpIfNeeded(int op, String callingPackage) {
+    private static void logOperationIfNeeded(int op, String callingPackage, String proxiedPackage) {
         // Check if debug logging propety is enabled.
         if (!SystemProperties.getBoolean(DEBUG_LOGGING_ENABLE_PROP, false)) {
             return;
@@ -4908,6 +4911,6 @@
         // Log a stack trace
         Exception here = new Exception("HERE!");
         android.util.Log.i(DEBUG_LOGGING_TAG, "Note operation package= " + callingPackage
-                + " op= " + opStr, here);
+                + " proxied= " + proxiedPackage + " op= " + opStr, here);
     }
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e2907e2..6e52b33 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -866,6 +866,14 @@
      */
     public static final int INSTALL_ENABLE_ROLLBACK = 0x00040000;
 
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that package verification should be
+     * disabled for this package.
+     *
+     * @hide
+     */
+    public static final int INSTALL_DISABLE_VERIFICATION = 0x00080000;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = {
             DONT_KILL_APP
diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index b2288fc..87568e8 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -16,13 +16,15 @@
 
 package android.ddm;
 
+import android.os.Debug;
+import android.os.UserHandle;
+import android.util.Log;
+
+import dalvik.system.VMRuntime;
+
 import org.apache.harmony.dalvik.ddmc.Chunk;
 import org.apache.harmony.dalvik.ddmc.ChunkHandler;
 import org.apache.harmony.dalvik.ddmc.DdmServer;
-import android.util.Log;
-import android.os.Debug;
-import android.os.UserHandle;
-import dalvik.system.VMRuntime;
 
 import java.nio.ByteBuffer;
 
@@ -35,6 +37,8 @@
     public static final int CHUNK_WAIT = type("WAIT");
     public static final int CHUNK_FEAT = type("FEAT");
 
+    private static final int CLIENT_PROTOCOL_VERSION = 1;
+
     private static DdmHandleHello mInstance = new DdmHandleHello();
 
     private static final String[] FRAMEWORK_FEATURES = new String[] {
@@ -145,7 +149,7 @@
                             + vmFlags.length() * 2
                             + 1);
         out.order(ChunkHandler.CHUNK_ORDER);
-        out.putInt(DdmServer.CLIENT_PROTOCOL_VERSION);
+        out.putInt(CLIENT_PROTOCOL_VERSION);
         out.putInt(android.os.Process.myPid());
         out.putInt(vmIdent.length());
         out.putInt(appName.length());
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 141d33b..1f33693 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -19,6 +19,7 @@
 import android.annotation.MainThread;
 import android.annotation.Nullable;
 import android.annotation.WorkerThread;
+
 import java.util.ArrayDeque;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.Callable;
@@ -70,7 +71,7 @@
  *     protected Long doInBackground(URL... urls) {
  *         int count = urls.length;
  *         long totalSize = 0;
- *         for (int i = 0; i < count; i++) {
+ *         for (int i = 0; i &lt; count; i++) {
  *             totalSize += Downloader.downloadFile(urls[i]);
  *             publishProgress((int) ((i / (float) count) * 100));
  *             // Escape early if cancel() is called
@@ -158,13 +159,22 @@
  * </ul>
  *
  * <h2>Memory observability</h2>
- * <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following
- * operations are safe without explicit synchronizations.</p>
+ * <p>AsyncTask guarantees that all callback calls are synchronized to ensure the following
+ * without explicit synchronizations.</p>
  * <ul>
- *     <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them
- *     in {@link #doInBackground}.
- *     <li>Set member fields in {@link #doInBackground}, and refer to them in
- *     {@link #onProgressUpdate} and {@link #onPostExecute}.
+ *     <li>The memory effects of {@link #onPreExecute}, and anything else
+ *     executed before the call to {@link #execute}, including the construction
+ *     of the AsyncTask object, are visible to {@link #doInBackground}.
+ *     <li>The memory effects of {@link #doInBackground} are visible to
+ *     {@link #onPostExecute}.
+ *     <li>Any memory effects of {@link #doInBackground} preceding a call
+ *     to {@link #publishProgress} are visible to the corresponding
+ *     {@link #onProgressUpdate} call. (But {@link #doInBackground} continues to
+ *     run, and care needs to be taken that later updates in {@link #doInBackground}
+ *     do not interfere with an in-progress {@link #onProgressUpdate} call.)
+ *     <li>Any memory effects preceding a call to {@link #cancel} are visible
+ *     after a call to {@link #isCancelled} that returns true as a result, or
+ *     during and after a resulting call to {@link #onCancelled}.
  * </ul>
  *
  * <h2>Order of execution</h2>
@@ -388,6 +398,10 @@
      * specified parameters are the parameters passed to {@link #execute}
      * by the caller of this task.
      *
+     * This will normally run on a background thread. But to better
+     * support testing frameworks, it is recommended that this also tolerates
+     * direct execution on the foreground thread, as part of the {@link #execute} call.
+     *
      * This method can call {@link #publishProgress} to publish updates
      * on the UI thread.
      *
@@ -404,6 +418,8 @@
 
     /**
      * Runs on the UI thread before {@link #doInBackground}.
+     * Invoked directly by {@link #execute} or {@link #executeOnExecutor}.
+     * The default version does nothing.
      *
      * @see #onPostExecute
      * @see #doInBackground
@@ -414,7 +430,10 @@
 
     /**
      * <p>Runs on the UI thread after {@link #doInBackground}. The
-     * specified result is the value returned by {@link #doInBackground}.</p>
+     * specified result is the value returned by {@link #doInBackground}.
+     * To better support testing frameworks, it is recommended that this be
+     * written to tolerate direct execution as part of the execute() call.
+     * The default version does nothing.</p>
      * 
      * <p>This method won't be invoked if the task was cancelled.</p>
      *
@@ -432,6 +451,7 @@
     /**
      * Runs on the UI thread after {@link #publishProgress} is invoked.
      * The specified values are the values passed to {@link #publishProgress}.
+     * The default version does nothing.
      *
      * @param values The values indicating progress.
      *
@@ -466,7 +486,8 @@
     /**
      * <p>Applications should preferably override {@link #onCancelled(Object)}.
      * This method is invoked by the default implementation of
-     * {@link #onCancelled(Object)}.</p>
+     * {@link #onCancelled(Object)}.
+     * The default version does nothing.</p>
      * 
      * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
      * {@link #doInBackground(Object[])} has finished.</p>
@@ -504,12 +525,16 @@
      * an attempt to stop the task.</p>
      * 
      * <p>Calling this method will result in {@link #onCancelled(Object)} being
-     * invoked on the UI thread after {@link #doInBackground(Object[])}
-     * returns. Calling this method guarantees that {@link #onPostExecute(Object)}
-     * is never invoked. After invoking this method, you should check the
-     * value returned by {@link #isCancelled()} periodically from
-     * {@link #doInBackground(Object[])} to finish the task as early as
-     * possible.</p>
+     * invoked on the UI thread after {@link #doInBackground(Object[])} returns.
+     * Calling this method guarantees that onPostExecute(Object) is never
+     * subsequently invoked, even if <tt>cancel</tt> returns false, but
+     * {@link #onPostExecute} has not yet run.  To finish the
+     * task as early as possible, check {@link #isCancelled()} periodically from
+     * {@link #doInBackground(Object[])}.</p>
+     *
+     * <p>This only requests cancellation. It never waits for a running
+     * background task to terminate, even if <tt>mayInterruptIfRunning</tt> is
+     * true.</p>
      *
      * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
      *        task should be interrupted; otherwise, in-progress tasks are allowed
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 3a5b8a8..27f7e22 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -24,7 +24,8 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
-import android.os.IBinder.DeathRecipient;
+
+import com.android.internal.util.Preconditions;
 
 import java.io.FileDescriptor;
 import java.lang.annotation.Retention;
@@ -127,13 +128,16 @@
             @NonNull BugreportParams params,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull BugreportCallback callback) {
-        // TODO(b/111441001): Enforce android.Manifest.permission.DUMP if necessary.
+        Preconditions.checkNotNull(bugreportFd);
+        Preconditions.checkNotNull(params);
+        Preconditions.checkNotNull(executor);
+        Preconditions.checkNotNull(callback);
         DumpstateListener dsListener = new DumpstateListener(executor, callback);
         try {
             // Note: mBinder can get callingUid from the binder transaction.
             mBinder.startBugreport(-1 /* callingUid */,
                     mContext.getOpPackageName(),
-                    (bugreportFd != null ? bugreportFd.getFileDescriptor() : new FileDescriptor()),
+                    bugreportFd.getFileDescriptor(),
                     (screenshotFd != null
                             ? screenshotFd.getFileDescriptor() : new FileDescriptor()),
                     params.getMode(), dsListener);
@@ -154,8 +158,7 @@
         }
     }
 
-    private final class DumpstateListener extends IDumpstateListener.Stub
-            implements DeathRecipient {
+    private final class DumpstateListener extends IDumpstateListener.Stub {
         private final Executor mExecutor;
         private final BugreportCallback mCallback;
 
@@ -165,11 +168,6 @@
         }
 
         @Override
-        public void binderDied() {
-            // TODO(b/111441001): implement
-        }
-
-        @Override
         public void onProgress(int progress) throws RemoteException {
             final long identity = Binder.clearCallingIdentity();
             try {
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 0743c23..67c8400 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -110,6 +110,16 @@
     public static final String SCAN_VOLUME_CALL = "scan_volume";
 
     /**
+     * Extra used with {@link #SCAN_FILE_CALL} or {@link #SCAN_VOLUME_CALL} to indicate that
+     * the file path originated from shell.
+     *
+     * {@hide}
+     */
+    @TestApi
+    public static final String EXTRA_ORIGINATED_FROM_SHELL =
+            "android.intent.extra.originated_from_shell";
+
+    /**
      * The method name used by the media scanner and mtp to tell the media provider to
      * rescan and reclassify that have become unhidden because of renaming folders or
      * removing nomedia files
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 992b996..0295dc8 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -82,6 +82,7 @@
 import android.sysprop.DisplayProperties;
 import android.text.InputType;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.LayoutDirection;
@@ -126,7 +127,6 @@
 import android.widget.ScrollBarDrawable;
 
 import com.android.internal.R;
-import com.android.internal.util.Preconditions;
 import com.android.internal.view.TooltipPopup;
 import com.android.internal.view.menu.MenuBuilder;
 import com.android.internal.widget.ScrollBarUtils;
@@ -813,6 +813,8 @@
      */
     private static final String CONTENT_CAPTURE_LOG_TAG = "View.ContentCapture";
 
+    private static final boolean DEBUG_CONTENT_CAPTURE = false;
+
     /**
      * When set to true, this view will save its attribute data.
      *
@@ -3393,9 +3395,12 @@
      * Masks for mPrivateFlags4, as generated by dumpFlags():
      *
      * |-------|-------|-------|-------|
-     *                              1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK
-     *                             1     PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED
-     *                            1      PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED
+     *                             1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK
+     *                            1     PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED
+     *                           1      PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED
+     *                          1       PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED
+     *                         1        PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE
+     *                         11       PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK
      * |-------|-------|-------|-------|
      */
 
@@ -3422,6 +3427,17 @@
     private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED = 0x10;
     private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED = 0x20;
 
+    /*
+     * Flags used to cache the value returned by isImportantForContentCapture while the view
+     * hierarchy is being traversed.
+     */
+    private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED = 0x40;
+    private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE = 0x80;
+
+    private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK =
+            PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED
+            | PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
+
     /* End of masks for mPrivateFlags4 */
 
     /** @hide */
@@ -4140,9 +4156,13 @@
      * The layout parameters associated with this view and used by the parent
      * {@link android.view.ViewGroup} to determine how this view should be
      * laid out.
+     *
+     * The field should not be used directly. Instead {@link #getLayoutParams()} and {@link
+     * #setLayoutParams(ViewGroup.LayoutParams)} should be used. The setter guarantees internal
+     * state correctness of the class.
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     protected ViewGroup.LayoutParams mLayoutParams;
 
     /**
@@ -5042,12 +5062,17 @@
      * view (through {@link #setContentCaptureSession(ContentCaptureSession)}.
      */
     @Nullable
-    private WeakReference<ContentCaptureSession> mContentCaptureSession;
+    private ContentCaptureSession mContentCaptureSession;
 
     @LayoutRes
     private int mSourceLayoutId = ID_NULL;
 
     /**
+     * Cached reference to the {@link ContentCaptureSession}, is reset on {@link #invalidate()}.
+     */
+    private ContentCaptureSession mCachedContentCaptureSession;
+
+    /**
      * Simple constructor to use when creating a view from code.
      *
      * @param context The Context the view is running in, through which it can
@@ -8233,15 +8258,7 @@
      * </ul>
      */
     public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
-        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
-            Trace.traceBegin(Trace.TRACE_TAG_VIEW,
-                    "onProvideContentCaptureStructure() for " + getClass().getSimpleName());
-        }
-        try {
-            onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags);
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-        }
+        onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags);
     }
 
     /** @hide */
@@ -8952,6 +8969,27 @@
      * @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
      */
     public final boolean isImportantForContentCapture() {
+        boolean isImportant;
+        if ((mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED) != 0) {
+            isImportant = (mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE) != 0;
+            return isImportant;
+        }
+
+        isImportant = calculateIsImportantForContentCapture();
+
+        mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
+        if (isImportant) {
+            mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
+        }
+        mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED;
+        return isImportant;
+    }
+
+    /**
+     * Calculates whether the flag is important for content capture so it can be used by
+     * {@link #isImportantForContentCapture()} while the tree is traversed.
+     */
+    private boolean calculateIsImportantForContentCapture() {
         // Check parent mode to ensure we're important
         ViewParent parent = mParent;
         while (parent instanceof View) {
@@ -8992,9 +9030,6 @@
         }
 
         // View group is important if at least one children also is
-        //TODO(b/111276913): decide if we really need to send the relevant parents or just the
-        // leaves (with absolute coordinates). If it's the latter, then we need to update this
-        // javadoc and ViewGroup's implementation.
         if (this instanceof ViewGroup) {
             final ViewGroup group = (ViewGroup) this;
             for (int i = 0; i < group.getChildCount(); i++) {
@@ -9031,6 +9066,10 @@
      * </ol>
      */
     private void notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared) {
+        AttachInfo ai = mAttachInfo;
+        // Skip it while the view is being laided out for the first time
+        if (ai != null && !ai.mReadyForContentCaptureUpdates) return;
+
         if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
             Trace.traceBegin(Trace.TRACE_TAG_VIEW,
                     "notifyContentCapture(" + appeared + ") for " + getClass().getSimpleName());
@@ -9043,24 +9082,27 @@
     }
 
     private void notifyAppearedOrDisappearedForContentCaptureIfNeededNoTrace(boolean appeared) {
+        AttachInfo ai = mAttachInfo;
+
         // First check if context has client, so it saves a service lookup when it doesn't
         if (!mContext.isContentCaptureSupported()) return;
 
         // Then check if it's enabled in the context...
-        final ContentCaptureManager ccm = mContext.getSystemService(ContentCaptureManager.class);
+        final ContentCaptureManager ccm = ai != null ? ai.getContentCaptureManager(mContext)
+                : mContext.getSystemService(ContentCaptureManager.class);
         if (ccm == null || !ccm.isContentCaptureEnabled()) return;
 
         // ... and finally at the view level
         // NOTE: isImportantForContentCapture() is more expensive than cm.isContentCaptureEnabled()
         if (!isImportantForContentCapture()) return;
 
-        final ContentCaptureSession session = getContentCaptureSession(ccm);
+        ContentCaptureSession session = getContentCaptureSession();
         if (session == null) return;
 
         if (appeared) {
             if (!isLaidOut() || getVisibility() != VISIBLE
                     || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) {
-                if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+                if (DEBUG_CONTENT_CAPTURE) {
                     Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'appeared' on " + this + ": laid="
                             + isLaidOut() + ", visibleToUser=" + isVisibleToUser()
                             + ", visible=" + (getVisibility() == VISIBLE)
@@ -9071,8 +9113,12 @@
                 }
                 return;
             }
-            mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
-            mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
+            setNotifiedContentCaptureAppeared();
+
+            // TODO(b/123307965): instead of post, we should queue it on AttachInfo and then
+            // dispatch on RootImpl, as we're doing with the removed ones (in that case, we should
+            // merge the delayNotifyContentCaptureDisappeared() into a more generic method that
+            // takes a session and a command, where the command is either view added or removed
 
             // The code below doesn't take much for a unique view, but it's called for all views
             // the first time the view hiearchy is laid off, which could acccumulative delay the
@@ -9086,7 +9132,7 @@
         } else {
             if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0
                     || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) {
-                if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+                if (DEBUG_CONTENT_CAPTURE) {
                     Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'disappeared' on " + this + ": laid="
                             + isLaidOut() + ", visibleToUser=" + isVisibleToUser()
                             + ", visible=" + (getVisibility() == VISIBLE)
@@ -9099,11 +9145,24 @@
             }
             mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
             mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
-            Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT,
-                    () -> session.notifyViewDisappeared(getAutofillId()), /* token= */ null);
+
+            if (ai != null) {
+                ai.delayNotifyContentCaptureDisappeared(session, getAutofillId());
+            } else {
+                if (DEBUG_CONTENT_CAPTURE) {
+                    Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on gone for " + this);
+                }
+                Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT,
+                        () -> session.notifyViewDisappeared(getAutofillId()), /* token= */ null);
+            }
         }
     }
 
+    private void setNotifiedContentCaptureAppeared() {
+        mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
+        mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
+    }
+
     /**
      * Sets the (optional) {@link ContentCaptureSession} associated with this view.
      *
@@ -9127,9 +9186,8 @@
      * {@link ContentCaptureSession#createContentCaptureSession(
      *        android.view.contentcapture.ContentCaptureContext)}.
      */
-    public void setContentCaptureSession(@NonNull ContentCaptureSession contentCaptureSession) {
-        mContentCaptureSession = new WeakReference<>(
-                Preconditions.checkNotNull(contentCaptureSession));
+    public void setContentCaptureSession(@Nullable ContentCaptureSession contentCaptureSession) {
+        mContentCaptureSession = contentCaptureSession;
     }
 
     /**
@@ -9141,8 +9199,18 @@
      */
     @Nullable
     public final ContentCaptureSession getContentCaptureSession() {
+        if (mCachedContentCaptureSession != null) {
+            return mCachedContentCaptureSession;
+        }
+
+        mCachedContentCaptureSession = getAndCacheContentCaptureSession();
+        return mCachedContentCaptureSession;
+    }
+
+    @Nullable
+    private ContentCaptureSession getAndCacheContentCaptureSession() {
         // First try the session explicitly set by setContentCaptureSession()
-        if (mContentCaptureSession != null) return mContentCaptureSession.get();
+        if (mContentCaptureSession != null) return mContentCaptureSession;
 
         // Then the session explicitly set in an ancestor
         ContentCaptureSession session = null;
@@ -9159,21 +9227,6 @@
         return session;
     }
 
-    /**
-     * Optimized version of {@link #getContentCaptureSession()} that avoids a service lookup.
-     */
-    @Nullable
-    private ContentCaptureSession getContentCaptureSession(@NonNull ContentCaptureManager ccm) {
-        if (mContentCaptureSession != null) return mContentCaptureSession.get();
-
-        ContentCaptureSession session = null;
-        if (mParent instanceof View) {
-            session = ((View) mParent).getContentCaptureSession(ccm);
-        }
-
-        return session != null ? session : ccm.getMainContentCaptureSession();
-    }
-
     @Nullable
     private AutofillManager getAutofillManager() {
         return mContext.getSystemService(AutofillManager.class);
@@ -9346,6 +9399,62 @@
     }
 
     /**
+     * Dispatches the initial Content Capture events for a view structure.
+     *
+     * @hide
+     */
+    public void dispatchInitialProvideContentCaptureStructure(@NonNull ContentCaptureManager ccm) {
+        AttachInfo ai = mAttachInfo;
+        if (ai == null) {
+            Log.w(CONTENT_CAPTURE_LOG_TAG,
+                    "dispatchProvideContentCaptureStructure(): no AttachInfo for " + this);
+            return;
+        }
+
+        // We must set it before checkign if the view itself is important, because it might
+        // initially not be (for example, if it's empty), although that might change later (for
+        // example, if important views are added)
+        ai.mReadyForContentCaptureUpdates = true;
+
+        if (!isImportantForContentCapture()) {
+            if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.DEBUG)) {
+                Log.d(CONTENT_CAPTURE_LOG_TAG,
+                        "dispatchProvideContentCaptureStructure(): decorView is not important");
+            }
+            return;
+        }
+
+        ai.mContentCaptureManager = ccm;
+
+        ContentCaptureSession session = getContentCaptureSession();
+        if (session == null) {
+            if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.DEBUG)) {
+                Log.d(CONTENT_CAPTURE_LOG_TAG,
+                        "dispatchProvideContentCaptureStructure(): no session for " + this);
+            }
+            return;
+        }
+
+        session.internalNotifyViewHierarchyEvent(/* started= */ true);
+        try {
+            dispatchProvideContentCaptureStructure();
+        } finally {
+            session.internalNotifyViewHierarchyEvent(/* started= */ false);
+        }
+    }
+
+    /** @hide */
+    void dispatchProvideContentCaptureStructure() {
+        ContentCaptureSession session = getContentCaptureSession();
+        if (session != null) {
+            ViewStructure structure = session.newViewStructure(this);
+            onProvideContentCaptureStructure(structure, /* flags= */ 0);
+            setNotifiedContentCaptureAppeared();
+            session.notifyViewAppeared(structure);
+        }
+    }
+
+    /**
      * @see #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
      *
      * Note: Called from the default {@link AccessibilityDelegate}.
@@ -17457,6 +17566,10 @@
             return;
         }
 
+        // Reset content capture caches
+        mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
+        mCachedContentCaptureSession = null;
+
         if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
                 || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
                 || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
@@ -27963,6 +28076,23 @@
         View mTooltipHost;
 
         /**
+         * The initial structure has been reported so the view is ready to report updates.
+         */
+        boolean mReadyForContentCaptureUpdates;
+
+        /**
+         * Map of ids (per session) that need to be notified after as gone the view hierchy is
+         * traversed.
+         */
+        // TODO(b/121197119): use SparseArray once session id becomes integer
+        ArrayMap<String, ArrayList<AutofillId>> mContentCaptureRemovedIds;
+
+        /**
+         * Cached reference to the {@link ContentCaptureManager}.
+         */
+        ContentCaptureManager mContentCaptureManager;
+
+        /**
          * Creates a new set of attachment information with the specified
          * events handler and thread.
          *
@@ -27980,6 +28110,31 @@
             mRootCallbacks = effectPlayer;
             mTreeObserver = new ViewTreeObserver(context);
         }
+
+        private void delayNotifyContentCaptureDisappeared(@NonNull ContentCaptureSession session,
+                @NonNull AutofillId id) {
+            if (mContentCaptureRemovedIds == null) {
+                // Most of the time there will be just one session, so intial capacity is 1
+                mContentCaptureRemovedIds = new ArrayMap<>(1);
+            }
+            String sessionId = session.getId();
+            // TODO: life would be much easier if we provided a MultiMap implementation somwhere...
+            ArrayList<AutofillId> ids = mContentCaptureRemovedIds.get(sessionId);
+            if (ids == null) {
+                ids = new ArrayList<>();
+                mContentCaptureRemovedIds.put(sessionId, ids);
+            }
+            ids.add(id);
+        }
+
+        @Nullable
+        private ContentCaptureManager getContentCaptureManager(@NonNull Context context) {
+            if (mContentCaptureManager != null) {
+                return mContentCaptureManager;
+            }
+            mContentCaptureManager = context.getSystemService(ContentCaptureManager.class);
+            return mContentCaptureManager;
+        }
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 0986cfa..e0e7843 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3603,7 +3603,7 @@
             return;
         }
 
-        final ChildListForAutoFill children = getChildrenForAutofill(flags);
+        final ChildListForAutoFillOrContentCapture children = getChildrenForAutofill(flags);
         final int childrenCount = children.size();
         structure.setChildCount(childrenCount);
         for (int i = 0; i < childrenCount; i++) {
@@ -3614,13 +3614,31 @@
         children.recycle();
     }
 
+    /** @hide */
+    @Override
+    public void dispatchProvideContentCaptureStructure() {
+        super.dispatchProvideContentCaptureStructure();
+
+        if (!isLaidOut()) return;
+
+        final ChildListForAutoFillOrContentCapture children = getChildrenForContentCapture();
+        final int childrenCount = children.size();
+        for (int i = 0; i < childrenCount; i++) {
+            final View child = children.get(i);
+            child.dispatchProvideContentCaptureStructure();
+        }
+        children.recycle();
+    }
+
     /**
      * Gets the children for autofill. Children for autofill are the first
      * level descendants that are important for autofill. The returned
      * child list object is pooled and the caller must recycle it once done.
      * @hide */
-    private @NonNull ChildListForAutoFill getChildrenForAutofill(@AutofillFlags int flags) {
-        final ChildListForAutoFill children = ChildListForAutoFill.obtain();
+    private @NonNull ChildListForAutoFillOrContentCapture getChildrenForAutofill(
+            @AutofillFlags int flags) {
+        final ChildListForAutoFillOrContentCapture children = ChildListForAutoFillOrContentCapture
+                .obtain();
         populateChildrenForAutofill(children, flags);
         return children;
     }
@@ -3647,6 +3665,34 @@
         }
     }
 
+    private @NonNull ChildListForAutoFillOrContentCapture getChildrenForContentCapture() {
+        final ChildListForAutoFillOrContentCapture children = ChildListForAutoFillOrContentCapture
+                .obtain();
+        populateChildrenForContentCapture(children);
+        return children;
+    }
+
+    /** @hide */
+    private void populateChildrenForContentCapture(ArrayList<View> list) {
+        final int childrenCount = mChildrenCount;
+        if (childrenCount <= 0) {
+            return;
+        }
+        final ArrayList<View> preorderedList = buildOrderedChildList();
+        final boolean customOrder = preorderedList == null
+                && isChildrenDrawingOrderEnabled();
+        for (int i = 0; i < childrenCount; i++) {
+            final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
+            final View child = (preorderedList == null)
+                    ? mChildren[childIndex] : preorderedList.get(childIndex);
+            if (child.isImportantForContentCapture()) {
+                list.add(child);
+            } else if (child instanceof ViewGroup) {
+                ((ViewGroup) child).populateChildrenForContentCapture(list);
+            }
+        }
+    }
+
     private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children,
             int childIndex) {
         final View child;
@@ -8544,16 +8590,16 @@
     /**
      * Pooled class that to hold the children for autifill.
      */
-    static class ChildListForAutoFill extends ArrayList<View> {
+    private static class ChildListForAutoFillOrContentCapture extends ArrayList<View> {
         private static final int MAX_POOL_SIZE = 32;
 
-        private static final Pools.SimplePool<ChildListForAutoFill> sPool =
+        private static final Pools.SimplePool<ChildListForAutoFillOrContentCapture> sPool =
                 new Pools.SimplePool<>(MAX_POOL_SIZE);
 
-        public static ChildListForAutoFill obtain() {
-            ChildListForAutoFill list = sPool.acquire();
+        public static ChildListForAutoFillOrContentCapture obtain() {
+            ChildListForAutoFillOrContentCapture list = sPool.acquire();
             if (list == null) {
-                list = new ChildListForAutoFill();
+                list = new ChildListForAutoFillOrContentCapture();
             }
             return list;
         }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 61fb38f..47528a0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -103,7 +103,10 @@
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.Interpolator;
+import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
+import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.MainContentCaptureSession;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Scroller;
 
@@ -154,6 +157,7 @@
     private static final boolean DEBUG_FPS = false;
     private static final boolean DEBUG_INPUT_STAGES = false || LOCAL_LOGV;
     private static final boolean DEBUG_KEEP_SCREEN_ON = false || LOCAL_LOGV;
+    private static final boolean DEBUG_CONTENT_CAPTURE = false || LOCAL_LOGV;
 
     /**
      * Set to false if we do not want to use the multi threaded renderer even though
@@ -412,6 +416,7 @@
     boolean mApplyInsetsRequested;
     boolean mLayoutRequested;
     boolean mFirst;
+    boolean mPerformContentCapture;
     boolean mReportNextDraw;
     boolean mFullRedrawNeeded;
     boolean mNewSurfaceNeeded;
@@ -608,6 +613,7 @@
         mTransparentRegion = new Region();
         mPreviousTransparentRegion = new Region();
         mFirst = true; // true for the first time the view is added
+        mPerformContentCapture = true; // also true for the first time the view is added
         mAdded = false;
         mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
                 context);
@@ -2756,6 +2762,24 @@
             }
         }
 
+        if (mAttachInfo.mContentCaptureRemovedIds != null) {
+            MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
+                    .getMainContentCaptureSession();
+            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureViewsGone");
+            try {
+                for (int i = 0; i < mAttachInfo.mContentCaptureRemovedIds.size(); i++) {
+                    String sessionId = mAttachInfo.mContentCaptureRemovedIds
+                            .keyAt(i);
+                    ArrayList<AutofillId> ids = mAttachInfo.mContentCaptureRemovedIds
+                            .valueAt(i);
+                    mainSession.notifyViewsDisappeared(sessionId, ids);
+                }
+                mAttachInfo.mContentCaptureRemovedIds = null;
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+            }
+        }
+
         mIsInTraversal = false;
     }
 
@@ -3451,6 +3475,35 @@
                 pendingDrawFinished();
             }
         }
+        if (mPerformContentCapture) {
+            performContentCapture();
+        }
+    }
+
+    private void performContentCapture() {
+        mPerformContentCapture = false; // One-time offer!
+        final View rootView = mView;
+        if (DEBUG_CONTENT_CAPTURE) {
+            Log.v(mTag, "dispatchContentCapture() on " + rootView);
+        }
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for "
+                    + getClass().getSimpleName());
+        }
+        try {
+            // First check if context supports it, so it saves a service lookup when it doesn't
+            if (!mContext.isContentCaptureSupported()) return;
+
+            // Then check if it's enabled in the contex itself.
+            final ContentCaptureManager ccm = mContext
+                    .getSystemService(ContentCaptureManager.class);
+            if (ccm == null || !ccm.isContentCaptureEnabled()) return;
+
+            // Content capture is a go!
+            rootView.dispatchInitialProvideContentCaptureStructure(ccm);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+        }
     }
 
     private boolean draw(boolean fullRedrawNeeded) {
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 63c21f3..acb81e0 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -93,6 +93,11 @@
     }
 
     @Override
+    public void internalNotifyViewHierarchyEvent(boolean started) {
+        getMainCaptureSession().notifyInitialViewHierarchyEvent(mId, started);
+    }
+
+    @Override
     boolean isContentCaptureEnabled() {
         return getMainCaptureSession().isContentCaptureEnabled();
     }
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 205ad30..dfac35d 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -71,13 +71,33 @@
      */
     public static final int TYPE_VIEW_TEXT_CHANGED = 3;
 
-    // TODO(b/111276913): add event to indicate when FLAG_SECURE was changed?
+    /**
+     * Called before events (such as {@link #TYPE_VIEW_APPEARED}) representing the initial view
+     * hierarchy are sent.
+     *
+     * <p><b>NOTE</b>: there is no guarantee this event will be sent. For example, it's not sent
+     * if the initial view hierarchy doesn't initially have any view that's important for content
+     * capture.
+     */
+    public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4;
+
+    /**
+     * Called after events (such as {@link #TYPE_VIEW_APPEARED}) representing the initial view
+     * hierarchy are sent.
+     *
+     * <p><b>NOTE</b>: there is no guarantee this event will be sent. For example, it's not sent
+     * if the initial view hierarchy doesn't initially have any view that's important for content
+     * capture.
+     */
+    public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5;
 
     /** @hide */
     @IntDef(prefix = { "TYPE_" }, value = {
             TYPE_VIEW_APPEARED,
             TYPE_VIEW_DISAPPEARED,
-            TYPE_VIEW_TEXT_CHANGED
+            TYPE_VIEW_TEXT_CHANGED,
+            TYPE_INITIAL_VIEW_TREE_APPEARING,
+            TYPE_INITIAL_VIEW_TREE_APPEARED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType{}
@@ -110,8 +130,10 @@
         return this;
     }
 
-    private void setAutofillIds(@NonNull ArrayList<AutofillId> ids) {
+    /** @hide */
+    public ContentCaptureEvent setAutofillIds(@NonNull ArrayList<AutofillId> ids) {
         mIds = Preconditions.checkNotNull(ids);
+        return this;
     }
 
     /**
@@ -195,7 +217,8 @@
      * Gets the type of the event.
      *
      * @return one of {@link #TYPE_VIEW_APPEARED}, {@link #TYPE_VIEW_DISAPPEARED},
-     * or {@link #TYPE_VIEW_TEXT_CHANGED}.
+     * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING}, or
+     * {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}.
      */
     public @EventType int getType() {
         return mType;
@@ -374,6 +397,10 @@
                 return "VIEW_DISAPPEARED";
             case TYPE_VIEW_TEXT_CHANGED:
                 return "VIEW_TEXT_CHANGED";
+            case TYPE_INITIAL_VIEW_TREE_APPEARING:
+                return "INITIAL_VIEW_HIERARCHY_STARTED";
+            case TYPE_INITIAL_VIEW_TREE_APPEARED:
+                return "INITIAL_VIEW_HIERARCHY_FINISHED";
             default:
                 return "UKNOWN_TYPE: " + type;
         }
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 68a3e8a..e028961 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -204,6 +204,12 @@
         return mId.hashCode();
     }
 
+    /** @hide */
+    @NonNull
+    public String getId() {
+        return mId;
+    }
+
     /**
      * Creates a new {@link ContentCaptureSession}.
      *
@@ -362,6 +368,9 @@
     abstract void internalNotifyViewTextChanged(@NonNull AutofillId id,
             @Nullable CharSequence text);
 
+    /** @hide */
+    public abstract void internalNotifyViewHierarchyEvent(boolean started);
+
     /**
      * Creates a {@link ViewStructure} for a "standard" view.
      *
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 034c8fa..0605fa3 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -15,6 +15,8 @@
  */
 package android.view.contentcapture;
 
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARING;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
 import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
@@ -66,6 +68,8 @@
 
     private static final String TAG = MainContentCaptureSession.class.getSimpleName();
 
+    private static final boolean FORCE_FLUSH = true;
+
     /**
      * Handler message used to flush the buffer.
      */
@@ -270,6 +274,10 @@
         }
     }
 
+    private void handleSendEvent(@NonNull ContentCaptureEvent event) {
+        handleSendEvent(event, /* forceFlush= */ false);
+    }
+
     private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
         final int eventType = event.getType();
         if (VERBOSE) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
@@ -518,6 +526,11 @@
     }
 
     @Override
+    public void internalNotifyViewHierarchyEvent(boolean started) {
+        notifyInitialViewHierarchyEvent(mId, started);
+    }
+
+    @Override
     boolean isContentCaptureEnabled() {
         return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
     }
@@ -536,33 +549,57 @@
                 new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
                         .setParentSessionId(parentSessionId)
                         .setClientContext(clientContext),
-                        /* forceFlush= */ true));
+                        FORCE_FLUSH));
     }
 
     void notifyChildSessionFinished(@NonNull String parentSessionId,
             @NonNull String childSessionId) {
         mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
                 new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
-                        .setParentSessionId(parentSessionId), /* forceFlush= */ true));
+                        .setParentSessionId(parentSessionId), FORCE_FLUSH));
     }
 
     void notifyViewAppeared(@NonNull String sessionId, @NonNull ViewStructureImpl node) {
         mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
-                new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED)
-                        .setViewNode(node.mNode), /* forceFlush= */ false));
+                new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED).setViewNode(node.mNode)));
     }
 
     void notifyViewDisappeared(@NonNull String sessionId, @NonNull AutofillId id) {
         mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
-                new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id),
-                        /* forceFlush= */ false));
+                new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id)));
+    }
+
+    /** @hide */
+    public void notifyViewsDisappeared(@NonNull String sessionId,
+            @NonNull ArrayList<AutofillId> ids) {
+        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED);
+        if (ids.size() == 1) {
+            event.setAutofillId(ids.get(0));
+        } else {
+            event.setAutofillIds(ids);
+        }
+
+        mHandler.sendMessage(
+                obtainMessage(MainContentCaptureSession::handleSendEvent, this, event));
     }
 
     void notifyViewTextChanged(@NonNull String sessionId, @NonNull AutofillId id,
             @Nullable CharSequence text) {
         mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
                 new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED).setAutofillId(id)
-                        .setText(text), /* forceFlush= */ false));
+                        .setText(text)));
+    }
+
+    void notifyInitialViewHierarchyEvent(@NonNull String sessionId, boolean started) {
+        if (started) {
+            mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
+                    new ContentCaptureEvent(sessionId, TYPE_INITIAL_VIEW_TREE_APPEARING)));
+        } else {
+            mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
+                    new ContentCaptureEvent(sessionId, TYPE_INITIAL_VIEW_TREE_APPEARED),
+                            FORCE_FLUSH));
+
+        }
     }
 
     @Override
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index cd80d53..429c618 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -22,6 +22,7 @@
 import android.view.ContextThemeWrapper;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
+import android.view.contentcapture.ContentCaptureManager;
 
 import java.lang.ref.WeakReference;
 
@@ -36,6 +37,7 @@
     private PhoneWindow mPhoneWindow;
     private WindowManager mWindowManager;
     private Resources mActivityResources;
+    private ContentCaptureManager mContentCaptureManager;
 
     private WeakReference<Context> mActivityContext;
 
@@ -60,6 +62,16 @@
             }
             return mWindowManager;
         }
+        if (Context.CONTENT_CAPTURE_MANAGER_SERVICE.equals(name)) {
+            if (mContentCaptureManager == null) {
+                Context activityContext = mActivityContext.get();
+                if (activityContext != null) {
+                    mContentCaptureManager = (ContentCaptureManager) activityContext
+                            .getSystemService(name);
+                }
+            }
+            return mContentCaptureManager;
+        }
         return super.getSystemService(name);
     }
 
@@ -79,4 +91,9 @@
     public AssetManager getAssets() {
         return mActivityResources.getAssets();
     }
+
+    @Override
+    public boolean isContentCaptureSupported() {
+        return true;
+    }
 }
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 49598bb..d69d416 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -230,6 +230,11 @@
 extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
 extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
 
+// Namespace for Android Runtime flags applied during boot time.
+static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot";
+// Feature flag name for Garbage Collector type.
+static const char* GCTYPE = "gctype";
+
 static AndroidRuntime* gCurRuntime = NULL;
 
 /*
@@ -776,7 +781,9 @@
     }
 
     std::string gc_type_override =
-            server_configurable_flags::GetServerConfigurableFlag("runtime_native", "gctype", "");
+            server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
+                                                                 GCTYPE,
+                                                                 /*default_value=*/ "");
     std::string gc_type_override_temp;
     if (gc_type_override.empty()) {
         parseRuntimeOption("dalvik.vm.gctype", gctypeOptsBuf, "-Xgc:");
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 6a81050..40ebd44 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -36,12 +36,11 @@
     frameworks-core-util-lib \
     mockwebserver \
     guava \
-    androidx.test.runner \
-    androidx.test.ext.junit \
-    androidx.test.rules \
     androidx.test.espresso.core \
+    androidx.test.ext.junit \
+    androidx.test.runner \
+    androidx.test.rules \
     mockito-target-minus-junit4 \
-    espresso-core \
     ub-uiautomator \
     platform-test-annotations \
     truth-prebuilt \
diff --git a/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java b/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java
index be1d44c..c71bc73 100644
--- a/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java
@@ -16,22 +16,28 @@
 
 package android.animation;
 
-import android.test.ActivityInstrumentationTestCase2;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
 
 import com.android.frameworks.coretests.R;
 
+import org.junit.Rule;
+import org.junit.Test;
+
 import java.util.HashSet;
 import java.util.Set;
 
 @LargeTest
-public class AnimatorInflaterTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity>  {
-    Set<Integer> identityHashes = new HashSet<Integer>();
+public class AnimatorInflaterTest {
+    @Rule
+    public final ActivityTestRule<BasicAnimatorActivity> mActivityRule =
+            new ActivityTestRule<>(BasicAnimatorActivity.class);
 
-    public AnimatorInflaterTest() {
-        super(BasicAnimatorActivity.class);
-    }
+    private final Set<Integer> mIdentityHashes = new HashSet<>();
 
     private void assertUnique(Object object) {
         assertUnique(object, "");
@@ -39,15 +45,16 @@
 
     private void assertUnique(Object object, String msg) {
         final int code = System.identityHashCode(object);
-        assertTrue("object should be unique " + msg + ", obj:" + object, identityHashes.add(code));
-
+        assertTrue("object should be unique " + msg + ", obj:" + object, mIdentityHashes.add(code));
     }
 
+    @Test
     public void testLoadStateListAnimator() {
-        StateListAnimator sla1 = AnimatorInflater.loadStateListAnimator(getActivity(),
+        final BasicAnimatorActivity activity = mActivityRule.getActivity();
+        StateListAnimator sla1 = AnimatorInflater.loadStateListAnimator(activity,
                 R.anim.test_state_anim);
-        sla1.setTarget(getActivity().mAnimatingButton);
-        StateListAnimator sla2 = AnimatorInflater.loadStateListAnimator(getActivity(),
+        sla1.setTarget(activity.mAnimatingButton);
+        StateListAnimator sla2 = AnimatorInflater.loadStateListAnimator(activity,
                 R.anim.test_state_anim);
         assertNull(sla2.getTarget());
         for (StateListAnimator sla : new StateListAnimator[]{sla1, sla2}) {
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
index 55837ba..7a1de0c 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
@@ -16,27 +16,38 @@
 
 package android.animation;
 
-import android.test.ActivityInstrumentationTestCase2;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
 import android.view.View;
 
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
 
 import com.android.frameworks.coretests.R;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
 import java.util.ArrayList;
 
-public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
+@SmallTest
+public class AnimatorSetActivityTest {
+
+    @Rule
+    public final ActivityTestRule<AnimatorSetActivity> mActivityRule =
+            new ActivityTestRule<>(AnimatorSetActivity.class);
 
     private static final long POLL_INTERVAL = 100; // ms
     private AnimatorSetActivity mActivity;
     private ObjectAnimator a1,a2,a3;
     private ValueAnimator a4,a5;
 
-    public AnimatorSetActivityTest() {
-        super(AnimatorSetActivity.class);
-    }
-
     static class MyListener implements Animator.AnimatorListener {
         boolean startIsCalled = false;
         boolean endIsCalled = false;
@@ -63,10 +74,9 @@
         }
     }
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
-        mActivity = getActivity();
+        mActivity = mActivityRule.getActivity();
 
         View square1 = mActivity.findViewById(R.id.square1);
         View square2 = mActivity.findViewById(R.id.square2);
@@ -78,7 +88,7 @@
         a5 = ValueAnimator.ofFloat(10f, 5f).setDuration(850);
     }
 
-    @Override
+    @After
     public void tearDown() throws Exception {
         mActivity = null;
         a1 = null;
@@ -86,10 +96,9 @@
         a3 = null;
         a4 = null;
         a5 = null;
-        super.tearDown();
     }
 
-    @SmallTest
+    @Test
     public void testGetChildAnimations() {
         AnimatorSet s1 = new AnimatorSet();
         s1.playTogether(a1, a2, a3);
@@ -129,7 +138,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testTotalDuration() {
         ArrayList<Animator> list = getAnimatorList();
 
@@ -192,7 +201,7 @@
 
     }
 
-    @SmallTest
+    @Test
     public void testGetDuration() {
         AnimatorSet s = new AnimatorSet();
         assertTrue(s.getDuration() < 0);
@@ -205,8 +214,8 @@
 
     }
 
-    @SmallTest
     @UiThreadTest
+    @Test
     public void testSetDuration() {
         AnimatorSet s = getSequentialSet();
         assertTrue(s.getDuration() < 0);
@@ -224,7 +233,7 @@
         assertEquals(duration, a5.getDuration());
     }
 
-    @SmallTest
+    @Test
     public void testAddListener() throws InterruptedException {
         // Verify that the listener is added to the list of listeners in the AnimatorSet
         // and that newly added listener gets callback for lifecycle events of the animator
@@ -241,13 +250,10 @@
         assertFalse(listener.endIsCalled);
 
         try {
-            runTestOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    s.start();
-                    assertTrue(listener.startIsCalled);
-                    assertFalse(listener.endIsCalled);
-                }
+            mActivityRule.runOnUiThread(() -> {
+                s.start();
+                assertTrue(listener.startIsCalled);
+                assertFalse(listener.endIsCalled);
             });
         } catch (Throwable throwable) {
             throwable.printStackTrace();
@@ -258,18 +264,13 @@
         assertTrue(listener.endIsCalled);
     }
 
-    @SmallTest
+    @Test
     public void testRemoveListener() throws Throwable {
         final AnimatorSet s = new AnimatorSet();
         s.playTogether(a1, a2, a3, a4);
         MyListener listener = new MyListener();
         s.addListener(listener);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                s.start();
-            }
-        });
+        mActivityRule.runOnUiThread(s::start);
 
         Thread.sleep(s.getTotalDuration() + 100);
         assertTrue(listener.startIsCalled);
@@ -282,18 +283,13 @@
         listener.startIsCalled = false;
         listener.endIsCalled = false;
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                s.start();
-            }
-        });
+        mActivityRule.runOnUiThread(s::start);
         Thread.sleep(s.getTotalDuration() + 100);
         assertFalse(listener.startIsCalled);
         assertFalse(listener.endIsCalled);
     }
 
-    @SmallTest
+    @Test
     public void testEnd() throws Throwable {
         // End animator set
         final AnimatorSet s = new AnimatorSet();
@@ -301,37 +297,30 @@
         final MyListener listener = new MyListener();
         s.addListener(listener);
         assertFalse(listener.endIsCalled);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                s.start();
-                assertTrue(s.isStarted());
-                assertTrue(listener.startIsCalled);
-                assertFalse(listener.endIsCalled);
-            }
+        mActivityRule.runOnUiThread(() -> {
+            s.start();
+            assertTrue(s.isStarted());
+            assertTrue(listener.startIsCalled);
+            assertFalse(listener.endIsCalled);
         });
 
         Thread.sleep(a2.getTotalDuration());
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                s.end();
-                assertTrue(listener.startIsCalled);
-                assertTrue(listener.endIsCalled);
-                assertFalse(s.isRunning());
-                assertFalse(s.isStarted());
+        mActivityRule.runOnUiThread(() -> {
+            s.end();
+            assertTrue(listener.startIsCalled);
+            assertTrue(listener.endIsCalled);
+            assertFalse(s.isRunning());
+            assertFalse(s.isStarted());
 
-                assertFalse(a1.isStarted());
-                assertFalse(a2.isStarted());
-                assertFalse(a3.isStarted());
-                assertFalse(a4.isStarted());
-            }
+            assertFalse(a1.isStarted());
+            assertFalse(a2.isStarted());
+            assertFalse(a3.isStarted());
+            assertFalse(a4.isStarted());
         });
-
     }
 
-    @SmallTest
+    @Test
     public void testStart() throws Throwable {
         final AnimatorSet s = new AnimatorSet();
         ArrayList<Animator> animators = getAnimatorList();
@@ -355,12 +344,9 @@
             assertFalse(l.endIsCalled);
         }
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                s.start();
-                assertTrue(l.startIsCalled);
-            }
+        mActivityRule.runOnUiThread(() -> {
+            s.start();
+            assertTrue(l.startIsCalled);
         });
 
         long timeout = s.getTotalDuration() * 2;
@@ -383,7 +369,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testCancel() throws Throwable {
         // Check whether cancel would trigger onAnimationCanceled and cancel all the unfinished
         // animations
@@ -411,42 +397,33 @@
             assertFalse(l.endIsCalled);
         }
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                s.start();
-            }
-        });
+        mActivityRule.runOnUiThread(s::start);
 
         Thread.sleep(a1.getTotalDuration());
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertTrue(s.isStarted());
-                ArrayList<Integer> runningAnimIds = new ArrayList<Integer>();
-                for (int i = 0; i < animators.size(); i++) {
-                    if (animators.get(i).isStarted()) {
-                        runningAnimIds.add(i);
-                    }
-                }
-                s.cancel();
-                assertTrue(l.startIsCalled);
-                assertTrue(l.cancelIsCalled);
-                assertTrue(l.endIsCalled);
-
-                for (int i = 0; i < listeners.size(); i++) {
-                    assertTrue(listeners.get(i).startIsCalled);
-                    if (runningAnimIds.contains(i)) {
-                        assertTrue(listeners.get(i).cancelIsCalled);
-                    }
-                    assertTrue(listeners.get(i).endIsCalled);
+        mActivityRule.runOnUiThread(() -> {
+            assertTrue(s.isStarted());
+            ArrayList<Integer> runningAnimIds = new ArrayList<>();
+            for (int i = 0; i < animators.size(); i++) {
+                if (animators.get(i).isStarted()) {
+                    runningAnimIds.add(i);
                 }
             }
-        });
+            s.cancel();
+            assertTrue(l.startIsCalled);
+            assertTrue(l.cancelIsCalled);
+            assertTrue(l.endIsCalled);
 
+            for (int i = 0; i < listeners.size(); i++) {
+                assertTrue(listeners.get(i).startIsCalled);
+                if (runningAnimIds.contains(i)) {
+                    assertTrue(listeners.get(i).cancelIsCalled);
+                }
+                assertTrue(listeners.get(i).endIsCalled);
+            }
+        });
     }
 
-    @SmallTest
+    @Test
     public void testIsRunning() throws Throwable {
         final AnimatorSet s = new AnimatorSet();
         final long startDelay = 500;
@@ -455,12 +432,7 @@
         s.setStartDelay(startDelay);
         MyListener listener = new MyListener();
         s.addListener(listener);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                s.start();
-            }
-        });
+        mActivityRule.runOnUiThread(s::start);
 
         while (!listener.endIsCalled) {
             boolean passedStartDelay = a1.isStarted() || a2.isStarted() || a3.isStarted() ||
@@ -471,35 +443,29 @@
         assertFalse(s.isRunning());
     }
 
-    @SmallTest
+    @Test
     public void testPauseAndResume() throws Throwable {
         final AnimatorSet set = getSequentialSet();
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                // Calling pause before start should have no effect, per documentation
-                set.pause();
-                set.start();
-                assertFalse(set.isPaused());
-            }
+        mActivityRule.runOnUiThread(() -> {
+            // Calling pause before start should have no effect, per documentation
+            set.pause();
+            set.start();
+            assertFalse(set.isPaused());
         });
 
         while (!a2.isStarted()) {
             Thread.sleep(50);
         }
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertFalse(set.isPaused());
-                set.pause();
-                assertTrue(set.isPaused());
-                set.resume();
-                assertFalse(set.isPaused());
-            }
+        mActivityRule.runOnUiThread(() -> {
+            assertFalse(set.isPaused());
+            set.pause();
+            assertTrue(set.isPaused());
+            set.resume();
+            assertFalse(set.isPaused());
         });
     }
 
-    @SmallTest
+    @Test
     public void testClone() throws Throwable {
         // Set up an AnimatorSet and two clones, add one listener to each. When the clones animate,
         // listeners of both the clone and the animator being cloned should receive animation
@@ -535,14 +501,11 @@
 
         // Start the animation, and make the first clone during its run and the second clone once
         // it ends.
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertFalse(l1.startIsCalled);
-                assertFalse(l1.endIsCalled);
+        mActivityRule.runOnUiThread(() -> {
+            assertFalse(l1.startIsCalled);
+            assertFalse(l1.endIsCalled);
 
-                s1.start();
-            }
+            s1.start();
         });
 
         // Make the first clone, during the animation's run.
@@ -552,20 +515,12 @@
         s2.addListener(l2);
 
         Thread.sleep(POLL_INTERVAL);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                s1.end();
-            }
-        });
+        mActivityRule.runOnUiThread(s1::end);
 
         Thread.sleep(POLL_INTERVAL);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertTrue(l1.startIsCalled);
-                assertTrue(l1.endIsCalled);
-            }
+        mActivityRule.runOnUiThread(() -> {
+            assertTrue(l1.startIsCalled);
+            assertTrue(l1.endIsCalled);
         });
         Thread.sleep(POLL_INTERVAL);
 
@@ -574,59 +529,49 @@
         final MyListener l3 = new MyListener();
         s3.addListener(l3);
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                // Checking the fields before animations start.
-                assertFalse(l2.startIsCalled);
-                assertFalse(l2.cancelIsCalled);
-                assertFalse(l2.endIsCalled);
-                assertFalse(l3.startIsCalled);
-                assertFalse(l3.cancelIsCalled);
-                assertFalse(l3.endIsCalled);
+        mActivityRule.runOnUiThread(() -> {
+            // Checking the fields before animations start.
+            assertFalse(l2.startIsCalled);
+            assertFalse(l2.cancelIsCalled);
+            assertFalse(l2.endIsCalled);
+            assertFalse(l3.startIsCalled);
+            assertFalse(l3.cancelIsCalled);
+            assertFalse(l3.endIsCalled);
 
-                s2.start();
-                s3.start();
-            }
+            s2.start();
+            s3.start();
         });
 
         Thread.sleep(POLL_INTERVAL);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                // Make sure the listeners receive the callbacks
-                // At this time only onAnimationStart() should be called.
-                assertTrue(l2.startIsCalled);
-                assertTrue(l3.startIsCalled);
-                assertFalse(l2.endIsCalled);
-                assertFalse(l3.endIsCalled);
-                assertFalse(l2.cancelIsCalled);
-                assertFalse(l3.cancelIsCalled);
+        mActivityRule.runOnUiThread(() -> {
+            // Make sure the listeners receive the callbacks
+            // At this time only onAnimationStart() should be called.
+            assertTrue(l2.startIsCalled);
+            assertTrue(l3.startIsCalled);
+            assertFalse(l2.endIsCalled);
+            assertFalse(l3.endIsCalled);
+            assertFalse(l2.cancelIsCalled);
+            assertFalse(l3.cancelIsCalled);
 
-                s2.end();
-                s3.cancel();
-            }
+            s2.end();
+            s3.cancel();
         });
         Thread.sleep(POLL_INTERVAL);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                // Check that the new listeners for the new animations gets called for the events.
-                assertTrue(l2.startIsCalled);
-                assertFalse(l2.cancelIsCalled);
-                assertTrue(l2.endIsCalled);
-                assertTrue(l3.startIsCalled);
-                assertTrue(l3.cancelIsCalled);
-                assertTrue(l3.endIsCalled);
+        mActivityRule.runOnUiThread(() -> {
+            // Check that the new listeners for the new animations gets called for the events.
+            assertTrue(l2.startIsCalled);
+            assertFalse(l2.cancelIsCalled);
+            assertTrue(l2.endIsCalled);
+            assertTrue(l3.startIsCalled);
+            assertTrue(l3.cancelIsCalled);
+            assertTrue(l3.endIsCalled);
 
-                // Check that the listener on the animation that was being clone receive the
-                // animation lifecycle events for the clones.
-                assertTrue(onlyContains(startedAnimators, s1, s2, s3));
-                assertTrue(onlyContains(canceledAnimators, s3));
-                assertTrue(onlyContains(endedAnimators, s1, s2, s3));
-            }
+            // Check that the listener on the animation that was being clone receive the
+            // animation lifecycle events for the clones.
+            assertTrue(onlyContains(startedAnimators, s1, s2, s3));
+            assertTrue(onlyContains(canceledAnimators, s3));
+            assertTrue(onlyContains(endedAnimators, s1, s2, s3));
         });
-
     }
 
     /**
@@ -663,5 +608,4 @@
         list.add(a5);
         return list;
     }
-
 }
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
index 4e90d1a..94c90aa 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
@@ -23,6 +23,8 @@
 
 import com.android.frameworks.coretests.R;
 
+import org.junit.Test;
+
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -36,7 +38,7 @@
 
     @Override
     public void setUp() throws Exception {
-        button = (Button) getActivity().findViewById(R.id.animatingButton);
+        button =  mActivityRule.getActivity().findViewById(R.id.animatingButton);
         mAnimator = new AnimatorSet();
         ((AnimatorSet)mAnimator).playSequentially(xAnim, yAnim);
         super.setUp();
@@ -53,23 +55,21 @@
      * its children
      */
     @MediumTest
-    public void testPlayingCancelDuringChildDelay() throws Exception {
+    @Test
+    public void testPlayingCancelDuringChildDelay() throws Throwable {
         yAnim.setStartDelay(500);
         final AnimatorSet animSet = new AnimatorSet();
         animSet.playSequentially(xAnim, yAnim);
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    Handler handler = new Handler();
-                    animSet.addListener(mFutureListener);
-                    mRunning = true;
-                    animSet.start();
-                    handler.postDelayed(new Canceler(animSet, mFuture), ANIM_DURATION + 250);
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                Handler handler = new Handler();
+                animSet.addListener(mFutureListener);
+                mRunning = true;
+                animSet.start();
+                handler.postDelayed(new Canceler(animSet, mFuture), ANIM_DURATION + 250);
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
diff --git a/core/tests/coretests/src/android/animation/AutoCancelTest.java b/core/tests/coretests/src/android/animation/AutoCancelTest.java
index b3ec92c..7df7336 100644
--- a/core/tests/coretests/src/android/animation/AutoCancelTest.java
+++ b/core/tests/coretests/src/android/animation/AutoCancelTest.java
@@ -16,15 +16,24 @@
 
 package android.animation;
 
+import static org.junit.Assert.assertTrue;
+
 import android.os.Handler;
-import android.test.ActivityInstrumentationTestCase2;
 
 import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Rule;
+import org.junit.Test;
 
 import java.util.HashMap;
 import java.util.concurrent.TimeUnit;
 
-public class AutoCancelTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
+public class AutoCancelTest {
+
+    @Rule
+    public final ActivityTestRule<BasicAnimatorActivity> mActivityRule =
+            new ActivityTestRule<>(BasicAnimatorActivity.class);
 
     boolean mAnimX1Canceled = false;
     boolean mAnimXY1Canceled = false;
@@ -37,10 +46,6 @@
 
     HashMap<Animator, Boolean> mCanceledMap = new HashMap<Animator, Boolean>();
 
-    public AutoCancelTest() {
-        super(BasicAnimatorActivity.class);
-    }
-
     ObjectAnimator setupAnimator(long startDelay, String... properties) {
         ObjectAnimator returnVal;
         if (properties.length == 1) {
@@ -58,8 +63,7 @@
         return returnVal;
     }
 
-    private void setupAnimators(long startDelay, boolean startLater, final FutureWaiter future)
-    throws Exception {
+    private void setupAnimators(long startDelay, boolean startLater, final FutureWaiter future) {
         // Animators to be auto-canceled
         final ObjectAnimator animX1 = setupAnimator(startDelay, "x");
         final ObjectAnimator animY1 = setupAnimator(startDelay, "y");
@@ -123,64 +127,56 @@
     }
 
     @SmallTest
-    public void testAutoCancel() throws Exception {
+    @Test
+    public void testAutoCancel() throws Throwable {
         final FutureWaiter future = new FutureWaiter();
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    setupAnimators(0, false, future);
-                } catch (Exception e) {
-                    future.setException(e);
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                setupAnimators(0, false, future);
+            } catch (Exception e) {
+                future.setException(e);
             }
         });
         assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS));
     }
 
     @SmallTest
-    public void testAutoCancelDelayed() throws Exception {
+    @Test
+    public void testAutoCancelDelayed() throws Throwable {
         final FutureWaiter future = new FutureWaiter();
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    setupAnimators(START_DELAY, false, future);
-                } catch (Exception e) {
-                    future.setException(e);
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                setupAnimators(START_DELAY, false, future);
+            } catch (Exception e) {
+                future.setException(e);
             }
         });
         assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS));
     }
 
     @SmallTest
-    public void testAutoCancelTestLater() throws Exception {
+    @Test
+    public void testAutoCancelTestLater() throws Throwable {
         final FutureWaiter future = new FutureWaiter();
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    setupAnimators(0, true, future);
-                } catch (Exception e) {
-                    future.setException(e);
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                setupAnimators(0, true, future);
+            } catch (Exception e) {
+                future.setException(e);
             }
         });
         assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS));
     }
 
     @SmallTest
-    public void testAutoCancelDelayedTestLater() throws Exception {
+    @Test
+    public void testAutoCancelDelayedTestLater() throws Throwable {
         final FutureWaiter future = new FutureWaiter();
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    setupAnimators(START_DELAY, true, future);
-                } catch (Exception e) {
-                    future.setException(e);
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                setupAnimators(START_DELAY, true, future);
+            } catch (Exception e) {
+                future.setException(e);
             }
         });
         assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS));
diff --git a/core/tests/coretests/src/android/animation/EventsTest.java b/core/tests/coretests/src/android/animation/EventsTest.java
index ba7413a..0c40a95 100644
--- a/core/tests/coretests/src/android/animation/EventsTest.java
+++ b/core/tests/coretests/src/android/animation/EventsTest.java
@@ -16,12 +16,20 @@
 
 package android.animation;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.CallSuper;
 import android.os.Handler;
-import android.test.ActivityInstrumentationTestCase2;
 
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
 
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -38,8 +46,11 @@
  * wait for some later event to occur before ending. These tests use a combination of an
  * AbstractFuture mechanism and a delayed action to release that Future later.
  */
-public abstract class EventsTest
-        extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
+public abstract class EventsTest {
+
+    @Rule
+    public final ActivityTestRule<BasicAnimatorActivity> mActivityRule =
+            new ActivityTestRule<>(BasicAnimatorActivity.class);
 
     protected static final int ANIM_DURATION = 400;
     protected static final int ANIM_DELAY = 100;
@@ -55,7 +66,6 @@
     private boolean mCanceled; // tracks whether we've canceled the animator
     protected Animator.AnimatorListener mFutureListener; // mechanism for delaying end of the test
     protected FutureWaiter mFuture; // Mechanism for waiting for the UI test to complete
-    private Animator.AnimatorListener mListener; // Listener that handles/tests the events
 
     protected Animator mAnimator; // The animator used in the tests. Must be set in subclass
                                   // setup() method prior to calling the superclass setup()
@@ -67,10 +77,12 @@
     protected static class Canceler implements Runnable {
         Animator mAnim;
         FutureWaiter mFuture;
+
         public Canceler(Animator anim, FutureWaiter future) {
             mAnim = anim;
             mFuture = future;
         }
+
         @Override
         public void run() {
             try {
@@ -79,7 +91,7 @@
                 mFuture.setException(new RuntimeException(e));
             }
         }
-    };
+    }
 
     /**
      * Timeout length, based on when the animation should reasonably be complete.
@@ -95,10 +107,12 @@
     static class Ender implements Runnable {
         Animator mAnim;
         FutureWaiter mFuture;
+
         public Ender(Animator anim, FutureWaiter future) {
             mAnim = anim;
             mFuture = future;
         }
+
         @Override
         public void run() {
             try {
@@ -107,7 +121,7 @@
                 mFuture.setException(new RuntimeException(e));
             }
         }
-    };
+    }
 
     /**
      * Pauses the given animator. Used to delay pausing until some later time (after the
@@ -116,10 +130,12 @@
     static class Pauser implements Runnable {
         Animator mAnim;
         FutureWaiter mFuture;
+
         public Pauser(Animator anim, FutureWaiter future) {
             mAnim = anim;
             mFuture = future;
         }
+
         @Override
         public void run() {
             try {
@@ -128,7 +144,7 @@
                 mFuture.setException(new RuntimeException(e));
             }
         }
-    };
+    }
 
     /**
      * Resumes the given animator. Used to delay resuming until some later time (after the
@@ -137,10 +153,12 @@
     static class Resumer implements Runnable {
         Animator mAnim;
         FutureWaiter mFuture;
+
         public Resumer(Animator anim, FutureWaiter future) {
             mAnim = anim;
             mFuture = future;
         }
+
         @Override
         public void run() {
             try {
@@ -149,7 +167,7 @@
                 mFuture.setException(new RuntimeException(e));
             }
         }
-    };
+    }
 
     /**
      * Releases the given Future object when the listener's end() event is called. Specifically,
@@ -171,28 +189,14 @@
         public FutureReleaseListener(FutureWaiter future, long timeout) {
             mFuture = future;
             Handler handler = new Handler();
-            handler.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    mFuture.release();
-                }
-            }, timeout);
+            handler.postDelayed(mFuture::release, timeout);
         }
 
         @Override
         public void onAnimationEnd(Animator animation) {
             Handler handler = new Handler();
-            handler.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    mFuture.release();
-                }
-            }, FUTURE_RELEASE_DELAY);
+            handler.postDelayed(mFuture::release, FUTURE_RELEASE_DELAY);
         }
-    };
-
-    public EventsTest() {
-        super(BasicAnimatorActivity.class);
     }
 
     /**
@@ -201,13 +205,12 @@
      * and then call super.setup(), where further properties are set on that animator.
      * @throws Exception
      */
-    @Override
+    @CallSuper
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
-
         // mListener is the main testing mechanism of this file. The asserts of each test
         // are embedded in the listener callbacks that it implements.
-        mListener = new AnimatorListenerAdapter() {
+        final Animator.AnimatorListener listener = new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
                 // This should only be called on an animation that has not yet been started
@@ -236,9 +239,8 @@
             }
         };
 
-        mAnimator.addListener(mListener);
+        mAnimator.addListener(listener);
         mAnimator.setDuration(ANIM_DURATION);
-
         mFuture = new FutureWaiter();
 
         mRunning = false;
@@ -251,6 +253,7 @@
      */
     @UiThreadTest
     @SmallTest
+    @Test
     public void testCancel() throws Exception {
         mAnimator.cancel();
     }
@@ -260,6 +263,7 @@
      */
     @UiThreadTest
     @SmallTest
+    @Test
     public void testEnd() throws Exception {
         mRunning = true; // end() implicitly starts an unstarted animator
         mAnimator.end();
@@ -270,19 +274,17 @@
      */
     @UiThreadTest
     @SmallTest
-    public void testStartCancel() throws Exception {
+    @Test
+    public void testStartCancel() throws Throwable {
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mRunning = true;
-                    mAnimator.start();
-                    mAnimator.cancel();
-                    mFuture.release();
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                mRunning = true;
+                mAnimator.start();
+                mAnimator.cancel();
+                mFuture.release();
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -293,19 +295,17 @@
      */
     @UiThreadTest
     @SmallTest
-    public void testStartEnd() throws Exception {
+    @Test
+    public void testStartEnd() throws Throwable {
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mRunning = true;
-                    mAnimator.start();
-                    mAnimator.end();
-                    mFuture.release();
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                mRunning = true;
+                mAnimator.start();
+                mAnimator.end();
+                mFuture.release();
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -315,20 +315,18 @@
      * Same as testStartCancel, but with a startDelayed animator
      */
     @SmallTest
-    public void testStartDelayedCancel() throws Exception {
+    @Test
+    public void testStartDelayedCancel() throws Throwable {
         mFutureListener = new FutureReleaseListener(mFuture);
         mAnimator.setStartDelay(ANIM_DELAY);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mRunning = true;
-                    mAnimator.start();
-                    mAnimator.cancel();
-                    mFuture.release();
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                mRunning = true;
+                mAnimator.start();
+                mAnimator.cancel();
+                mFuture.release();
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -338,20 +336,18 @@
      * Same as testStartEnd, but with a startDelayed animator
      */
     @SmallTest
-    public void testStartDelayedEnd() throws Exception {
+    @Test
+    public void testStartDelayedEnd() throws Throwable {
         mFutureListener = new FutureReleaseListener(mFuture);
         mAnimator.setStartDelay(ANIM_DELAY);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mRunning = true;
-                    mAnimator.start();
-                    mAnimator.end();
-                    mFuture.release();
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                mRunning = true;
+                mAnimator.start();
+                mAnimator.end();
+                mFuture.release();
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -361,20 +357,18 @@
      * Verify that canceling an animator that is playing does the right thing.
      */
     @MediumTest
-    public void testPlayingCancel() throws Exception {
+    @Test
+    public void testPlayingCancel() throws Throwable {
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    Handler handler = new Handler();
-                    mAnimator.addListener(mFutureListener);
-                    mRunning = true;
-                    mAnimator.start();
-                    handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                Handler handler = new Handler();
+                mAnimator.addListener(mFutureListener);
+                mRunning = true;
+                mAnimator.start();
+                handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -384,20 +378,18 @@
      * Verify that ending an animator that is playing does the right thing.
      */
     @MediumTest
-    public void testPlayingEnd() throws Exception {
+    @Test
+    public void testPlayingEnd() throws Throwable {
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    Handler handler = new Handler();
-                    mAnimator.addListener(mFutureListener);
-                    mRunning = true;
-                    mAnimator.start();
-                    handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DURATION);
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                Handler handler = new Handler();
+                mAnimator.addListener(mFutureListener);
+                mRunning = true;
+                mAnimator.start();
+                handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DURATION);
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -407,21 +399,19 @@
      * Same as testPlayingCancel, but with a startDelayed animator
      */
     @MediumTest
-    public void testPlayingDelayedCancel() throws Exception {
+    @Test
+    public void testPlayingDelayedCancel() throws Throwable {
         mAnimator.setStartDelay(ANIM_DELAY);
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    Handler handler = new Handler();
-                    mAnimator.addListener(mFutureListener);
-                    mRunning = true;
-                    mAnimator.start();
-                    handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                Handler handler = new Handler();
+                mAnimator.addListener(mFutureListener);
+                mRunning = true;
+                mAnimator.start();
+                handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(),  TimeUnit.MILLISECONDS);
@@ -431,21 +421,19 @@
      * Same as testPlayingEnd, but with a startDelayed animator
      */
     @MediumTest
-    public void testPlayingDelayedEnd() throws Exception {
+    @Test
+    public void testPlayingDelayedEnd() throws Throwable {
         mAnimator.setStartDelay(ANIM_DELAY);
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    Handler handler = new Handler();
-                    mAnimator.addListener(mFutureListener);
-                    mRunning = true;
-                    mAnimator.start();
-                    handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DURATION);
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                Handler handler = new Handler();
+                mAnimator.addListener(mFutureListener);
+                mRunning = true;
+                mAnimator.start();
+                handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DURATION);
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(),  TimeUnit.MILLISECONDS);
@@ -455,24 +443,21 @@
      * Same as testPlayingDelayedCancel, but cancel during the startDelay period
      */
     @MediumTest
-    public void testPlayingDelayedCancelMidDelay() throws Exception {
+    @Test
+    public void testPlayingDelayedCancelMidDelay() throws Throwable {
         mAnimator.setStartDelay(ANIM_DELAY);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    // Set the listener to automatically timeout after an uncanceled animation
-                    // would have finished. This tests to make sure that we're not calling
-                    // the listeners with cancel/end callbacks since they won't be called
-                    // with the start event.
-                    mFutureListener = new FutureReleaseListener(mFuture, getTimeout());
-                    Handler handler = new Handler();
-                    mRunning = true;
-                    mAnimator.start();
-                    handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DELAY);
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                // Set the listener to automatically timeout after an uncanceled animation would
+                // have finished. This tests to make sure that we're not calling the listeners with
+                // cancel/end callbacks since they won't be called with the start event.
+                mFutureListener = new FutureReleaseListener(mFuture, getTimeout());
+                Handler handler = new Handler();
+                mRunning = true;
+                mAnimator.start();
+                handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DELAY);
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout() + 100,  TimeUnit.MILLISECONDS);
@@ -482,24 +467,21 @@
      * Same as testPlayingDelayedEnd, but end during the startDelay period
      */
     @MediumTest
-    public void testPlayingDelayedEndMidDelay() throws Exception {
+    @Test
+    public void testPlayingDelayedEndMidDelay() throws Throwable {
         mAnimator.setStartDelay(ANIM_DELAY);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    // Set the listener to automatically timeout after an uncanceled animation
-                    // would have finished. This tests to make sure that we're not calling
-                    // the listeners with cancel/end callbacks since they won't be called
-                    // with the start event.
-                    mFutureListener = new FutureReleaseListener(mFuture, getTimeout());
-                    Handler handler = new Handler();
-                    mRunning = true;
-                    mAnimator.start();
-                    handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DELAY);
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                // Set the listener to automatically timeout after an uncanceled animation would
+                // have finished. This tests to make sure that we're not calling the listeners with
+                // cancel/end callbacks since they won't be called with the start event.
+                mFutureListener = new FutureReleaseListener(mFuture, getTimeout());
+                Handler handler = new Handler();
+                mRunning = true;
+                mAnimator.start();
+                handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DELAY);
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout() + 100,  TimeUnit.MILLISECONDS);
@@ -510,20 +492,18 @@
      * does nothing.
      */
     @MediumTest
-    public void testStartDoubleCancel() throws Exception {
+    @Test
+    public void testStartDoubleCancel() throws Throwable {
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mRunning = true;
-                    mAnimator.start();
-                    mAnimator.cancel();
-                    mAnimator.cancel();
-                    mFuture.release();
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                mRunning = true;
+                mAnimator.start();
+                mAnimator.cancel();
+                mAnimator.cancel();
+                mFuture.release();
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(),  TimeUnit.MILLISECONDS);
@@ -534,21 +514,19 @@
      * does nothing.
      */
     @MediumTest
-    public void testStartDoubleEnd() throws Exception {
+    @Test
+    public void testStartDoubleEnd() throws Throwable {
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mRunning = true;
-                    mAnimator.start();
-                    mAnimator.end();
-                    mRunning = true; // end() implicitly starts an unstarted animator
-                    mAnimator.end();
-                    mFuture.release();
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                mRunning = true;
+                mAnimator.start();
+                mAnimator.end();
+                mRunning = true; // end() implicitly starts an unstarted animator
+                mAnimator.end();
+                mFuture.release();
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(),  TimeUnit.MILLISECONDS);
@@ -558,21 +536,19 @@
      * Same as testStartDoubleCancel, but with a startDelayed animator
      */
     @MediumTest
-    public void testStartDelayedDoubleCancel() throws Exception {
+    @Test
+    public void testStartDelayedDoubleCancel() throws Throwable {
         mAnimator.setStartDelay(ANIM_DELAY);
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mRunning = true;
-                    mAnimator.start();
-                    mAnimator.cancel();
-                    mAnimator.cancel();
-                    mFuture.release();
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                mRunning = true;
+                mAnimator.start();
+                mAnimator.cancel();
+                mAnimator.cancel();
+                mFuture.release();
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(),  TimeUnit.MILLISECONDS);
@@ -582,22 +558,20 @@
      * Same as testStartDoubleEnd, but with a startDelayed animator
      */
     @MediumTest
-    public void testStartDelayedDoubleEnd() throws Exception {
+    @Test
+    public void testStartDelayedDoubleEnd() throws Throwable {
         mAnimator.setStartDelay(ANIM_DELAY);
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mRunning = true;
-                    mAnimator.start();
-                    mAnimator.end();
-                    mRunning = true; // end() implicitly starts an unstarted animator
-                    mAnimator.end();
-                    mFuture.release();
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                mRunning = true;
+                mAnimator.start();
+                mAnimator.end();
+                mRunning = true; // end() implicitly starts an unstarted animator
+                mAnimator.end();
+                mFuture.release();
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(),  TimeUnit.MILLISECONDS);
@@ -608,22 +582,20 @@
      * the appropriate timeout duration.
      */
     @MediumTest
-    public void testPauseResume() throws Exception {
+    @Test
+    public void testPauseResume() throws Throwable {
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    Handler handler = new Handler();
-                    mAnimator.addListener(mFutureListener);
-                    mRunning = true;
-                    mAnimator.start();
-                    handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
-                    handler.postDelayed(new Resumer(mAnimator, mFuture),
-                            ANIM_PAUSE_DELAY + ANIM_PAUSE_DURATION);
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                Handler handler = new Handler();
+                mAnimator.addListener(mFutureListener);
+                mRunning = true;
+                mAnimator.start();
+                handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
+                handler.postDelayed(new Resumer(mAnimator, mFuture),
+                        ANIM_PAUSE_DELAY + ANIM_PAUSE_DURATION);
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout() + ANIM_PAUSE_DURATION, TimeUnit.MILLISECONDS);
@@ -634,23 +606,21 @@
      * the appropriate timeout duration.
      */
     @MediumTest
-    public void testPauseResumeDelayed() throws Exception {
+    @Test
+    public void testPauseResumeDelayed() throws Throwable {
         mAnimator.setStartDelay(ANIM_DELAY);
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    Handler handler = new Handler();
-                    mAnimator.addListener(mFutureListener);
-                    mRunning = true;
-                    mAnimator.start();
-                    handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
-                    handler.postDelayed(new Resumer(mAnimator, mFuture),
-                            ANIM_PAUSE_DELAY + ANIM_PAUSE_DURATION);
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                Handler handler = new Handler();
+                mAnimator.addListener(mFutureListener);
+                mRunning = true;
+                mAnimator.start();
+                handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
+                handler.postDelayed(new Resumer(mAnimator, mFuture),
+                        ANIM_PAUSE_DELAY + ANIM_PAUSE_DURATION);
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout() + ANIM_PAUSE_DURATION + ANIM_FULL_DURATION_SLOP,
@@ -661,20 +631,18 @@
      * Verify that pausing an animator without resuming it causes a timeout.
      */
     @MediumTest
-    public void testPauseTimeout() throws Exception {
+    @Test
+    public void testPauseTimeout() throws Throwable {
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    Handler handler = new Handler();
-                    mAnimator.addListener(mFutureListener);
-                    mRunning = true;
-                    mAnimator.start();
-                    handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                Handler handler = new Handler();
+                mAnimator.addListener(mFutureListener);
+                mRunning = true;
+                mAnimator.start();
+                handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         try {
@@ -689,21 +657,19 @@
      * Verify that pausing a startDelayed animator without resuming it causes a timeout.
      */
     @MediumTest
-    public void testPauseTimeoutDelayed() throws Exception {
+    @Test
+    public void testPauseTimeoutDelayed() throws Throwable {
         mAnimator.setStartDelay(ANIM_DELAY);
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    Handler handler = new Handler();
-                    mAnimator.addListener(mFutureListener);
-                    mRunning = true;
-                    mAnimator.start();
-                    handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                Handler handler = new Handler();
+                mAnimator.addListener(mFutureListener);
+                mRunning = true;
+                mAnimator.start();
+                handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         try {
diff --git a/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java
index 53f9472..63ad061 100644
--- a/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java
+++ b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java
@@ -27,11 +27,10 @@
 
     @Override
     public void setUp() throws Exception {
-        final BasicAnimatorActivity activity = getActivity();
-        Button button = (Button) activity.findViewById(R.id.animatingButton);
+        final BasicAnimatorActivity activity = mActivityRule.getActivity();
+        Button button = activity.findViewById(R.id.animatingButton);
 
         mAnimator = ObjectAnimator.ofFloat(button, "translationX", 0, 100);
         super.setUp();
     }
-
 }
diff --git a/core/tests/coretests/src/android/animation/StateListAnimatorTest.java b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
index e755b89..12f1977 100644
--- a/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
+++ b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
@@ -16,41 +16,47 @@
 
 package android.animation;
 
-import android.test.ActivityInstrumentationTestCase2;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
 import android.util.StateSet;
 import android.view.View;
 import android.view.ViewGroup;
 
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
 
 import com.android.frameworks.coretests.R;
 
+import org.junit.Rule;
+import org.junit.Test;
+
 import java.util.concurrent.atomic.AtomicInteger;
 
 @LargeTest
-public class StateListAnimatorTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
+public class StateListAnimatorTest {
 
-    public StateListAnimatorTest() {
-        super(BasicAnimatorActivity.class);
-    }
+    @Rule
+    public final ActivityTestRule<BasicAnimatorActivity> mActivityRule =
+            new ActivityTestRule<>(BasicAnimatorActivity.class);
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-    }
-
+    @Test
     public void testInflateFromAnimator() throws Exception {
         StateListAnimator stateListAnimator = AnimatorInflater
-                .loadStateListAnimator(getActivity(), R.anim.test_state_anim);
+                .loadStateListAnimator(mActivityRule.getActivity(), R.anim.test_state_anim);
         assertNotNull("A state list animator should be returned", stateListAnimator);
         assertEquals("State list animator should have three items", 3,
                 stateListAnimator.getTuples().size());
     }
 
     @UiThreadTest
+    @Test
     public void testAttachDetach() throws Exception {
-        View view = new View(getActivity());
+        final BasicAnimatorActivity activity = mActivityRule.getActivity();
+        View view = new View(activity);
         final AtomicInteger setStateCount = new AtomicInteger(0);
         StateListAnimator stateListAnimator = new StateListAnimator() {
             @Override
@@ -62,7 +68,7 @@
         view.setStateListAnimator(stateListAnimator);
         assertNotNull("State list animator should have a reference to view even if it is detached",
                 stateListAnimator.getTarget());
-        ViewGroup viewGroup = (ViewGroup) getActivity().findViewById(android.R.id.content);
+        ViewGroup viewGroup = activity.findViewById(android.R.id.content);
         int preSetStateCount = setStateCount.get();
         viewGroup.addView(view);
         assertTrue("When view is attached, state list drawable's setState should be called",
@@ -82,9 +88,10 @@
                 stateListAnimator2.getTarget());
     }
 
+    @Test
     public void testStateListLoading() throws InterruptedException {
         StateListAnimator stateListAnimator = AnimatorInflater
-                .loadStateListAnimator(getActivity(), R.anim.test_state_anim);
+                .loadStateListAnimator(mActivityRule.getActivity(), R.anim.test_state_anim);
         assertNotNull("A state list animator should be returned", stateListAnimator);
         assertEquals("Steate list animator should have two items", 3,
                 stateListAnimator.getTuples().size());
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java
index f6d71b8..ba9aef8 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java
@@ -26,5 +26,4 @@
         mAnimator = ValueAnimator.ofFloat(0, 1);
         super.setUp();
     }
-
 }
diff --git a/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java b/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
index 997af00..81cd4da 100644
--- a/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
+++ b/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
@@ -16,17 +16,24 @@
 
 package android.animation;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import android.os.Handler;
-import android.test.ActivityInstrumentationTestCase2;
 import android.view.ViewPropertyAnimator;
 import android.widget.Button;
 
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
 
 import com.android.frameworks.coretests.R;
 
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -41,8 +48,11 @@
  * wait for some later event to occur before ending. These tests use a combination of an
  * AbstractFuture mechanism and a delayed action to release that Future later.
  */
-public abstract class ViewPropertyAnimatorTest
-        extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
+public class ViewPropertyAnimatorTest {
+
+    @Rule
+    public final ActivityTestRule<BasicAnimatorActivity> mActivityRule =
+            new ActivityTestRule<>(BasicAnimatorActivity.class);
 
     protected static final int ANIM_DURATION = 400;
     protected static final int ANIM_DELAY = 100;
@@ -79,7 +89,7 @@
                 mFuture.setException(new RuntimeException(e));
             }
         }
-    };
+    }
 
     /**
      * Timeout length, based on when the animation should reasonably be complete.
@@ -108,28 +118,14 @@
         public FutureReleaseListener(FutureWaiter future, long timeout) {
             mFuture = future;
             Handler handler = new Handler();
-            handler.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    mFuture.release();
-                }
-            }, timeout);
+            handler.postDelayed(mFuture::release, timeout);
         }
 
         @Override
         public void onAnimationEnd(Animator animation) {
             Handler handler = new Handler();
-            handler.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    mFuture.release();
-                }
-            }, FUTURE_RELEASE_DELAY);
+            handler.postDelayed(mFuture::release, FUTURE_RELEASE_DELAY);
         }
-    };
-
-    public ViewPropertyAnimatorTest() {
-        super(BasicAnimatorActivity.class);
     }
 
     /**
@@ -138,15 +134,13 @@
      * and then call super.setup(), where further properties are set on that animator.
      * @throws Exception
      */
-    @Override
+    @Before
     public void setUp() throws Exception {
-        final BasicAnimatorActivity activity = getActivity();
-        Button button = (Button) activity.findViewById(R.id.animatingButton);
+        final BasicAnimatorActivity activity = mActivityRule.getActivity();
+        Button button = activity.findViewById(R.id.animatingButton);
 
         mAnimator = button.animate().x(100).y(100);
 
-        super.setUp();
-
         // mListener is the main testing mechanism of this file. The asserts of each test
         // are embedded in the listener callbacks that it implements.
         mListener = new AnimatorListenerAdapter() {
@@ -195,6 +189,7 @@
      */
     @UiThreadTest
     @SmallTest
+    @Test
     public void testCancel() throws Exception {
         mAnimator.cancel();
     }
@@ -204,19 +199,17 @@
      */
     @UiThreadTest
     @SmallTest
-    public void testStartCancel() throws Exception {
+    @Test
+    public void testStartCancel() throws Throwable {
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mRunning = true;
-                    mAnimator.start();
-                    mAnimator.cancel();
-                    mFuture.release();
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                mRunning = true;
+                mAnimator.start();
+                mAnimator.cancel();
+                mFuture.release();
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -226,20 +219,18 @@
      * Same as testStartCancel, but with a startDelayed animator
      */
     @SmallTest
-    public void testStartDelayedCancel() throws Exception {
+    @Test
+    public void testStartDelayedCancel() throws Throwable {
         mFutureListener = new FutureReleaseListener(mFuture);
         mAnimator.setStartDelay(ANIM_DELAY);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mRunning = true;
-                    mAnimator.start();
-                    mAnimator.cancel();
-                    mFuture.release();
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                mRunning = true;
+                mAnimator.start();
+                mAnimator.cancel();
+                mFuture.release();
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -249,20 +240,18 @@
      * Verify that canceling an animator that is playing does the right thing.
      */
     @MediumTest
-    public void testPlayingCancel() throws Exception {
+    @Test
+    public void testPlayingCancel() throws Throwable {
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    Handler handler = new Handler();
-                    mAnimator.setListener(mFutureListener);
-                    mRunning = true;
-                    mAnimator.start();
-                    handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                Handler handler = new Handler();
+                mAnimator.setListener(mFutureListener);
+                mRunning = true;
+                mAnimator.start();
+                handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -272,21 +261,19 @@
      * Same as testPlayingCancel, but with a startDelayed animator
      */
     @MediumTest
-    public void testPlayingDelayedCancel() throws Exception {
+    @Test
+    public void testPlayingDelayedCancel() throws Throwable {
         mAnimator.setStartDelay(ANIM_DELAY);
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    Handler handler = new Handler();
-                    mAnimator.setListener(mFutureListener);
-                    mRunning = true;
-                    mAnimator.start();
-                    handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                Handler handler = new Handler();
+                mAnimator.setListener(mFutureListener);
+                mRunning = true;
+                mAnimator.start();
+                handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(),  TimeUnit.MILLISECONDS);
@@ -296,24 +283,21 @@
      * Same as testPlayingDelayedCancel, but cancel during the startDelay period
      */
     @MediumTest
-    public void testPlayingDelayedCancelMidDelay() throws Exception {
+    @Test
+    public void testPlayingDelayedCancelMidDelay() throws Throwable {
         mAnimator.setStartDelay(ANIM_DELAY);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    // Set the listener to automatically timeout after an uncanceled animation
-                    // would have finished. This tests to make sure that we're not calling
-                    // the listeners with cancel/end callbacks since they won't be called
-                    // with the start event.
-                    mFutureListener = new FutureReleaseListener(mFuture, getTimeout());
-                    Handler handler = new Handler();
-                    mRunning = true;
-                    mAnimator.start();
-                    handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DELAY);
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                // Set the listener to automatically timeout after an uncanceled animation would
+                // have finished. This tests to make sure that we're not calling the listeners with
+                // cancel/end callbacks since they won't be called with the start event.
+                mFutureListener = new FutureReleaseListener(mFuture, getTimeout());
+                Handler handler = new Handler();
+                mRunning = true;
+                mAnimator.start();
+                handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DELAY);
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout() + 100,  TimeUnit.MILLISECONDS);
@@ -324,20 +308,18 @@
      * does nothing.
      */
     @MediumTest
-    public void testStartDoubleCancel() throws Exception {
+    @Test
+    public void testStartDoubleCancel() throws Throwable {
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mRunning = true;
-                    mAnimator.start();
-                    mAnimator.cancel();
-                    mAnimator.cancel();
-                    mFuture.release();
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                mRunning = true;
+                mAnimator.start();
+                mAnimator.cancel();
+                mAnimator.cancel();
+                mFuture.release();
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -347,24 +329,21 @@
      * Same as testStartDoubleCancel, but with a startDelayed animator
      */
     @MediumTest
-    public void testStartDelayedDoubleCancel() throws Exception {
+    @Test
+    public void testStartDelayedDoubleCancel() throws Throwable {
         mAnimator.setStartDelay(ANIM_DELAY);
         mFutureListener = new FutureReleaseListener(mFuture);
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mRunning = true;
-                    mAnimator.start();
-                    mAnimator.cancel();
-                    mAnimator.cancel();
-                    mFuture.release();
-                } catch (junit.framework.AssertionFailedError e) {
-                    mFuture.setException(new RuntimeException(e));
-                }
+        mActivityRule.runOnUiThread(() -> {
+            try {
+                mRunning = true;
+                mAnimator.start();
+                mAnimator.cancel();
+                mAnimator.cancel();
+                mFuture.release();
+            } catch (junit.framework.AssertionFailedError e) {
+                mFuture.setException(new RuntimeException(e));
             }
         });
         mFuture.get(getTimeout(),  TimeUnit.MILLISECONDS);
      }
-
 }
diff --git a/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java b/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java
index 16be0b0..d8799cb 100644
--- a/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java
+++ b/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java
@@ -17,7 +17,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/AccessibilityInteractionControllerTest.java b/core/tests/coretests/src/android/view/AccessibilityInteractionControllerTest.java
index d0719cb..7855ef9 100644
--- a/core/tests/coretests/src/android/view/AccessibilityInteractionControllerTest.java
+++ b/core/tests/coretests/src/android/view/AccessibilityInteractionControllerTest.java
@@ -26,9 +26,6 @@
 import android.app.UiAutomation;
 import android.graphics.Rect;
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.TextUtils;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -36,6 +33,10 @@
 import android.view.accessibility.AccessibilityTestActivity;
 import android.view.accessibility.AccessibilityWindowInfo;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.rule.ActivityTestRule;
+
 import com.android.compatibility.common.util.TestUtils;
 import com.android.frameworks.coretests.R;
 
diff --git a/core/tests/coretests/src/android/view/PinchZoomAction.java b/core/tests/coretests/src/android/view/PinchZoomAction.java
index bec9b55..cfdec4d 100644
--- a/core/tests/coretests/src/android/view/PinchZoomAction.java
+++ b/core/tests/coretests/src/android/view/PinchZoomAction.java
@@ -16,20 +16,21 @@
 
 package android.view;
 
-import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
-import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import static org.hamcrest.Matchers.allOf;
 
 import android.os.SystemClock;
-import android.support.test.espresso.InjectEventSecurityException;
-import android.support.test.espresso.PerformException;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.action.Swiper;
-import android.support.test.espresso.util.HumanReadables;
+
+import androidx.test.espresso.InjectEventSecurityException;
+import androidx.test.espresso.PerformException;
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.action.Swiper;
+import androidx.test.espresso.util.HumanReadables;
 
 import org.hamcrest.Matcher;
 
diff --git a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
index 1990135..f63a454 100644
--- a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
+++ b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
@@ -16,48 +16,43 @@
 
 package android.view;
 
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
-import android.test.ActivityInstrumentationTestCase2;
 import android.util.DisplayMetrics;
 import android.widget.TextView;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
 
 import com.android.frameworks.coretests.R;
 
-import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 
 @LargeTest
-public class ScaleGestureDetectorTest extends ActivityInstrumentationTestCase2<ScaleGesture> {
-    private ScaleGesture mScaleGestureActivity;
+public class ScaleGestureDetectorTest {
 
-    public ScaleGestureDetectorTest() {
-        super("com.android.frameworks.coretests", ScaleGesture.class);
-    }
+    @Rule
+    public final ActivityTestRule<ScaleGesture> mActivityRule =
+            new ActivityTestRule<>(ScaleGesture.class);
+    private ScaleGesture mScaleGestureActivity;
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
-        injectInstrumentation(InstrumentationRegistry.getInstrumentation());
-        mScaleGestureActivity = getActivity();
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        super.tearDown();
+        mScaleGestureActivity = mActivityRule.getActivity();
     }
 
     @Test
     public void testScaleGestureDetector() {
         // No scaling should have occurred prior to performing pinch zoom action.
         final float initialScaleFactor = 1.0f;
-        assertEquals(initialScaleFactor, mScaleGestureActivity.getScaleFactor());
+        assertEquals(initialScaleFactor, mScaleGestureActivity.getScaleFactor(), 0f);
 
         // Specify start and end coordinates, irrespective of device display size.
         final DisplayMetrics dm = new DisplayMetrics();
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 71612e6..34fdebf 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -155,5 +155,10 @@
         void internalNotifyViewTextChanged(AutofillId id, CharSequence text) {
             throw new UnsupportedOperationException("should not have been called");
         }
+
+        @Override
+        public void internalNotifyViewHierarchyEvent(boolean started) {
+            throw new UnsupportedOperationException("should not have been called");
+        }
     }
 }
diff --git a/core/tests/coretests/src/android/widget/EditorCursorTest.java b/core/tests/coretests/src/android/widget/EditorCursorTest.java
index e4f55df..585c601 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorTest.java
@@ -16,11 +16,12 @@
 
 package android.widget;
 
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
 import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnLeft;
 import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnRight;
 
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+
 import static junit.framework.Assert.fail;
 
 import static org.hamcrest.MatcherAssert.assertThat;
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index 483270e..f6e02bc 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -16,15 +16,6 @@
 
 package android.widget;
 
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.Espresso.pressBack;
-import static android.support.test.espresso.action.ViewActions.clearText;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.action.ViewActions.replaceText;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
 import static android.widget.espresso.DragHandleUtils.onHandleView;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
@@ -37,43 +28,55 @@
 import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
 import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex;
 
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.Espresso.pressBack;
+import static androidx.test.espresso.action.ViewActions.clearText;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.replaceText;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 
 import android.content.res.TypedArray;
-import android.support.test.espresso.NoMatchingViewException;
-import android.support.test.espresso.ViewAssertion;
-import android.test.ActivityInstrumentationTestCase2;
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.Spanned;
 import android.text.TextPaint;
 import android.text.style.SuggestionSpan;
 import android.text.style.TextAppearanceSpan;
-import android.view.View;
 
 import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
 
 import com.android.frameworks.coretests.R;
 
+import org.junit.Rule;
+import org.junit.Test;
+
 /**
  * SuggestionsPopupWindowTest tests.
  *
  * TODO: Add tests for when there are no suggestions
  */
-public class SuggestionsPopupWindowTest extends ActivityInstrumentationTestCase2<TextViewActivity> {
+@SmallTest
+public class SuggestionsPopupWindowTest {
 
-    public SuggestionsPopupWindowTest() {
-        super(TextViewActivity.class);
-    }
+    @Rule
+    public final ActivityTestRule<TextViewActivity> mActivityRule =
+            new ActivityTestRule<>(TextViewActivity.class);
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        getActivity();
+    private TextViewActivity getActivity() {
+        return mActivityRule.getActivity();
     }
 
     private void setSuggestionSpan(SuggestionSpan span, int start, int end) {
-        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+        final TextView textView = getActivity().findViewById(R.id.textview);
         textView.post(
                 () -> {
                     final Spannable text = (Spannable) textView.getText();
@@ -83,7 +86,7 @@
         getInstrumentation().waitForIdleSync();
     }
 
-    @SmallTest
+    @Test
     public void testOnTextContextMenuItem() {
         final String text = "abc def ghi";
 
@@ -94,14 +97,14 @@
                 new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
         setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
 
-        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+        final TextView textView = getActivity().findViewById(R.id.textview);
         textView.post(() -> textView.onTextContextMenuItem(TextView.ID_REPLACE));
         getInstrumentation().waitForIdleSync();
 
         assertSuggestionsPopupIsDisplayed();
     }
 
-    @SmallTest
+    @Test
     public void testSelectionActionMode() {
         final String text = "abc def ghi";
 
@@ -123,7 +126,7 @@
         assertSuggestionsPopupIsDisplayed();
     }
 
-    @SmallTest
+    @Test
     public void testInsertionActionMode() {
         final String text = "abc def ghi";
 
@@ -146,13 +149,13 @@
     }
 
     private void showSuggestionsPopup() {
-        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+        final TextView textView = getActivity().findViewById(R.id.textview);
         textView.post(() -> textView.onTextContextMenuItem(TextView.ID_REPLACE));
         getInstrumentation().waitForIdleSync();
         assertSuggestionsPopupIsDisplayed();
     }
 
-    @SmallTest
+    @Test
     public void testSuggestionItems() {
         final String text = "abc def ghi";
 
@@ -190,7 +193,7 @@
         onView(withId(R.id.textview)).check(matches(withText("abc ghi")));
     }
 
-    @SmallTest
+    @Test
     public void testMisspelled() {
         final String text = "abc def ghi";
 
@@ -217,7 +220,7 @@
         // TODO: Check if add to dictionary dialog is displayed.
     }
 
-    @SmallTest
+    @Test
     public void testEasyCorrect() {
         final String text = "abc def ghi";
 
@@ -253,7 +256,7 @@
                 getActivity().getString(com.android.internal.R.string.delete));
     }
 
-    @SmallTest
+    @Test
     public void testTextAppearanceInSuggestionsPopup() {
         final String text = "abc def ghi";
 
@@ -302,53 +305,49 @@
             assertSuggestionsPopupContainsItem(
                     getActivity().getString(com.android.internal.R.string.delete));
 
-            onSuggestionsPopup().check(new ViewAssertion() {
-                @Override
-                public void check(View view, NoMatchingViewException e) {
-                    final ListView listView = (ListView) view.findViewById(
-                            com.android.internal.R.id.suggestionContainer);
-                    assertNotNull(listView);
-                    final int childNum = listView.getChildCount();
-                    assertEquals(singleWordCandidates.length + multiWordCandidates.length,
-                            childNum);
+            onSuggestionsPopup().check((view, e) -> {
+                final ListView listView = view.findViewById(
+                        com.android.internal.R.id.suggestionContainer);
+                assertNotNull(listView);
+                final int childNum = listView.getChildCount();
+                assertEquals(singleWordCandidates.length + multiWordCandidates.length, childNum);
 
-                    for (int j = 0; j < childNum; j++) {
-                        final TextView suggestion = (TextView) listView.getChildAt(j);
-                        assertNotNull(suggestion);
-                        final Spanned spanned = (Spanned) suggestion.getText();
-                        assertNotNull(spanned);
+                for (int j = 0; j < childNum; j++) {
+                    final TextView suggestion = (TextView) listView.getChildAt(j);
+                    assertNotNull(suggestion);
+                    final Spanned spanned = (Spanned) suggestion.getText();
+                    assertNotNull(spanned);
 
-                        // Check that the suggestion item order is kept.
-                        final String expectedText;
-                        if (j < singleWordCandidates.length) {
-                            expectedText = "abc " + singleWordCandidates[j] + " ghi";
-                        } else {
-                            expectedText = multiWordCandidates[j - singleWordCandidates.length];
-                        }
-                        assertEquals(expectedText, spanned.toString());
-
-                        // Check that the text is highlighted with correct color and text size.
-                        final TextAppearanceSpan[] taSpan = spanned.getSpans(
-                                text.indexOf('d'), text.indexOf('f') + 1, TextAppearanceSpan.class);
-                        assertEquals(1, taSpan.length);
-                        TextPaint tp = new TextPaint();
-                        taSpan[0].updateDrawState(tp);
-                        assertEquals(expectedHighlightTextColor, tp.getColor());
-                        assertEquals(expectedHighlightTextSize, tp.getTextSize());
-
-                        // Check the correct part of the text is highlighted.
-                        final int expectedStart;
-                        final int expectedEnd;
-                        if (j < singleWordCandidates.length) {
-                            expectedStart = text.indexOf('d');
-                            expectedEnd = text.indexOf('f') + 1;
-                        } else {
-                            expectedStart = 0;
-                            expectedEnd = text.length();
-                        }
-                        assertEquals(expectedStart, spanned.getSpanStart(taSpan[0]));
-                        assertEquals(expectedEnd, spanned.getSpanEnd(taSpan[0]));
+                    // Check that the suggestion item order is kept.
+                    final String expectedText;
+                    if (j < singleWordCandidates.length) {
+                        expectedText = "abc " + singleWordCandidates[j] + " ghi";
+                    } else {
+                        expectedText = multiWordCandidates[j - singleWordCandidates.length];
                     }
+                    assertEquals(expectedText, spanned.toString());
+
+                    // Check that the text is highlighted with correct color and text size.
+                    final TextAppearanceSpan[] taSpan = spanned.getSpans(
+                            text.indexOf('d'), text.indexOf('f') + 1, TextAppearanceSpan.class);
+                    assertEquals(1, taSpan.length);
+                    TextPaint tp = new TextPaint();
+                    taSpan[0].updateDrawState(tp);
+                    assertEquals(expectedHighlightTextColor, tp.getColor());
+                    assertEquals(expectedHighlightTextSize, tp.getTextSize(), 0f);
+
+                    // Check the correct part of the text is highlighted.
+                    final int expectedStart;
+                    final int expectedEnd;
+                    if (j < singleWordCandidates.length) {
+                        expectedStart = text.indexOf('d');
+                        expectedEnd = text.indexOf('f') + 1;
+                    } else {
+                        expectedStart = 0;
+                        expectedEnd = text.length();
+                    }
+                    assertEquals(expectedStart, spanned.getSpanStart(taSpan[0]));
+                    assertEquals(expectedEnd, spanned.getSpanEnd(taSpan[0]));
                 }
             });
             pressBack();
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index 41fa08b..b411668 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -16,15 +16,6 @@
 
 package android.widget;
 
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.Espresso.pressBack;
-import static android.support.test.espresso.action.ViewActions.replaceText;
-import static android.support.test.espresso.action.ViewActions.typeText;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
 import static android.widget.espresso.ContextMenuUtils.assertContextMenuContainsItemDisabled;
 import static android.widget.espresso.ContextMenuUtils.assertContextMenuContainsItemEnabled;
 import static android.widget.espresso.ContextMenuUtils.assertContextMenuIsNotDisplayed;
@@ -42,6 +33,15 @@
 import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
 import static android.widget.espresso.TextViewAssertions.hasSelection;
 
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.Espresso.pressBack;
+import static androidx.test.espresso.action.ViewActions.replaceText;
+import static androidx.test.espresso.action.ViewActions.typeText;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
 import android.app.Activity;
 import android.view.MotionEvent;
 import android.view.textclassifier.TextClassificationManager;
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 9d93421..267a9c8 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -16,15 +16,6 @@
 
 package android.widget;
 
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.action.ViewActions.longClick;
-import static android.support.test.espresso.action.ViewActions.pressKey;
-import static android.support.test.espresso.action.ViewActions.replaceText;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
 import static android.widget.espresso.CustomViewActions.longPressAtRelativeCoordinates;
 import static android.widget.espresso.DragHandleUtils.onHandleView;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
@@ -44,6 +35,16 @@
 import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
 import static android.widget.espresso.TextViewAssertions.hasSelection;
 
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.longClick;
+import static androidx.test.espresso.action.ViewActions.pressKey;
+import static androidx.test.espresso.action.ViewActions.replaceText;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -60,7 +61,6 @@
 import android.app.Instrumentation;
 import android.content.ClipData;
 import android.content.ClipboardManager;
-import android.support.test.espresso.action.EspressoKey;
 import android.support.test.uiautomator.UiDevice;
 import android.text.InputType;
 import android.text.Selection;
@@ -79,6 +79,7 @@
 import android.widget.espresso.CustomViewActions.RelativeCoordinatesProvider;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.espresso.action.EspressoKey;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.Suppress;
 import androidx.test.rule.ActivityTestRule;
diff --git a/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java b/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java
index 02ee9be..0f8faa8 100644
--- a/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java
@@ -16,28 +16,29 @@
 
 package android.widget.espresso;
 
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
-import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static android.support.test.espresso.matcher.ViewMatchers.hasFocus;
-import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
-import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
+import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static androidx.test.espresso.matcher.ViewMatchers.hasFocus;
+import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
+import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.not;
 
-import android.support.test.espresso.NoMatchingRootException;
-import android.support.test.espresso.NoMatchingViewException;
-import android.support.test.espresso.ViewInteraction;
-import android.support.test.espresso.matcher.ViewMatchers;
 import android.view.View;
 import android.widget.MenuPopupWindow.MenuDropDownListView;
 
+import androidx.test.espresso.NoMatchingRootException;
+import androidx.test.espresso.NoMatchingViewException;
+import androidx.test.espresso.ViewInteraction;
+import androidx.test.espresso.matcher.ViewMatchers;
+
 import com.android.internal.view.menu.ListMenuItemView;
 
 import org.hamcrest.Description;
diff --git a/core/tests/coretests/src/android/widget/espresso/CustomViewActions.java b/core/tests/coretests/src/android/widget/espresso/CustomViewActions.java
index daf9e78..217553e 100644
--- a/core/tests/coretests/src/android/widget/espresso/CustomViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/CustomViewActions.java
@@ -16,15 +16,15 @@
 
 package android.widget.espresso;
 
-import static android.support.test.espresso.action.ViewActions.actionWithAssertions;
+import static androidx.test.espresso.action.ViewActions.actionWithAssertions;
 
 import android.view.View;
 
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.action.CoordinatesProvider;
-import android.support.test.espresso.action.GeneralClickAction;
-import android.support.test.espresso.action.Press;
-import android.support.test.espresso.action.Tap;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.action.CoordinatesProvider;
+import androidx.test.espresso.action.GeneralClickAction;
+import androidx.test.espresso.action.Press;
+import androidx.test.espresso.action.Tap;
 
 import com.android.internal.util.Preconditions;
 
diff --git a/core/tests/coretests/src/android/widget/espresso/DragAction.java b/core/tests/coretests/src/android/widget/espresso/DragAction.java
index b2c8e38..afdc4d3 100644
--- a/core/tests/coretests/src/android/widget/espresso/DragAction.java
+++ b/core/tests/coretests/src/android/widget/espresso/DragAction.java
@@ -16,27 +16,30 @@
 
 package android.widget.espresso;
 
-import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
-import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+
 import static com.android.internal.util.Preconditions.checkNotNull;
+
 import static org.hamcrest.Matchers.allOf;
+
 import android.annotation.Nullable;
 import android.os.SystemClock;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.PerformException;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.action.CoordinatesProvider;
-import android.support.test.espresso.action.MotionEvents;
-import android.support.test.espresso.action.PrecisionDescriber;
-import android.support.test.espresso.action.Swiper;
-import android.support.test.espresso.util.HumanReadables;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
 
-import org.hamcrest.Matcher;
+import androidx.test.espresso.PerformException;
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.action.CoordinatesProvider;
+import androidx.test.espresso.action.MotionEvents;
+import androidx.test.espresso.action.PrecisionDescriber;
+import androidx.test.espresso.action.Swiper;
+import androidx.test.espresso.util.HumanReadables;
 
+import org.hamcrest.Matcher;
 
 /**
  * Drags on a View using touch events.<br>
diff --git a/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java b/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java
index 1693e54..1b849b5 100644
--- a/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java
@@ -16,22 +16,23 @@
 
 package android.widget.espresso;
 
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.RootMatchers.isPlatformPopup;
-import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
-import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup;
+import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
+import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
 
 import static org.hamcrest.Matchers.allOf;
 
-import android.support.test.espresso.NoMatchingRootException;
-import android.support.test.espresso.NoMatchingViewException;
-import android.support.test.espresso.ViewInteraction;
 import android.widget.Editor;
 
+import androidx.test.espresso.NoMatchingRootException;
+import androidx.test.espresso.NoMatchingViewException;
+import androidx.test.espresso.ViewInteraction;
+
 public final class DragHandleUtils {
 
     private DragHandleUtils() {}
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index 0355f82..d45d4b0 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -16,30 +16,31 @@
 
 package android.widget.espresso;
 
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.RootMatchers.isPlatformPopup;
-import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
-import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withTagValue;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup;
+import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
+import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withTagValue;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.is;
 
-import android.support.test.espresso.NoMatchingRootException;
-import android.support.test.espresso.NoMatchingViewException;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.ViewInteraction;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.test.espresso.NoMatchingRootException;
+import androidx.test.espresso.NoMatchingViewException;
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.ViewInteraction;
+
 import com.android.internal.widget.FloatingToolbar;
 
 import org.hamcrest.Description;
diff --git a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
index b50d6f4..f56af5a 100644
--- a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
+++ b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
@@ -16,18 +16,19 @@
 
 package android.widget.espresso;
 
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.action.CoordinatesProvider;
-import android.support.test.espresso.action.MotionEvents;
-import android.support.test.espresso.action.MotionEvents.DownResultHolder;
-import android.support.test.espresso.action.Press;
-import android.support.test.espresso.action.Tapper;
 import android.view.InputDevice;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
 
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.action.CoordinatesProvider;
+import androidx.test.espresso.action.MotionEvents;
+import androidx.test.espresso.action.MotionEvents.DownResultHolder;
+import androidx.test.espresso.action.Press;
+import androidx.test.espresso.action.Tapper;
+
 import org.hamcrest.Matcher;
 
 /**
diff --git a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
index 022be76..abee736 100644
--- a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
+++ b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
@@ -18,16 +18,19 @@
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 import android.annotation.IntDef;
-import android.support.test.espresso.InjectEventSecurityException;
-import android.support.test.espresso.UiController;
+import android.os.SystemClock;
 import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
+import androidx.test.espresso.InjectEventSecurityException;
+import androidx.test.espresso.UiController;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Iterator;
+
 /**
  * Class to wrap an UiController to overwrite source of motion events to SOURCE_MOUSE.
  * Note that this doesn't change the tool type.
@@ -71,6 +74,32 @@
         return mUiController.injectMotionEvent(event);
     }
 
+    /**
+     * Copied from latest {@link androidx.test.espresso.UiController}, since current
+     * {@link androidx.test.espresso.UiController#injectMotionEventSequence(Iterable)} seems not a
+     * default method.
+     */
+    @Override
+    public boolean injectMotionEventSequence(Iterable<MotionEvent> events)
+            throws InjectEventSecurityException {
+        android.util.Log.w(
+                "UIC",
+                "Using default injectMotionEventSeq() - this may not inject a sequence properly. "
+                + "If wrapping UIController please override this method and delegate.");
+        Iterator<MotionEvent> mei = events.iterator();
+        boolean success = true;
+        while (mei.hasNext()) {
+            MotionEvent me = mei.next();
+            if (me.getEventTime() - SystemClock.uptimeMillis() > 10) {
+                // Because the loopMainThreadForAtLeast is overkill for waiting, intentially only
+                // call it with a smaller amount of milliseconds as best effort
+                loopMainThreadForAtLeast(10);
+            }
+            success &= injectMotionEvent(me);
+        }
+        return success;
+    }
+
     @Override
     public boolean injectString(String str) throws InjectEventSecurityException {
         return mUiController.injectString(str);
diff --git a/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java b/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java
index b5a96ae..32c02403 100644
--- a/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java
@@ -16,36 +16,40 @@
 
 package android.widget.espresso;
 
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
-import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
+import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.view.View;
+
+import androidx.test.espresso.NoMatchingRootException;
+import androidx.test.espresso.NoMatchingViewException;
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.ViewInteraction;
+import androidx.test.espresso.action.GeneralLocation;
+import androidx.test.espresso.action.Press;
+import androidx.test.espresso.action.Tap;
 
 import org.hamcrest.Matcher;
 
-import android.support.test.espresso.NoMatchingRootException;
-import android.support.test.espresso.NoMatchingViewException;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.ViewInteraction;
-import android.support.test.espresso.action.GeneralLocation;
-import android.support.test.espresso.action.Press;
-import android.support.test.espresso.action.Tap;
-import android.view.View;
-
 public final class SuggestionsPopupwindowUtils {
     private static final int id = com.android.internal.R.id.suggestionWindowContainer;
 
     private SuggestionsPopupwindowUtils() {};
 
     public static ViewInteraction onSuggestionsPopup() {
+        getInstrumentation().waitForIdleSync();
         return onView(withId(id)).inRoot(withDecorView(hasDescendant(withId(id))));
     }
 
     private static ViewInteraction onSuggestionsPopupItem(Matcher<View> matcher) {
+        getInstrumentation().waitForIdleSync();
         return onView(matcher).inRoot(withDecorView(hasDescendant(withId(id))));
     }
 
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
index b4c547e..83ce67b 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -16,16 +16,9 @@
 
 package android.widget.espresso;
 
-import static android.support.test.espresso.action.ViewActions.actionWithAssertions;
+import static androidx.test.espresso.action.ViewActions.actionWithAssertions;
 
 import android.graphics.Rect;
-import android.support.test.espresso.PerformException;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.action.CoordinatesProvider;
-import android.support.test.espresso.action.GeneralLocation;
-import android.support.test.espresso.action.Press;
-import android.support.test.espresso.action.Tap;
-import android.support.test.espresso.util.HumanReadables;
 import android.text.Layout;
 import android.view.MotionEvent;
 import android.view.View;
@@ -33,6 +26,14 @@
 import android.widget.Editor.HandleView;
 import android.widget.TextView;
 
+import androidx.test.espresso.PerformException;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.action.CoordinatesProvider;
+import androidx.test.espresso.action.GeneralLocation;
+import androidx.test.espresso.action.Press;
+import androidx.test.espresso.action.Tap;
+import androidx.test.espresso.util.HumanReadables;
+
 /**
  * A collection of actions on a {@link android.widget.TextView}.
  */
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
index 5ed69e0..d638da5 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
@@ -16,7 +16,7 @@
 
 package android.widget.espresso;
 
-import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
+import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
@@ -26,14 +26,15 @@
 import android.annotation.IntDef;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.support.test.espresso.NoMatchingViewException;
-import android.support.test.espresso.ViewAssertion;
 import android.text.Spanned;
 import android.text.TextUtils;
 import android.view.View;
 import android.widget.EditText;
 import android.widget.TextView;
 
+import androidx.test.espresso.NoMatchingViewException;
+import androidx.test.espresso.ViewAssertion;
+
 import junit.framework.AssertionFailedError;
 
 import org.hamcrest.Matcher;
diff --git a/core/tests/coretests/src/android/widget/espresso/ViewClickAction.java b/core/tests/coretests/src/android/widget/espresso/ViewClickAction.java
index 8bce1b0..1e6447d 100644
--- a/core/tests/coretests/src/android/widget/espresso/ViewClickAction.java
+++ b/core/tests/coretests/src/android/widget/espresso/ViewClickAction.java
@@ -16,17 +16,18 @@
 
 package android.widget.espresso;
 
-import org.hamcrest.Matcher;
-
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.action.CoordinatesProvider;
-import android.support.test.espresso.action.GeneralClickAction;
-import android.support.test.espresso.action.PrecisionDescriber;
-import android.support.test.espresso.action.Tapper;
 import android.view.View;
 import android.view.ViewConfiguration;
 
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.action.CoordinatesProvider;
+import androidx.test.espresso.action.GeneralClickAction;
+import androidx.test.espresso.action.PrecisionDescriber;
+import androidx.test.espresso.action.Tapper;
+
+import org.hamcrest.Matcher;
+
 public final class ViewClickAction implements ViewAction {
     private final GeneralClickAction mGeneralClickAction;
 
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index fe2fb85..7430c7a 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -16,12 +16,12 @@
 
 package com.android.internal.app;
 
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
 import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
 
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
index 3ddd8aa..64b7c2c 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
@@ -22,8 +22,9 @@
 
 import android.os.Process;
 import android.os.SystemClock;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
 
 import com.android.internal.os.KernelCpuThreadReader.ProcessCpuUsage;
 import com.android.internal.os.KernelCpuThreadReader.ThreadCpuUsage;
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/NotificationVisibilityTest.java b/core/tests/coretests/src/com/android/internal/statusbar/NotificationVisibilityTest.java
index b740ecc..22432a8 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/NotificationVisibilityTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/NotificationVisibilityTest.java
@@ -19,8 +19,8 @@
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertThat;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 035ee10..bb47658 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -58,6 +58,14 @@
 }
 
 prebuilt_etc {
+    name: "privapp_whitelist_com.android.dialer",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.dialer.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
     name: "privapp_whitelist_com.android.launcher3",
     product_specific: true,
     sub_dir: "permissions",
diff --git a/data/etc/com.android.dialer.xml b/data/etc/com.android.dialer.xml
new file mode 100644
index 0000000..ccdb21f
--- /dev/null
+++ b/data/etc/com.android.dialer.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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
+  -->
+<permissions>
+    <privapp-permissions package="com.android.dialer">
+        <permission name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"/>
+        <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+        <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.STATUS_BAR"/>
+        <permission name="android.permission.STOP_APP_SWITCHES"/>
+        <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
+        <permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 2e7b998..4ef5adb 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -49,17 +49,6 @@
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.dialer">
-        <permission name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"/>
-        <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
-        <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
-        <permission name="android.permission.MODIFY_PHONE_STATE"/>
-        <permission name="android.permission.STATUS_BAR"/>
-        <permission name="android.permission.STOP_APP_SWITCHES"/>
-        <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
-        <permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.emergency">
         <!-- Required to place emergency calls from emergency info screen. -->
         <permission name="android.permission.CALL_PRIVILEGED"/>
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index b67aea2..d945635 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -142,10 +142,8 @@
 void SkiaRecordingCanvas::drawWebViewFunctor(int functor) {
     FunctorDrawable* functorDrawable;
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
-        // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
-        // interop is disabled.
         functorDrawable =
-                mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor, asSkCanvas());
+                mDisplayList->allocateDrawable<VkFunctorDrawable>(functor, asSkCanvas());
     } else {
         functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, asSkCanvas());
     }
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 2f8d381..fe2d41e 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -43,7 +43,9 @@
         , mImageInfo(image_info) {}
 
 VkFunctorDrawHandler::~VkFunctorDrawHandler() {
-    mFunctorHandle->postDrawVk();
+    if (mDrawn) {
+        mFunctorHandle->postDrawVk();
+    }
 }
 
 void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) {
@@ -77,6 +79,7 @@
     params.format = vulkan_info.fFormat;
 
     mFunctorHandle->drawVk(params);
+    mDrawn = true;
 
     vulkan_info.fDrawBounds->offset.x = mClip.fLeft;
     vulkan_info.fDrawBounds->offset.y = mClip.fTop;
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
index 1a53c8f..d3f9777 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
@@ -44,6 +44,8 @@
     const SkMatrix mMatrix;
     const SkIRect mClip;
     const SkImageInfo mImageInfo;
+
+    bool mDrawn = false;
 };
 
 /**
diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp
index 6deb47f..e43a60c 100644
--- a/native/webview/plat_support/draw_functor.cpp
+++ b/native/webview/plat_support/draw_functor.cpp
@@ -177,9 +177,6 @@
         webview_functor_callbacks.vk.initialize = &initializeVk;
         webview_functor_callbacks.vk.draw = &drawVk;
         webview_functor_callbacks.vk.postDraw = &postDrawVk;
-        // TODO(boliu): Remove this once SkiaRecordingCanvas::drawWebViewFunctor
-        // no longer uses GL interop.
-        webview_functor_callbacks.gles.draw = &draw_gl;
         break;
     }
     callbacks_initialized = true;
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 815ae9a..866b46f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -631,6 +631,14 @@
                   android:exported="true">
         </provider>
 
+        <!-- Provides list and realistic previews of clock faces for the picker app. -->
+        <provider
+            android:name="com.android.keyguard.clock.ClockOptionsProvider"
+            android:authorities="com.android.keyguard.clock"
+            android:exported="true"
+            android:grantUriPermissions="true">
+        </provider>
+
         <receiver
             android:name=".statusbar.KeyboardShortcutsReceiver">
             <intent-filter>
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_preview.png
new file mode 100644
index 0000000..67f072f
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_preview.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png
new file mode 100644
index 0000000..8d0e6ed
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png
new file mode 100644
index 0000000..035a4df
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png
new file mode 100644
index 0000000..1ac0113
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png
new file mode 100644
index 0000000..63927bc
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png
new file mode 100644
index 0000000..83d714b
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png
new file mode 100644
index 0000000..a538149
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png
new file mode 100644
index 0000000..2bfd655
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 41acf82..1f33307 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -485,4 +485,17 @@
         <item>Fifty\nNine</item>
     </string-array>
 
+    <!-- Title for default clock face that will appear in the picker app next to a preview image of
+         the clock face. [CHAR LIMIT=8] -->
+    <string name="clock_title_default" translatable="false">Default</string>
+    <!-- Title for Bubble clock face that will appear in the picker app next to a preview image of
+         the clock face. [CHAR LIMIT=8] -->
+    <string name="clock_title_bubble" translatable="false">Bubble</string>
+    <!-- Title for Stretch clock face that will appear in the picker app next to a preview image of
+         the clock face. [CHAR LIMIT=8] -->
+    <string name="clock_title_stretch" translatable="false">Stretch</string>
+    <!-- Title for Typographic clock face that will appear in the picker app next to a preview image of
+         the clock face. [CHAR LIMIT=8] -->
+    <string name="clock_title_type" translatable="false">Type</string>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java
new file mode 100644
index 0000000..812f215
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import android.graphics.Bitmap;
+
+import java.util.function.Supplier;
+
+/**
+ * Metadata about an available clock face.
+ */
+final class ClockInfo {
+
+    private final String mName;
+    private final String mTitle;
+    private final String mId;
+    private final Supplier<Bitmap> mThumbnail;
+    private final Supplier<Bitmap> mPreview;
+
+    private ClockInfo(String name, String title, String id,
+            Supplier<Bitmap> thumbnail, Supplier<Bitmap> preview) {
+        mName = name;
+        mTitle = title;
+        mId = id;
+        mThumbnail = thumbnail;
+        mPreview = preview;
+    }
+
+    /**
+     * Gets the non-internationalized name for the clock face.
+     */
+    String getName() {
+        return mName;
+    }
+
+    /**
+     * Gets the name (title) of the clock face to be shown in the picker app.
+     */
+    String getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Gets the ID of the clock face, used by the picker to set the current selection.
+     */
+    String getId() {
+        return mId;
+    }
+
+    /**
+     * Gets a thumbnail image of the clock.
+     */
+    Bitmap getThumbnail() {
+        return mThumbnail.get();
+    }
+
+    /**
+     * Gets a potentially realistic preview image of the clock face.
+     */
+    Bitmap getPreview() {
+        return mPreview.get();
+    }
+
+    static Builder builder() {
+        return new Builder();
+    }
+
+    static class Builder {
+        private String mName;
+        private String mTitle;
+        private String mId;
+        private Supplier<Bitmap> mThumbnail;
+        private Supplier<Bitmap> mPreview;
+
+        public ClockInfo build() {
+            return new ClockInfo(mName, mTitle, mId, mThumbnail, mPreview);
+        }
+
+        public Builder setName(String name) {
+            mName = name;
+            return this;
+        }
+
+        public Builder setTitle(String title) {
+            mTitle = title;
+            return this;
+        }
+
+        public Builder setId(String id) {
+            mId = id;
+            return this;
+        }
+
+        public Builder setThumbnail(Supplier<Bitmap> thumbnail) {
+            mThumbnail = thumbnail;
+            return this;
+        }
+
+        public Builder setPreview(Supplier<Bitmap> preview) {
+            mPreview = preview;
+            return this;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 3217ca6..9598142 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -17,12 +17,15 @@
 
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.graphics.BitmapFactory;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings;
 import android.view.LayoutInflater;
 
+import com.android.keyguard.R;
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.statusbar.policy.ExtensionController;
 import com.android.systemui.statusbar.policy.ExtensionController.Extension;
@@ -45,6 +48,7 @@
     private final LayoutInflater mLayoutInflater;
     private final ContentResolver mContentResolver;
 
+    private final List<ClockInfo> mClockInfos = new ArrayList<>();
     /**
      * Observe settings changes to know when to switch the clock face.
      */
@@ -76,6 +80,36 @@
         mExtensionController = extensionController;
         mLayoutInflater = LayoutInflater.from(context);
         mContentResolver = context.getContentResolver();
+
+        Resources res = context.getResources();
+        mClockInfos.add(ClockInfo.builder()
+                .setName("default")
+                .setTitle(res.getString(R.string.clock_title_default))
+                .setId("default")
+                .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.default_thumbnail))
+                .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.default_preview))
+                .build());
+        mClockInfos.add(ClockInfo.builder()
+                .setName("bubble")
+                .setTitle(res.getString(R.string.clock_title_bubble))
+                .setId(BubbleClockController.class.getName())
+                .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.bubble_thumbnail))
+                .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.bubble_preview))
+                .build());
+        mClockInfos.add(ClockInfo.builder()
+                .setName("stretch")
+                .setTitle(res.getString(R.string.clock_title_stretch))
+                .setId(StretchAnalogClockController.class.getName())
+                .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.stretch_thumbnail))
+                .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.stretch_preview))
+                .build());
+        mClockInfos.add(ClockInfo.builder()
+                .setName("type")
+                .setTitle(res.getString(R.string.clock_title_type))
+                .setId(TypeClockController.class.getName())
+                .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.type_thumbnail))
+                .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.type_preview))
+                .build());
     }
 
     /**
@@ -101,6 +135,13 @@
         }
     }
 
+    /**
+     * Get information about available clock faces.
+     */
+    List<ClockInfo> getClockInfos() {
+        return mClockInfos;
+    }
+
     private void setClockPlugin(ClockPlugin plugin) {
         for (int i = 0; i < mListeners.size(); i++) {
             // It probably doesn't make sense to supply the same plugin instances to multiple
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java
new file mode 100644
index 0000000..5ef35be
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+
+import java.io.FileNotFoundException;
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * Exposes custom clock face options and provides realistic preview images.
+ *
+ * APIs:
+ *
+ *   /list_options: List the available clock faces, which has the following columns
+ *     name: name of the clock face
+ *     title: title of the clock face
+ *     id: value used to set the clock face
+ *     thumbnail: uri of the thumbnail image, should be /thumbnail/{name}
+ *     preview: uri of the preview image, should be /preview/{name}
+ *
+ *   /thumbnail/{id}: Opens a file stream for the thumbnail image for clock face {id}.
+ *
+ *   /preview/{id}: Opens a file stream for the preview image for clock face {id}.
+ */
+public final class ClockOptionsProvider extends ContentProvider {
+
+    private static final String TAG = "ClockOptionsProvider";
+    private static final String KEY_LIST_OPTIONS = "/list_options";
+    private static final String KEY_PREVIEW = "preview";
+    private static final String KEY_THUMBNAIL = "thumbnail";
+    private static final String COLUMN_NAME = "name";
+    private static final String COLUMN_TITLE = "title";
+    private static final String COLUMN_ID = "id";
+    private static final String COLUMN_THUMBNAIL = "thumbnail";
+    private static final String COLUMN_PREVIEW = "preview";
+    private static final String MIME_TYPE_PNG = "image/png";
+    private static final String CONTENT_SCHEME = "content";
+    private static final String AUTHORITY = "com.android.keyguard.clock";
+
+    private final Supplier<List<ClockInfo>> mClocksSupplier;
+
+    public ClockOptionsProvider() {
+        this(() -> Dependency.get(ClockManager.class).getClockInfos());
+    }
+
+    @VisibleForTesting
+    ClockOptionsProvider(Supplier<List<ClockInfo>> clocksSupplier) {
+        mClocksSupplier = clocksSupplier;
+    }
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        List<String> segments = uri.getPathSegments();
+        if (segments.size() > 0 && (KEY_PREVIEW.equals(segments.get(0))
+                || KEY_THUMBNAIL.equals(segments.get(0)))) {
+            return MIME_TYPE_PNG;
+        }
+        return "vnd.android.cursor.dir/clock_faces";
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        if (!KEY_LIST_OPTIONS.equals(uri.getPath())) {
+            return null;
+        }
+        MatrixCursor cursor = new MatrixCursor(new String[] {
+                COLUMN_NAME, COLUMN_TITLE, COLUMN_ID, COLUMN_THUMBNAIL, COLUMN_PREVIEW});
+        List<ClockInfo> clocks = mClocksSupplier.get();
+        for (int i = 0; i < clocks.size(); i++) {
+            ClockInfo clock = clocks.get(i);
+            cursor.newRow()
+                    .add(COLUMN_NAME, clock.getName())
+                    .add(COLUMN_TITLE, clock.getTitle())
+                    .add(COLUMN_ID, clock.getId())
+                    .add(COLUMN_THUMBNAIL, createThumbnailUri(clock))
+                    .add(COLUMN_PREVIEW, createPreviewUri(clock));
+        }
+        return cursor;
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues initialValues) {
+        return null;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+        List<String> segments = uri.getPathSegments();
+        if (segments.size() != 2 || !(KEY_PREVIEW.equals(segments.get(0))
+                || KEY_THUMBNAIL.equals(segments.get(0)))) {
+            throw new FileNotFoundException("Invalid preview url");
+        }
+        String id = segments.get(1);
+        if (TextUtils.isEmpty(id)) {
+            throw new FileNotFoundException("Invalid preview url, missing id");
+        }
+        ClockInfo clock = null;
+        List<ClockInfo> clocks = mClocksSupplier.get();
+        for (int i = 0; i < clocks.size(); i++) {
+            if (id.equals(clocks.get(i).getId())) {
+                clock = clocks.get(i);
+                break;
+            }
+        }
+        if (clock == null) {
+            throw new FileNotFoundException("Invalid preview url, id not found");
+        }
+        return openPipeHelper(uri, MIME_TYPE_PNG, null, KEY_PREVIEW.equals(segments.get(0))
+                ? clock.getPreview() : clock.getThumbnail(), new MyWriter());
+    }
+
+    private Uri createThumbnailUri(ClockInfo clock) {
+        return new Uri.Builder()
+                .scheme(CONTENT_SCHEME)
+                .authority(AUTHORITY)
+                .appendPath(KEY_THUMBNAIL)
+                .appendPath(clock.getId())
+                .build();
+    }
+
+    private Uri createPreviewUri(ClockInfo clock) {
+        return new Uri.Builder()
+                .scheme(CONTENT_SCHEME)
+                .authority(AUTHORITY)
+                .appendPath(KEY_PREVIEW)
+                .appendPath(clock.getId())
+                .build();
+    }
+
+    private static class MyWriter implements ContentProvider.PipeDataWriter<Bitmap> {
+        @Override
+        public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
+                Bundle opts, Bitmap bitmap) {
+            try (AutoCloseOutputStream os = new AutoCloseOutputStream(output)) {
+                bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
+            } catch (Exception e) {
+                Log.w(TAG, "fail to write to pipe", e);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 404f2e5..9a9a52f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -186,7 +186,12 @@
      * Set a listener to be notified of bubble expand events.
      */
     public void setExpandListener(BubbleExpandListener listener) {
-        mExpandListener = listener;
+        mExpandListener = ((isExpanding, key) -> {
+            if (listener != null) {
+                listener.onBubbleExpandChanged(isExpanding, key);
+            }
+            mStatusBarWindowController.setBubbleExpanded(isExpanding);
+        });
         if (mStackView != null) {
             mStackView.setExpandListener(mExpandListener);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index ffaa236..86e17f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -202,7 +202,8 @@
     private void applyFocusableFlag(State state) {
         boolean panelFocusable = state.statusBarFocusable && state.panelExpanded;
         if (state.bouncerShowing && (state.keyguardOccluded || state.keyguardNeedsInput)
-                || ENABLE_REMOTE_INPUT && state.remoteInputActive) {
+                || ENABLE_REMOTE_INPUT && state.remoteInputActive
+                || state.bubbleExpanded) {
             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
         } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
@@ -486,6 +487,21 @@
         return mCurrentState.bubblesShowing;
     }
 
+    /**
+     * Sets if there is a bubble being expanded on the screen.
+     */
+    public void setBubbleExpanded(boolean bubbleExpanded) {
+        mCurrentState.bubbleExpanded = bubbleExpanded;
+        apply(mCurrentState);
+    }
+
+    /**
+     * The bubble is shown in expanded state for the status bar.
+     */
+    public boolean getBubbleExpanded() {
+        return mCurrentState.bubbleExpanded;
+    }
+
     public void setStateListener(OtherwisedCollapsedListener listener) {
         mListener = listener;
     }
@@ -539,6 +555,7 @@
         boolean wallpaperSupportsAmbientMode;
         boolean notTouchable;
         boolean bubblesShowing;
+        boolean bubbleExpanded;
 
         /**
          * The {@link StatusBar} state from the status bar.
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java
new file mode 100644
index 0000000..d2b2654
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Bitmap;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Supplier;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class ClockInfoTest extends SysuiTestCase {
+
+    @Mock
+    private Supplier<Bitmap> mMockSupplier;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testGetName() {
+        final String name = "name";
+        ClockInfo info = ClockInfo.builder().setName(name).build();
+        assertThat(info.getName()).isEqualTo(name);
+    }
+
+    @Test
+    public void testGetTitle() {
+        final String title = "title";
+        ClockInfo info = ClockInfo.builder().setTitle(title).build();
+        assertThat(info.getTitle()).isEqualTo(title);
+    }
+
+    @Test
+    public void testGetId() {
+        final String id = "id";
+        ClockInfo info = ClockInfo.builder().setId(id).build();
+        assertThat(info.getId()).isEqualTo(id);
+    }
+
+    @Test
+    public void testGetThumbnail() {
+        ClockInfo info = ClockInfo.builder().setThumbnail(mMockSupplier).build();
+        info.getThumbnail();
+        verify(mMockSupplier).get();
+    }
+
+    @Test
+    public void testGetPreview() {
+        ClockInfo info = ClockInfo.builder().setPreview(mMockSupplier).build();
+        info.getPreview();
+        verify(mMockSupplier).get();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java
new file mode 100644
index 0000000..0cd6f9a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class ClockOptionsProviderTest extends SysuiTestCase {
+
+    private static final String CONTENT_SCHEME = "content";
+    private static final String AUTHORITY = "com.android.keyguard.clock";
+    private static final String LIST_OPTIONS = "list_options";
+    private static final String PREVIEW = "preview";
+    private static final String THUMBNAIL = "thumbnail";
+    private static final String MIME_TYPE_LIST_OPTIONS = "vnd.android.cursor.dir/clock_faces";
+    private static final String MIME_TYPE_PNG = "image/png";
+    private static final String NAME_COLUMN = "name";
+    private static final String TITLE_COLUMN = "title";
+    private static final String ID_COLUMN = "id";
+    private static final String PREVIEW_COLUMN = "preview";
+    private static final String THUMBNAIL_COLUMN = "thumbnail";
+
+    private ClockOptionsProvider mProvider;
+    private Supplier<List<ClockInfo>> mMockSupplier;
+    private List<ClockInfo> mClocks;
+    private Uri mListOptionsUri;
+    @Mock
+    private Supplier<Bitmap> mMockBitmapSupplier;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mClocks = new ArrayList<>();
+        mProvider = new ClockOptionsProvider(() -> mClocks);
+        mListOptionsUri = new Uri.Builder()
+                .scheme(CONTENT_SCHEME)
+                .authority(AUTHORITY)
+                .appendPath(LIST_OPTIONS)
+                .build();
+    }
+
+    @Test
+    public void testGetType_listOptions() {
+        Uri uri = new Uri.Builder()
+                .scheme(CONTENT_SCHEME)
+                .authority(AUTHORITY)
+                .appendPath(LIST_OPTIONS)
+                .build();
+        assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_LIST_OPTIONS);
+    }
+
+    @Test
+    public void testGetType_preview() {
+        Uri uri = new Uri.Builder()
+                .scheme(CONTENT_SCHEME)
+                .authority(AUTHORITY)
+                .appendPath(PREVIEW)
+                .appendPath("id")
+                .build();
+        assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_PNG);
+    }
+
+    @Test
+    public void testGetType_thumbnail() {
+        Uri uri = new Uri.Builder()
+                .scheme(CONTENT_SCHEME)
+                .authority(AUTHORITY)
+                .appendPath(THUMBNAIL)
+                .appendPath("id")
+                .build();
+        assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_PNG);
+    }
+
+    @Test
+    public void testQuery_noClocks() {
+        Cursor cursor = mProvider.query(mListOptionsUri, null, null, null);
+        assertThat(cursor.getCount()).isEqualTo(0);
+    }
+
+    @Test
+    public void testQuery_listOptions() {
+        mClocks.add(ClockInfo.builder()
+                .setName("name_a")
+                .setTitle("title_a")
+                .setId("id_a")
+                .build());
+        mClocks.add(ClockInfo.builder()
+                .setName("name_b")
+                .setTitle("title_b")
+                .setId("id_b")
+                .build());
+        Cursor cursor = mProvider.query(mListOptionsUri, null, null, null);
+        assertThat(cursor.getCount()).isEqualTo(2);
+        cursor.moveToFirst();
+        assertThat(cursor.getString(
+                cursor.getColumnIndex(NAME_COLUMN))).isEqualTo("name_a");
+        assertThat(cursor.getString(
+                cursor.getColumnIndex(TITLE_COLUMN))).isEqualTo("title_a");
+        assertThat(cursor.getString(
+                cursor.getColumnIndex(ID_COLUMN))).isEqualTo("id_a");
+        assertThat(cursor.getString(
+                cursor.getColumnIndex(PREVIEW_COLUMN)))
+                .isEqualTo("content://com.android.keyguard.clock/preview/id_a");
+        assertThat(cursor.getString(
+                cursor.getColumnIndex(THUMBNAIL_COLUMN)))
+                .isEqualTo("content://com.android.keyguard.clock/thumbnail/id_a");
+        cursor.moveToNext();
+        assertThat(cursor.getString(
+                cursor.getColumnIndex(NAME_COLUMN))).isEqualTo("name_b");
+        assertThat(cursor.getString(
+                cursor.getColumnIndex(TITLE_COLUMN))).isEqualTo("title_b");
+        assertThat(cursor.getString(
+                cursor.getColumnIndex(ID_COLUMN))).isEqualTo("id_b");
+        assertThat(cursor.getString(
+                cursor.getColumnIndex(PREVIEW_COLUMN)))
+                .isEqualTo("content://com.android.keyguard.clock/preview/id_b");
+        assertThat(cursor.getString(
+                cursor.getColumnIndex(THUMBNAIL_COLUMN)))
+                .isEqualTo("content://com.android.keyguard.clock/thumbnail/id_b");
+    }
+
+    @Test
+    public void testOpenFile_preview() throws Exception {
+        mClocks.add(ClockInfo.builder()
+                .setId("id")
+                .setPreview(mMockBitmapSupplier)
+                .build());
+        Uri uri = new Uri.Builder()
+                .scheme(CONTENT_SCHEME)
+                .authority(AUTHORITY)
+                .appendPath(PREVIEW)
+                .appendPath("id")
+                .build();
+        mProvider.openFile(uri, "r").close();
+        verify(mMockBitmapSupplier).get();
+    }
+
+    @Test
+    public void testOpenFile_thumbnail() throws Exception {
+        mClocks.add(ClockInfo.builder()
+                .setId("id")
+                .setThumbnail(mMockBitmapSupplier)
+                .build());
+        Uri uri = new Uri.Builder()
+                .scheme(CONTENT_SCHEME)
+                .authority(AUTHORITY)
+                .appendPath(THUMBNAIL)
+                .appendPath("id")
+                .build();
+        mProvider.openFile(uri, "r").close();
+        verify(mMockBitmapSupplier).get();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index e32d48d..49b4641 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -168,12 +168,14 @@
         // We should have bubbles & their notifs should show in the shade
         assertTrue(mBubbleController.hasBubbles());
         assertTrue(mRow.getEntry().showInShadeWhenBubble());
+        assertFalse(mStatusBarWindowController.getBubbleExpanded());
 
         // Expand the stack
         BubbleStackView stackView = mBubbleController.getStackView();
         stackView.expandStack();
         assertTrue(mBubbleController.isStackExpanded());
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
+        assertTrue(mStatusBarWindowController.getBubbleExpanded());
 
         // Make sure it's no longer in the shade
         assertFalse(mRow.getEntry().showInShadeWhenBubble());
@@ -182,6 +184,7 @@
         stackView.collapseStack();
         verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key);
         assertFalse(mBubbleController.isStackExpanded());
+        assertFalse(mStatusBarWindowController.getBubbleExpanded());
     }
 
     @Test
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index d20508a..9d9721d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4697,7 +4697,7 @@
                     mSettings.getCurrentUserId(),
                     mContext.getBasePackageName());
             nextIme = mSettings.getSelectedInputMethod();
-            nextEnabledImes = getEnabledInputMethodList();
+            nextEnabledImes = mSettings.getEnabledInputMethodListLocked();
             final PrintWriter pr = shellCommand.getOutPrintWriter();
             pr.println("Reset current and enabled IMEs");
             pr.println("Newly selected IME:");
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index f736056..1dada92 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -17,8 +17,10 @@
 package com.android.server.os;
 
 import android.annotation.RequiresPermission;
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.content.Context;
+import android.content.pm.UserInfo;
 import android.os.Binder;
 import android.os.BugreportParams;
 import android.os.IDumpstate;
@@ -28,26 +30,29 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserManager;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
 import java.io.FileDescriptor;
 
 // TODO(b/111441001):
-// 1. Handle the case where another bugreport is in progress
-// 2. Make everything threadsafe
-// 3. Pass validation & other errors on listener
+// Intercept onFinished() & implement death recipient here and shutdown
+// bugreportd service.
 
 /**
  * Implementation of the service that provides a privileged API to capture and consume bugreports.
  *
- * <p>Delegates the actualy generation to a native implementation of {@code Dumpstate}.
+ * <p>Delegates the actualy generation to a native implementation of {@code IDumpstate}.
  */
 class BugreportManagerServiceImpl extends IDumpstate.Stub {
     private static final String TAG = "BugreportManagerService";
     private static final String BUGREPORT_SERVICE = "bugreportd";
     private static final long DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS = 30 * 1000;
 
-    private IDumpstate mDs = null;
+    private final Object mLock = new Object();
     private final Context mContext;
     private final AppOpsManager mAppOps;
 
@@ -59,43 +64,44 @@
     @Override
     @RequiresPermission(android.Manifest.permission.DUMP)
     public IDumpstateToken setListener(String name, IDumpstateListener listener,
-            boolean getSectionDetails) throws RemoteException {
-        // TODO(b/111441001): Figure out if lazy setting of listener should be allowed
-        // and if so how to handle it.
+            boolean getSectionDetails) {
         throw new UnsupportedOperationException("setListener is not allowed on this service");
     }
 
-    // TODO(b/111441001): Intercept onFinished here in system server and shutdown
-    // the bugreportd service.
     @Override
     @RequiresPermission(android.Manifest.permission.DUMP)
     public void startBugreport(int callingUidUnused, String callingPackage,
             FileDescriptor bugreportFd, FileDescriptor screenshotFd,
-            int bugreportMode, IDumpstateListener listener) throws RemoteException {
-        int callingUid = Binder.getCallingUid();
-        // TODO(b/111441001): validate all arguments & ensure primary user
-        validate(bugreportMode);
+            int bugreportMode, IDumpstateListener listener) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "startBugreport");
+        Preconditions.checkNotNull(callingPackage);
+        Preconditions.checkNotNull(bugreportFd);
+        Preconditions.checkNotNull(listener);
+        validateBugreportMode(bugreportMode);
+        ensureIsPrimaryUser();
 
+        int callingUid = Binder.getCallingUid();
         mAppOps.checkPackage(callingUid, callingPackage);
-        mDs = getDumpstateService();
-        if (mDs == null) {
-            Slog.w(TAG, "Unable to get bugreport service");
-            // TODO(b/111441001): pass error on listener
-            return;
+
+        synchronized (mLock) {
+            startBugreportLocked(callingUid, callingPackage, bugreportFd, screenshotFd,
+                    bugreportMode, listener);
         }
-        mDs.startBugreport(callingUid, callingPackage,
-                bugreportFd, screenshotFd, bugreportMode, listener);
     }
 
     @Override
     @RequiresPermission(android.Manifest.permission.DUMP)
-    public void cancelBugreport() throws RemoteException {
-        // This tells init to cancel bugreportd service.
-        SystemProperties.set("ctl.stop", BUGREPORT_SERVICE);
-        mDs = null;
+    public void cancelBugreport() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "startBugreport");
+        // This tells init to cancel bugreportd service. Note that this is achieved through setting
+        // a system property which is not thread-safe. So the lock here offers thread-safety only
+        // among callers of the API.
+        synchronized (mLock) {
+            SystemProperties.set("ctl.stop", BUGREPORT_SERVICE);
+        }
     }
 
-    private boolean validate(@BugreportParams.BugreportMode int mode) {
+    private void validateBugreportMode(@BugreportParams.BugreportMode int mode) {
         if (mode != BugreportParams.BUGREPORT_MODE_FULL
                 && mode != BugreportParams.BUGREPORT_MODE_INTERACTIVE
                 && mode != BugreportParams.BUGREPORT_MODE_REMOTE
@@ -103,9 +109,66 @@
                 && mode != BugreportParams.BUGREPORT_MODE_TELEPHONY
                 && mode != BugreportParams.BUGREPORT_MODE_WIFI) {
             Slog.w(TAG, "Unknown bugreport mode: " + mode);
-            return false;
+            throw new IllegalArgumentException("Unknown bugreport mode: " + mode);
         }
-        return true;
+    }
+
+    /**
+     * Validates that the current user is the primary user.
+     *
+     * @throws IllegalArgumentException if the current user is not the primary user
+     */
+    private void ensureIsPrimaryUser() {
+        UserInfo currentUser = null;
+        try {
+            currentUser = ActivityManager.getService().getCurrentUser();
+        } catch (RemoteException e) {
+            // Impossible to get RemoteException for an in-process call.
+        }
+
+        UserInfo primaryUser = UserManager.get(mContext).getPrimaryUser();
+        if (currentUser == null) {
+            logAndThrow("No current user. Only primary user is allowed to take bugreports.");
+        }
+        if (primaryUser == null) {
+            logAndThrow("No primary user. Only primary user is allowed to take bugreports.");
+        }
+        if (primaryUser.id != currentUser.id) {
+            logAndThrow("Current user not primary user. Only primary user"
+                    + " is allowed to take bugreports.");
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void startBugreportLocked(int callingUid, String callingPackage,
+            FileDescriptor bugreportFd, FileDescriptor screenshotFd,
+            int bugreportMode, IDumpstateListener listener) {
+        if (isDumpstateBinderServiceRunningLocked()) {
+            Slog.w(TAG, "'dumpstate' is already running. Cannot start a new bugreport"
+                    + " while another one is currently in progress.");
+            // TODO(b/111441001): Use a new error code; add this to the documentation of the API.
+            reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR);
+            return;
+        }
+
+        IDumpstate ds = startAndGetDumpstateBinderServiceLocked();
+        if (ds == null) {
+            Slog.w(TAG, "Unable to get bugreport service");
+            reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR);
+            return;
+        }
+        try {
+            ds.startBugreport(callingUid, callingPackage,
+                    bugreportFd, screenshotFd, bugreportMode, listener);
+        } catch (RemoteException e) {
+            reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private boolean isDumpstateBinderServiceRunningLocked() {
+        IDumpstate ds = IDumpstate.Stub.asInterface(ServiceManager.getService("dumpstate"));
+        return ds != null;
     }
 
     /*
@@ -115,8 +178,12 @@
      * <p>Generating bugreports requires root privileges. To limit the footprint
      * of the root access, the actual generation in Dumpstate binary is accessed as a
      * oneshot service 'bugreport'.
+     *
+     * <p>Note that starting the service is achieved through setting a system property, which is
+     * not thread-safe. So the lock here offers thread-safety only among callers of the API.
      */
-    private IDumpstate getDumpstateService() {
+    @GuardedBy("mLock")
+    private IDumpstate startAndGetDumpstateBinderServiceLocked() {
         // Start bugreport service.
         SystemProperties.set("ctl.start", BUGREPORT_SERVICE);
 
@@ -145,4 +212,18 @@
         }
         return ds;
     }
+
+    private void reportError(IDumpstateListener listener, int errorCode) {
+        try {
+            listener.onError(errorCode);
+        } catch (RemoteException e) {
+            // Something went wrong in binder or app process. There's nothing to do here.
+            Slog.w(TAG, "onError() transaction threw RemoteException: " + e.getMessage());
+        }
+    }
+
+    private void logAndThrow(String message) {
+        Slog.w(TAG, message);
+        throw new IllegalArgumentException(message);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a0a2f6c..e18da7f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3941,13 +3941,13 @@
                 if (apex != null) {
                     try {
                         final ApexInfo activePkg = apex.getActivePackage(packageName);
-                        if (activePkg != null) {
+                        if (activePkg != null && !TextUtils.isEmpty(activePkg.packagePath)) {
                             try {
                                 return PackageParser.generatePackageInfoFromApex(
                                         new File(activePkg.packagePath), true /* collect certs */);
                             } catch (PackageParserException pe) {
-                                throw new IllegalStateException("Unable to parse: " + activePkg,
-                                        pe);
+                                Log.e(TAG, "Unable to parse package at "
+                                        + activePkg.packagePath, pe);
                             }
                         }
                     } catch (RemoteException e) {
@@ -13426,6 +13426,10 @@
             return false;
         }
 
+        if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
+            return false;
+        }
+
         boolean ensureVerifyAppsEnabled = isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS);
 
         // Check if installing from ADB
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 0371663..cc6cfbb 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -186,6 +186,7 @@
     private void preRebootVerification(@NonNull PackageInstallerSession session) {
         boolean success = true;
 
+        // STOPSHIP: TODO(b/123753157): Verify APKs through Package Verifier.
         if (!sessionContainsApex(session)) {
             // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs,
             // right away.
@@ -336,6 +337,7 @@
 
         PackageInstaller.SessionParams params = originalSession.params.copy();
         params.isStaged = false;
+        params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
         int apkSessionId = mPi.createSession(
                 params, originalSession.getInstallerPackageName(), originalSession.userId);
         PackageInstallerSession apkSession = mPi.getSession(apkSessionId);
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 8e04160..3c6a54a 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -26,6 +26,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.text.TextUtils;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.StatsLog;
 
@@ -64,10 +65,8 @@
             return PackageHealthObserverImpact.USER_IMPACT_NONE;
         }
 
-        RollbackInfo rollback =
-                getAvailableMainlineRollback(mContext.getSystemService(RollbackManager.class),
-                        failedPackage, moduleMetadataPackage);
-        if (rollback == null) {
+        if (getAvailableRollback(mContext.getSystemService(RollbackManager.class),
+                        failedPackage, moduleMetadataPackage) == null) {
             // Don't handle the notification, no rollbacks available for the package
             return PackageHealthObserverImpact.USER_IMPACT_NONE;
         }
@@ -84,38 +83,46 @@
         }
 
         RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
-        RollbackInfo rollback = getAvailableMainlineRollback(rollbackManager,
+        Pair<RollbackInfo, Boolean> rollbackPair = getAvailableRollback(rollbackManager,
                 failedPackage, moduleMetadataPackage);
-        if (rollback == null) {
-            Slog.w(TAG, "Expected rollback but no mainline rollback found for package: [ "
+        if (rollbackPair == null) {
+            Slog.w(TAG, "Expected rollback but no valid rollback found for package: [ "
                     + failedPackage.getPackageName() + "] with versionCode: ["
                     + failedPackage.getVersionCode() + "]");
             return false;
         }
+        RollbackInfo rollback = rollbackPair.first;
+        // We only log mainline package rollbacks, so check if rollback contains the
+        // module metadata provider, if it does, the rollback is a mainline rollback
+        boolean hasModuleMetadataPackage = rollbackPair.second;
 
-        StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
-                StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
-                moduleMetadataPackage.getPackageName(),
-                moduleMetadataPackage.getVersionCode());
+        if (hasModuleMetadataPackage) {
+            StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+                    StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
+                    moduleMetadataPackage.getPackageName(),
+                    moduleMetadataPackage.getVersionCode());
+        }
         LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
-            int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
-                    RollbackManager.STATUS_FAILURE);
-            if (status == RollbackManager.STATUS_SUCCESS) {
-                StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
-                        StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
-                        moduleMetadataPackage.getPackageName(),
-                        moduleMetadataPackage.getVersionCode());
-            } else {
-                StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
-                        StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
-                        moduleMetadataPackage.getPackageName(),
-                        moduleMetadataPackage.getVersionCode());
+            if (hasModuleMetadataPackage) {
+                int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
+                        RollbackManager.STATUS_FAILURE);
+                if (status == RollbackManager.STATUS_SUCCESS) {
+                    StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+                            StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+                            moduleMetadataPackage.getPackageName(),
+                            moduleMetadataPackage.getVersionCode());
+                } else {
+                    StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+                            StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
+                            moduleMetadataPackage.getPackageName(),
+                            moduleMetadataPackage.getVersionCode());
+                }
             }
         });
 
         mHandler.post(() ->
                 rollbackManager.commitRollback(rollback.getRollbackId(),
-                    Collections.singletonList(moduleMetadataPackage),
+                    Collections.singletonList(failedPackage),
                     rollbackReceiver.getIntentSender()));
         // Assume rollback executed successfully
         return true;
@@ -134,7 +141,7 @@
         PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
     }
 
-    private RollbackInfo getAvailableMainlineRollback(RollbackManager rollbackManager,
+    private Pair<RollbackInfo, Boolean> getAvailableRollback(RollbackManager rollbackManager,
             VersionedPackage failedPackage, VersionedPackage moduleMetadataPackage) {
         for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) {
             // We only rollback mainline packages, so check if rollback contains the
@@ -149,8 +156,8 @@
                         && packageRollback.getVersionRolledBackFrom().getVersionCode()
                         == failedPackage.getVersionCode();
             }
-            if (hasModuleMetadataPackage && hasFailedPackage) {
-                return rollback;
+            if (hasFailedPackage) {
+                return new Pair<RollbackInfo, Boolean>(rollback, hasModuleMetadataPackage);
             }
         }
         return null;
@@ -159,7 +166,7 @@
     private VersionedPackage getModuleMetadataPackage() {
         String packageName = mContext.getResources().getString(
                 R.string.config_defaultModuleMetadataProvider);
-        if (!TextUtils.isEmpty(packageName)) {
+        if (TextUtils.isEmpty(packageName)) {
             return null;
         }
 
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 0f7407b..3586772 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1780,8 +1780,8 @@
             long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
         StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
         final long elapsedMillis = SystemClock.elapsedRealtime();
-        // Fails every 10 buckets.
-        if (mDebugFailingElapsedClockPullCount++ % 10 == 0) {
+        // Fails every 5 buckets.
+        if (mDebugFailingElapsedClockPullCount++ % 5 == 0) {
             mDebugFailingElapsedClockPreviousValue = elapsedMillis;
             throw new RuntimeException("Failing debug elapsed clock");
         }
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 3729eaf..5c7b287 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -127,6 +127,7 @@
         "android.frameworks.schedulerservice@1.0",
         "android.frameworks.sensorservice@1.0",
         "android.system.suspend@1.0",
+        "suspend_control_aidl_interface-cpp",
     ],
 
     static_libs: [
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 024760d..5c19ad3 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -32,8 +32,8 @@
 #include <android/hardware/power/1.0/IPower.h>
 #include <android/hardware/power/1.1/IPower.h>
 #include <android/hardware/power/stats/1.0/IPowerStats.h>
-#include <android/system/suspend/1.0/ISystemSuspend.h>
-#include <android/system/suspend/1.0/ISystemSuspendCallback.h>
+#include <android/system/suspend/BnSuspendCallback.h>
+#include <android/system/suspend/ISuspendControlService.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <jni.h>
 
@@ -46,14 +46,14 @@
 
 using android::hardware::Return;
 using android::hardware::Void;
+using android::system::suspend::BnSuspendCallback;
 using android::hardware::power::V1_0::PowerStatePlatformSleepState;
 using android::hardware::power::V1_0::PowerStateVoter;
 using android::hardware::power::V1_0::Status;
 using android::hardware::power::V1_1::PowerStateSubsystem;
 using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
 using android::hardware::hidl_vec;
-using android::system::suspend::V1_0::ISystemSuspend;
-using android::system::suspend::V1_0::ISystemSuspendCallback;
+using android::system::suspend::ISuspendControlService;
 using IPowerV1_1 = android::hardware::power::V1_1::IPower;
 using IPowerV1_0 = android::hardware::power::V1_0::IPower;
 
@@ -68,7 +68,7 @@
 extern sp<IPowerV1_0> getPowerHalV1_0();
 extern sp<IPowerV1_1> getPowerHalV1_1();
 extern bool processPowerHalReturn(const Return<void> &ret, const char* functionName);
-extern sp<ISystemSuspend> getSuspendHal();
+extern sp<ISuspendControlService> getSuspendControl();
 
 // Java methods used in getLowPowerStats
 static jmethodID jgetAndUpdatePlatformState = NULL;
@@ -103,17 +103,17 @@
 
 sp<PowerHalDeathRecipient> gDeathRecipient = new PowerHalDeathRecipient();
 
-class WakeupCallback : public ISystemSuspendCallback {
-public:
-    Return<void> notifyWakeup(bool success) override {
-        ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
+class WakeupCallback : public BnSuspendCallback {
+   public:
+    binder::Status notifyWakeup(bool success) override {
+        ALOGI("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
         int ret = sem_post(&wakeup_sem);
         if (ret < 0) {
             char buf[80];
             strerror_r(errno, buf, sizeof(buf));
             ALOGE("Error posting wakeup sem: %s\n", buf);
         }
-        return Void();
+        return binder::Status::ok();
     }
 };
 
@@ -136,9 +136,12 @@
             jniThrowException(env, "java/lang/IllegalStateException", buf);
             return -1;
         }
-        ALOGV("Registering callback...");
-        sp<ISystemSuspend> suspendHal = getSuspendHal();
-        suspendHal->registerCallback(new WakeupCallback());
+        sp<ISuspendControlService> suspendControl = getSuspendControl();
+        bool isRegistered = false;
+        suspendControl->registerCallback(new WakeupCallback(), &isRegistered);
+        if (!isRegistered) {
+            ALOGE("Failed to register wakeup callback");
+        }
     }
 
     // Wait for wakeup.
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 0c9b5f4..9be728b 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -20,6 +20,7 @@
 
 #include <android/hardware/power/1.1/IPower.h>
 #include <android/system/suspend/1.0/ISystemSuspend.h>
+#include <android/system/suspend/ISuspendControlService.h>
 #include <nativehelper/JNIHelp.h>
 #include "jni.h"
 
@@ -30,13 +31,14 @@
 #include <android-base/chrono_utils.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
+#include <binder/IServiceManager.h>
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+#include <hidl/ServiceManagement.h>
 #include <utils/Timers.h>
 #include <utils/misc.h>
 #include <utils/String8.h>
 #include <utils/Log.h>
-#include <hardware/power.h>
-#include <hardware_legacy/power.h>
-#include <hidl/ServiceManagement.h>
 
 #include "com_android_server_power_PowerManagerService.h"
 
@@ -48,6 +50,7 @@
 using android::system::suspend::V1_0::ISystemSuspend;
 using android::system::suspend::V1_0::IWakeLock;
 using android::system::suspend::V1_0::WakeLockType;
+using android::system::suspend::ISuspendControlService;
 using IPowerV1_1 = android::hardware::power::V1_1::IPower;
 using IPowerV1_0 = android::hardware::power::V1_0::IPower;
 
@@ -176,6 +179,7 @@
 }
 
 static sp<ISystemSuspend> gSuspendHal = nullptr;
+static sp<ISuspendControlService> gSuspendControl = nullptr;
 static sp<IWakeLock> gSuspendBlocker = nullptr;
 static std::mutex gSuspendMutex;
 
@@ -191,18 +195,33 @@
     return gSuspendHal;
 }
 
+sp<ISuspendControlService> getSuspendControl() {
+    static std::once_flag suspendControlFlag;
+    std::call_once(suspendControlFlag, [](){
+        while(gSuspendControl == nullptr) {
+            sp<IBinder> control =
+                    defaultServiceManager()->getService(String16("suspend_control"));
+            if (control != nullptr) {
+                gSuspendControl = interface_cast<ISuspendControlService>(control);
+            }
+        }
+    });
+    return gSuspendControl;
+}
+
 void enableAutoSuspend() {
     static bool enabled = false;
-
-    std::lock_guard<std::mutex> lock(gSuspendMutex);
     if (!enabled) {
-        sp<ISystemSuspend> suspendHal = getSuspendHal();
-        suspendHal->enableAutosuspend();
-        enabled = true;
+        sp<ISuspendControlService> suspendControl = getSuspendControl();
+        suspendControl->enableAutosuspend(&enabled);
     }
-    if (gSuspendBlocker) {
-        gSuspendBlocker->release();
-        gSuspendBlocker.clear();
+
+    {
+        std::lock_guard<std::mutex> lock(gSuspendMutex);
+        if (gSuspendBlocker) {
+            gSuspendBlocker->release();
+            gSuspendBlocker.clear();
+        }
     }
 }
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index caeedee..ab30cda 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1285,47 +1285,45 @@
             }
             traceEnd();
 
-            if (!mOnlyCore) {
-                if (context.getPackageManager().hasSystemFeature(
-                        PackageManager.FEATURE_WIFI)) {
-                    // Wifi Service must be started first for wifi-related services.
-                    traceBeginAndSlog("StartWifi");
-                    mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
-                    traceEnd();
-                    traceBeginAndSlog("StartWifiScanning");
-                    mSystemServiceManager.startService(
-                            "com.android.server.wifi.scanner.WifiScanningService");
-                    traceEnd();
-                }
+            if (context.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_WIFI)) {
+                // Wifi Service must be started first for wifi-related services.
+                traceBeginAndSlog("StartWifi");
+                mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+                traceEnd();
+                traceBeginAndSlog("StartWifiScanning");
+                mSystemServiceManager.startService(
+                        "com.android.server.wifi.scanner.WifiScanningService");
+                traceEnd();
+            }
 
-                if (context.getPackageManager().hasSystemFeature(
-                        PackageManager.FEATURE_WIFI_RTT)) {
-                    traceBeginAndSlog("StartRttService");
-                    mSystemServiceManager.startService(
-                            "com.android.server.wifi.rtt.RttService");
-                    traceEnd();
-                }
+            if (context.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_WIFI_RTT)) {
+                traceBeginAndSlog("StartRttService");
+                mSystemServiceManager.startService(
+                        "com.android.server.wifi.rtt.RttService");
+                traceEnd();
+            }
 
-                if (context.getPackageManager().hasSystemFeature(
-                        PackageManager.FEATURE_WIFI_AWARE)) {
-                    traceBeginAndSlog("StartWifiAware");
-                    mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
-                    traceEnd();
-                }
+            if (context.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_WIFI_AWARE)) {
+                traceBeginAndSlog("StartWifiAware");
+                mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
+                traceEnd();
+            }
 
-                if (context.getPackageManager().hasSystemFeature(
-                        PackageManager.FEATURE_WIFI_DIRECT)) {
-                    traceBeginAndSlog("StartWifiP2P");
-                    mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
-                    traceEnd();
-                }
+            if (context.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_WIFI_DIRECT)) {
+                traceBeginAndSlog("StartWifiP2P");
+                mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
+                traceEnd();
+            }
 
-                if (context.getPackageManager().hasSystemFeature(
-                        PackageManager.FEATURE_LOWPAN)) {
-                    traceBeginAndSlog("StartLowpan");
-                    mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS);
-                    traceEnd();
-                }
+            if (context.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_LOWPAN)) {
+                traceBeginAndSlog("StartLowpan");
+                mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS);
+                traceEnd();
             }
 
             if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index f5b4308..37caeb2 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -58,6 +58,8 @@
         "util.cc",
         "layout_validation.cc",
     ],
+    // b/123880763, clang-tidy analyzer has segmentation fault with dex_builder.cc
+    tidy_checks: ["-clang-analyzer-*"],
     host_supported: true,
 }
 
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
index 4da79b3..b407b2a 100644
--- a/telephony/java/android/telephony/AvailableNetworkInfo.java
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -114,7 +114,7 @@
         in.readStringList(mMccMncs);
     }
 
-    public AvailableNetworkInfo(int subId, int priority, ArrayList<String> mccMncs) {
+    public AvailableNetworkInfo(int subId, int priority, List<String> mccMncs) {
         mSubId = subId;
         mPriority = priority;
         mMccMncs = new ArrayList<String>(mccMncs);
diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java
index a4cce9c..0d4f09f 100644
--- a/telephony/java/android/telephony/CallAttributes.java
+++ b/telephony/java/android/telephony/CallAttributes.java
@@ -117,9 +117,9 @@
 
         CallAttributes s = (CallAttributes) o;
 
-        return (mPreciseCallState == s.mPreciseCallState
+        return (Objects.equals(mPreciseCallState, s.mPreciseCallState)
                 && mNetworkType == s.mNetworkType
-                && mCallQuality == s.mCallQuality);
+                && Objects.equals(mCallQuality, s.mCallQuality));
     }
 
     /**
diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java
index 59f3e1f..19e1931 100644
--- a/telephony/java/android/telephony/PreciseCallState.java
+++ b/telephony/java/android/telephony/PreciseCallState.java
@@ -287,11 +287,11 @@
             return false;
         }
         PreciseCallState other = (PreciseCallState) obj;
-        return (mRingingCallState != other.mRingingCallState &&
-            mForegroundCallState != other.mForegroundCallState &&
-            mBackgroundCallState != other.mBackgroundCallState &&
-            mDisconnectCause != other.mDisconnectCause &&
-            mPreciseDisconnectCause != other.mPreciseDisconnectCause);
+        return (mRingingCallState == other.mRingingCallState
+                && mForegroundCallState == other.mForegroundCallState
+                && mBackgroundCallState == other.mBackgroundCallState
+                && mDisconnectCause == other.mDisconnectCause
+                && mPreciseDisconnectCause == other.mPreciseDisconnectCause);
     }
 
     @Override
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7c3bde4..f2a9340 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7974,9 +7974,7 @@
      * support for the feature and device firmware support.
      *
      * @return {@code true} if the device and carrier both support RTT, {@code false} otherwise.
-     * @hide
      */
-    @TestApi
     public boolean isRttSupported() {
         try {
             ITelephony telephony = getITelephony();
@@ -9702,10 +9700,10 @@
      *
      * <p>
      * Requires Permission:
-     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isOpportunisticNetworkEnabled() {
         String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
         boolean isEnabled = false;
@@ -10093,12 +10091,17 @@
      * Get preferred opportunistic data subscription Id
      *
      * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}),
-     * or has permission {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}.
+     * or has either READ_PRIVILEGED_PHONE_STATE
+     * or {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission.
      * @return subId preferred opportunistic subscription id or
      * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} if there are no preferred
      * subscription id
      *
      */
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PHONE_STATE
+    })
     public int getPreferredOpportunisticDataSubscription() {
         String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
         int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 4d95e55..d8d2d9e 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -465,7 +465,7 @@
     public static final int CODE_USER_REJECTED_SESSION_MODIFICATION = 511;
 
     /**
-    * Upgrade Downgrade request cacncelled by the user who initiated it
+    * Upgrade Downgrade request cancelled by the user who initiated it
     */
     public static final int CODE_USER_CANCELLED_SESSION_MODIFICATION = 512;
 
@@ -887,6 +887,185 @@
     public static final int CODE_OEM_CAUSE_15 = 0xf00f;
 
     /**
+     * @hide
+     */
+    @IntDef(value = {
+            CODE_UNSPECIFIED,
+            CODE_LOCAL_ILLEGAL_ARGUMENT,
+            CODE_LOCAL_ILLEGAL_STATE,
+            CODE_LOCAL_INTERNAL_ERROR,
+            CODE_LOCAL_IMS_SERVICE_DOWN,
+            CODE_LOCAL_NO_PENDING_CALL,
+            CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE,
+            CODE_LOCAL_POWER_OFF,
+            CODE_LOCAL_LOW_BATTERY,
+            CODE_LOCAL_NETWORK_NO_SERVICE,
+            CODE_LOCAL_NETWORK_NO_LTE_COVERAGE,
+            CODE_LOCAL_NETWORK_ROAMING,
+            CODE_LOCAL_NETWORK_IP_CHANGED,
+            CODE_LOCAL_SERVICE_UNAVAILABLE,
+            CODE_LOCAL_NOT_REGISTERED,
+            CODE_LOCAL_CALL_EXCEEDED,
+            CODE_LOCAL_CALL_BUSY,
+            CODE_LOCAL_CALL_DECLINE,
+            CODE_LOCAL_CALL_VCC_ON_PROGRESSING,
+            CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED,
+            CODE_LOCAL_CALL_CS_RETRY_REQUIRED,
+            CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED,
+            CODE_LOCAL_CALL_TERMINATED,
+            CODE_LOCAL_HO_NOT_FEASIBLE,
+            CODE_TIMEOUT_1XX_WAITING,
+            CODE_TIMEOUT_NO_ANSWER,
+            CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE,
+            CODE_CALL_BARRED,
+            CODE_FDN_BLOCKED,
+            CODE_IMEI_NOT_ACCEPTED,
+            CODE_DIAL_MODIFIED_TO_USSD,
+            CODE_DIAL_MODIFIED_TO_SS,
+            CODE_DIAL_MODIFIED_TO_DIAL,
+            CODE_DIAL_MODIFIED_TO_DIAL_VIDEO,
+            CODE_DIAL_VIDEO_MODIFIED_TO_DIAL,
+            CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO,
+            CODE_DIAL_VIDEO_MODIFIED_TO_SS,
+            CODE_DIAL_VIDEO_MODIFIED_TO_USSD,
+            CODE_SIP_REDIRECTED,
+            CODE_SIP_BAD_REQUEST,
+            CODE_SIP_FORBIDDEN,
+            CODE_SIP_NOT_FOUND,
+            CODE_SIP_NOT_SUPPORTED,
+            CODE_SIP_REQUEST_TIMEOUT,
+            CODE_SIP_TEMPRARILY_UNAVAILABLE,
+            CODE_SIP_BAD_ADDRESS,
+            CODE_SIP_BUSY,
+            CODE_SIP_REQUEST_CANCELLED,
+            CODE_SIP_NOT_ACCEPTABLE,
+            CODE_SIP_NOT_REACHABLE,
+            CODE_SIP_CLIENT_ERROR,
+            CODE_SIP_TRANSACTION_DOES_NOT_EXIST,
+            CODE_SIP_SERVER_INTERNAL_ERROR,
+            CODE_SIP_SERVICE_UNAVAILABLE,
+            CODE_SIP_SERVER_TIMEOUT,
+            CODE_SIP_SERVER_ERROR,
+            CODE_SIP_USER_REJECTED,
+            CODE_SIP_GLOBAL_ERROR,
+            CODE_EMERGENCY_TEMP_FAILURE,
+            CODE_EMERGENCY_PERM_FAILURE,
+            CODE_SIP_USER_MARKED_UNWANTED,
+            CODE_SIP_METHOD_NOT_ALLOWED,
+            CODE_SIP_PROXY_AUTHENTICATION_REQUIRED,
+            CODE_SIP_REQUEST_ENTITY_TOO_LARGE,
+            CODE_SIP_REQUEST_URI_TOO_LARGE,
+            CODE_SIP_EXTENSION_REQUIRED,
+            CODE_SIP_INTERVAL_TOO_BRIEF,
+            CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST,
+            CODE_SIP_LOOP_DETECTED,
+            CODE_SIP_TOO_MANY_HOPS,
+            CODE_SIP_AMBIGUOUS,
+            CODE_SIP_REQUEST_PENDING,
+            CODE_SIP_UNDECIPHERABLE,
+            CODE_MEDIA_INIT_FAILED,
+            CODE_MEDIA_NO_DATA,
+            CODE_MEDIA_NOT_ACCEPTABLE,
+            CODE_MEDIA_UNSPECIFIED,
+            CODE_USER_TERMINATED,
+            CODE_USER_NOANSWER,
+            CODE_USER_IGNORE,
+            CODE_USER_DECLINE,
+            CODE_LOW_BATTERY,
+            CODE_BLACKLISTED_CALL_ID,
+            CODE_USER_TERMINATED_BY_REMOTE,
+            CODE_USER_REJECTED_SESSION_MODIFICATION,
+            CODE_USER_CANCELLED_SESSION_MODIFICATION,
+            CODE_SESSION_MODIFICATION_FAILED,
+            CODE_UT_NOT_SUPPORTED,
+            CODE_UT_SERVICE_UNAVAILABLE,
+            CODE_UT_OPERATION_NOT_ALLOWED,
+            CODE_UT_NETWORK_ERROR,
+            CODE_UT_CB_PASSWORD_MISMATCH,
+            CODE_UT_SS_MODIFIED_TO_DIAL,
+            CODE_UT_SS_MODIFIED_TO_USSD,
+            CODE_UT_SS_MODIFIED_TO_SS,
+            CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO,
+            CODE_ECBM_NOT_SUPPORTED,
+            CODE_MULTIENDPOINT_NOT_SUPPORTED,
+            CODE_REGISTRATION_ERROR,
+            CODE_ANSWERED_ELSEWHERE,
+            CODE_CALL_PULL_OUT_OF_SYNC,
+            CODE_CALL_END_CAUSE_CALL_PULL,
+            CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE,
+            CODE_REJECTED_ELSEWHERE,
+            CODE_SUPP_SVC_FAILED,
+            CODE_SUPP_SVC_CANCELLED,
+            CODE_SUPP_SVC_REINVITE_COLLISION,
+            CODE_IWLAN_DPD_FAILURE,
+            CODE_EPDG_TUNNEL_ESTABLISH_FAILURE,
+            CODE_EPDG_TUNNEL_REKEY_FAILURE,
+            CODE_EPDG_TUNNEL_LOST_CONNECTION,
+            CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED,
+            CODE_REMOTE_CALL_DECLINE,
+            CODE_DATA_LIMIT_REACHED,
+            CODE_DATA_DISABLED,
+            CODE_WIFI_LOST,
+            CODE_IKEV2_AUTH_FAILURE,
+            CODE_RADIO_OFF,
+            CODE_NO_VALID_SIM,
+            CODE_RADIO_INTERNAL_ERROR,
+            CODE_NETWORK_RESP_TIMEOUT,
+            CODE_NETWORK_REJECT,
+            CODE_RADIO_ACCESS_FAILURE,
+            CODE_RADIO_LINK_FAILURE,
+            CODE_RADIO_LINK_LOST,
+            CODE_RADIO_UPLINK_FAILURE,
+            CODE_RADIO_SETUP_FAILURE,
+            CODE_RADIO_RELEASE_NORMAL,
+            CODE_RADIO_RELEASE_ABNORMAL,
+            CODE_ACCESS_CLASS_BLOCKED,
+            CODE_NETWORK_DETACH,
+            CODE_SIP_ALTERNATE_EMERGENCY_CALL,
+            CODE_UNOBTAINABLE_NUMBER,
+            CODE_NO_CSFB_IN_CS_ROAM,
+            CODE_REJECT_UNKNOWN,
+            CODE_REJECT_ONGOING_CALL_WAITING_DISABLED,
+            CODE_REJECT_CALL_ON_OTHER_SUB,
+            CODE_REJECT_1X_COLLISION,
+            CODE_REJECT_SERVICE_NOT_REGISTERED,
+            CODE_REJECT_CALL_TYPE_NOT_ALLOWED,
+            CODE_REJECT_ONGOING_E911_CALL,
+            CODE_REJECT_ONGOING_CALL_SETUP,
+            CODE_REJECT_MAX_CALL_LIMIT_REACHED,
+            CODE_REJECT_UNSUPPORTED_SIP_HEADERS,
+            CODE_REJECT_UNSUPPORTED_SDP_HEADERS,
+            CODE_REJECT_ONGOING_CALL_TRANSFER,
+            CODE_REJECT_INTERNAL_ERROR,
+            CODE_REJECT_QOS_FAILURE,
+            CODE_REJECT_ONGOING_HANDOVER,
+            CODE_REJECT_VT_TTY_NOT_ALLOWED,
+            CODE_REJECT_ONGOING_CALL_UPGRADE,
+            CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED,
+            CODE_REJECT_ONGOING_CONFERENCE_CALL,
+            CODE_REJECT_VT_AVPF_NOT_ALLOWED,
+            CODE_REJECT_ONGOING_ENCRYPTED_CALL,
+            CODE_REJECT_ONGOING_CS_CALL,
+            CODE_OEM_CAUSE_1,
+            CODE_OEM_CAUSE_2,
+            CODE_OEM_CAUSE_3,
+            CODE_OEM_CAUSE_4,
+            CODE_OEM_CAUSE_5,
+            CODE_OEM_CAUSE_6,
+            CODE_OEM_CAUSE_7,
+            CODE_OEM_CAUSE_8,
+            CODE_OEM_CAUSE_9,
+            CODE_OEM_CAUSE_10,
+            CODE_OEM_CAUSE_11,
+            CODE_OEM_CAUSE_12,
+            CODE_OEM_CAUSE_13,
+            CODE_OEM_CAUSE_14,
+            CODE_OEM_CAUSE_15
+    }, prefix = "CODE_")
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsCode {}
+
+    /**
      * Network string error messages.
      * mExtraMessage may have these values.
      */
@@ -964,7 +1143,7 @@
     /**
      * @return an integer representing more information about the completion of an operation.
      */
-    public int getCode() {
+    public @ImsCode int getCode() {
         return mCode;
     }
 
diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java
index 77e36bb..603f4e6 100644
--- a/telephony/java/android/telephony/mbms/GroupCallCallback.java
+++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java
@@ -57,7 +57,7 @@
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    void onError(@GroupCallError int errorCode, @Nullable String message);
+    default void onError(@GroupCallError int errorCode, @Nullable String message) {}
 
     /**
      * Called to indicate this call has changed state.
@@ -65,8 +65,8 @@
      * See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED}
      * and {@link GroupCall#STATE_STALLED}.
      */
-    void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
-            @GroupCall.GroupCallStateChangeReason int reason);
+    default void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
+            @GroupCall.GroupCallStateChangeReason int reason) {}
 
     /**
      * Broadcast Signal Strength updated.
@@ -78,5 +78,6 @@
      * {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
      * for this call due to timing, geography or popularity.
      */
-    void onBroadcastSignalStrengthUpdated(@IntRange(from = -1, to = 4) int signalStrength);
+    default void onBroadcastSignalStrengthUpdated(
+            @IntRange(from = -1, to = 4) int signalStrength) {}
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
index 04e7ba1..ac7e172 100644
--- a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
@@ -57,7 +57,7 @@
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    void onError(@GroupCallError int errorCode, @Nullable String message);
+    default void onError(@GroupCallError int errorCode, @Nullable String message) {}
 
     /**
      * Indicates that the list of currently available SAIs has been updated. The app may use this
@@ -70,8 +70,8 @@
      * @param availableSais A list of lists of available SAIS in neighboring cells, where each list
      *                      contains the available SAIs in an individual cell.
      */
-    void onAvailableSaisUpdated(@NonNull List<Integer> currentSais,
-            @NonNull List<List<Integer>> availableSais);
+    default void onAvailableSaisUpdated(@NonNull List<Integer> currentSais,
+            @NonNull List<List<Integer>> availableSais) {}
 
     /**
      * Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied
@@ -85,7 +85,7 @@
      * @param interfaceName The interface name for the data link.
      * @param index The index for the data link.
      */
-    void onServiceInterfaceAvailable(@NonNull String interfaceName, int index);
+    default void onServiceInterfaceAvailable(@NonNull String interfaceName, int index) {}
 
     /**
      * Called to indicate that the middleware has been initialized and is ready.
@@ -95,5 +95,5 @@
      * delivered via {@link #onError(int, String)} with error code
      * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
      */
-    void onMiddlewareReady();
+    default void onMiddlewareReady() {}
 }
diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk
index 780bb24..40d4eff 100644
--- a/tests/RollbackTest/Android.mk
+++ b/tests/RollbackTest/Android.mk
@@ -76,12 +76,14 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 LOCAL_COMPATIBILITY_SUITE := general-tests
+LOCAL_COMPATIBILITY_SUPPORT_FILES := $(ROLLBACK_TEST_APEX_V1)
 LOCAL_JAVA_RESOURCE_FILES := \
   $(ROLLBACK_TEST_APP_AV1) \
   $(ROLLBACK_TEST_APP_AV2) \
   $(ROLLBACK_TEST_APP_A_CRASHING_V2) \
   $(ROLLBACK_TEST_APP_BV1) \
-  $(ROLLBACK_TEST_APP_BV2)
+  $(ROLLBACK_TEST_APP_BV2) \
+  $(ROLLBACK_TEST_APEX_V2)
 LOCAL_SDK_VERSION := system_current
 LOCAL_TEST_CONFIG := RollbackTest.xml
 include $(BUILD_PACKAGE)
diff --git a/tests/RollbackTest/TestApex/Android.bp b/tests/RollbackTest/TestApex/Android.bp
new file mode 100644
index 0000000..a2a8e17
--- /dev/null
+++ b/tests/RollbackTest/TestApex/Android.bp
@@ -0,0 +1,56 @@
+// Copyright (C) 2019 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.
+
+apex {
+    name: "com.android.tests.rollback.testapex.RollbackTestApexV1",
+    manifest: "RollbackTestApexV1.json",
+    file_contexts: "apex.test",
+    prebuilts: ["RollbackTestApex.prebuilt.txt"],
+    key: "RollbackTestApex.key",
+    installable: false,
+}
+
+apex {
+    name: "com.android.tests.rollback.testapex.RollbackTestApexV2",
+    manifest: "RollbackTestApexV2.json",
+    file_contexts: "apex.test",
+    prebuilts: ["RollbackTestApex.prebuilt.txt"],
+    key: "RollbackTestApex.key",
+    installable: false,
+}
+
+apex_key {
+    name: "RollbackTestApex.key",
+    public_key: "com.android.tests.rollback.testapex.avbpubkey",
+    private_key: "com.android.tests.rollback.testapex.pem",
+    installable: false,
+}
+
+prebuilt_etc {
+    name: "RollbackTestApex.prebuilt.txt",
+    src: "RollbackTestApex.prebuilt.txt",
+}
+
+filegroup {
+    name: "RollbackTestApexV1_filegroup",
+    srcs: [":com.android.tests.rollback.testapex.RollbackTestApexV1"],
+    export_to_make_var: "ROLLBACK_TEST_APEX_V1",
+}
+
+filegroup {
+    name: "RollbackTestApexV2_filegroup",
+    srcs: [":com.android.tests.rollback.testapex.RollbackTestApexV2"],
+    export_to_make_var: "ROLLBACK_TEST_APEX_V2",
+}
+
diff --git a/tests/RollbackTest/TestApex/RollbackTestApex.prebuilt.txt b/tests/RollbackTest/TestApex/RollbackTestApex.prebuilt.txt
new file mode 100644
index 0000000..e871146
--- /dev/null
+++ b/tests/RollbackTest/TestApex/RollbackTestApex.prebuilt.txt
@@ -0,0 +1,3 @@
+
+This file contains dummy content to include in the RollbackTestApex.
+
diff --git a/tests/RollbackTest/TestApex/RollbackTestApexV1.json b/tests/RollbackTest/TestApex/RollbackTestApexV1.json
new file mode 100644
index 0000000..c3239ca
--- /dev/null
+++ b/tests/RollbackTest/TestApex/RollbackTestApexV1.json
@@ -0,0 +1,4 @@
+{
+    "name": "com.android.tests.rollback.testapex",
+    "version": 1
+}
diff --git a/tests/RollbackTest/TestApex/RollbackTestApexV2.json b/tests/RollbackTest/TestApex/RollbackTestApexV2.json
new file mode 100644
index 0000000..9de3f45
--- /dev/null
+++ b/tests/RollbackTest/TestApex/RollbackTestApexV2.json
@@ -0,0 +1,4 @@
+{
+    "name": "com.android.tests.rollback.testapex",
+    "version": 2
+}
diff --git a/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.avbpubkey b/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.avbpubkey
new file mode 100644
index 0000000..b347331
--- /dev/null
+++ b/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.avbpubkey
Binary files differ
diff --git a/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.pem b/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.pem
new file mode 100644
index 0000000..7181ce5
--- /dev/null
+++ b/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEArBLX+v6RMiK6soQFgbc0RZ+wticTD5sCbu9Q5B5WT7UnV1Wt
+cNI/n2bjks3UYNxTneOzMQOVjd4ln0BYZoNvoDtrc1BsYABpt4FywSq1zz/3sp3L
+3Vp8vaUyRsx0Da+PcOdYHPFs1WPX3Shw3MSPhUO/72KTm6GhO/cHEPEzSQLaw/zO
+8FiPpDdRqILqVJlu1DZ+i1DO+To9mKt59uLlxy3F+HAMnQaNW6+2NCV/vdCxQrEL
+m7PpsM6DymsFKcvVra1Il4apKkYNgcgTQ+AlmyPxGo6twfZvRMrU3bcrUmrg3OAx
+tMD7MufXlKkoFMlT5clrQ7P2ErKoFxhTVWhBL5ZJv/lKwaTfxCLyNvYQ7JALKtOL
+9xx4c77NdyYVjxMDAJ7E+Uj3QjdOv3FxM38Fcvt0zN4SSlmDjEDtm2e2w53Ztgz1
+zHAuNllIW1zGdPqa+ROoa9JLJePlyNXG9Rn1sSkjM9WwteaJ3nrdje6uBsvo7I9g
+1MWab2XNXQwJNHACICtyU7QPryGP4U/lyHqWEkdoXrs8O36gmTGU/EsJYSIVILax
+0HZC3zkmxHgiN95ZvD+Y5Y9HDXAN04kT8O1V/QD4QUCP8MGXNrkQP/JZAnu5Y2D1
+Wrn/7Tft2ON3OpH5vUgR99bKR549LcRrYgxeTnci3xkOsnTjZRxEUJFQgVUCAwEA
+AQKCAgEAlBzIMbDLk+cW4rAG+WeTo9ZXygKKQqV/i7OM4j5GtudMTL1fxDwFLZTn
+kCaBhzo+8ynaxPa71ViA87n0HZFHFRnwXFq+XkgctyrCdwjnY9RAxktS/l5z/t1i
+EFTOFDWod1t6mbcpoegGF8RGmZDLpL7zR/+G5LBUU4RHXcrwBQli+s3x5imkwoon
+TLAbOeSz5BBgDlTpQtdhy7bWDa+ybya0QCtagBLyvBfb8rjQYrduzOQOBODw5xJr
+mGFUGWztqUf9swfjNpMD71EjXApk9EwUrXJgmBMiJYmdfpa6wH9kvFpyDo8J6gBr
+rPeJm5LMF3+vR6Bw0Bld3DtBB8PsTrockOdeJNHfnY54480e7AahfM5gkeuIYC4P
+E3CIbyAgpS8+OLpsXP207AOjFz1TLiOqyDUDsBqSMNEQ7QbIPTQw8UZ/o0vEoqEv
+RrJAvIAv0KUcFNUL0t6VX2OXbV+i+T85wPey3XqK9P8Zzc8NUEGJ2paDUkSuemKC
+nF9/siGaoBHXsNUkQuV9xYo4co66MHDGAx/OdUM5lPiUcrF/bR+ZUA3p1BLUQXbQ
+aJD9uKlvJ4ZRSwhR/fQc5UiVWl3l76k+0Ia/Ddd1ArphVhbvGPOsm3W7wJ6KukSk
+rBJh1PPV1HnA+icS2Wvu2kVdz9+39cQUYJIICO1uwWfPIb/R5MUCggEBAOSwkwXs
+jqewNc6X4mc19RGKIMUvOrRrQkOOY33pI+BjoqI8cl+NOHNB7F8/gVnHIfhZpOG6
+WD5St1/qlSPT1Nte+38P+woAp5mdclZyRgYb4MjZybZihUPIPVgUbxiZTDHy1Sw0
+kRgmvaV3ccvG9/9bBUKNr9Z+XySsIhq5DzY+yIZ33vhLB/0g05x2HVIsSlsRWzri
+lw0iX/v07bdIRUIv7QB8xcLY/23pIbHAXS8DaknN8YxpKtjilOnUWVp/dx4SIcJY
+6PtSBfPcwVJ2MA/xXWBvDP82/XxvbxKnjj8lvkbfqMC6jrXJBw3NT/he8b932IBb
+PwLGU4hoKvjXfUcCggEBAMCfa4N1KUXW0Mo3BcDk2Hk8zVYuTPDNTWg1Mxb46sn+
+HNVJKU0OrejM2hNNCwzJG7eVQBorJj5XefhaQFTvWoLKbf1YZuWKaQrRwKkHEqDi
+edplA2RkpUebeS4KYIr22rzu3ZrZqiJmRU5kAS9hwOzwFvnXUgGy7IoZKXhpo0HK
+xvwylb/C5FXh97tecDdH/5evB/DC515TIhPzUQ5tpT0oAl9MU7Pe1E/opzrD/0sR
+dqKVJcl9vBRbtcAIkCOVpLoA9T2VvisafZAJaRXc3ACFMceewgpVAtBCsWUtLfvW
+fHMHWfti5je19SWg7bUK56uHJ0vdAI+irkfpe1t0aoMCggEAR5cPL3eSYOREs9vQ
+QEcf5NG82H2kfv5kzAkzFCN7267VJryNgWQQG+SzPk3/DD/OXpSRjShsn3X9ecVR
+0tlpdRMS3//8snDqBqjHNlCnoxnvEHE9OB83YLS6n2wmKykyNSCzoxcBpPHbxITT
+1tr+n626w87fEOKWnkBUnND59h1JYO79mfTDF3bDR+Oh4iuDS2bvjEuKxc3RBmry
+T8IMDGA8bT6iGhEcRSgKKD7z7NfA2kHiL/ZsN2EXBOw43J+yhnNephx3MtXGj0S4
+MDxXZ2ZDuQCKrQpl6CJqPwi8+v+xxTYW+d5s9nNsBeIT+sieHTZDTEtEOnYjiDwz
+15p92QKCAQEAkMwGNQawpOhLgYcFEzC0LcbwEFWzztx10N0U77LkRD16jTZ3Do73
+WmYLlLC4mr7e0A0o58MB96EodfHaJD7dSi5Dqkt25hw6xEBS1H0Vms1EjlCa0S/7
+Mq4D1QFF+5B/c8EX4ty20S8R8FCqt2SDc1kz3FHpOo+20kUB8Jtwdveox1J7UXB+
+1rSL1lSyhEviLbMMhAbvh+90UYz5pJ/1s9hMmDi3PyJFdWBNvZYyZcrV5He7tRCI
+fsFGCfol6CoIby5jLA1Rq/M46jq4vQ+ObfGyLv3/nWa0O7u2wHjK9WIRoSKomJmK
+t9xXURb9Obfd2Qo7FwMl9dNzsWkpKuGDYwKCAQB+hiWu2C+0foJ4Z8auM1SK8be4
+waplfD7qIvONE/vtl+VAN+eVpz5kkQJfXiafktAHahgEdSx43bofR0Kv0/R7IRs3
+M1WYAr0w+19TXLXkuh2oYIbcoGrIN3HMmlKMv44xh/QUhRe337cCLejP0SESNN+k
+5wT91SbJwuCw/QsG3sD6NIMtCNWdcsAQq/ruhz7pQ/JZUFOueV0tkzbK+oNHWbNU
+lS99qjPaVCXZFlz/t2/89cljh9mtRjcfXIBfuTijN9sXNcLTXsfLsyHJ86eEbI2U
+o2JQ7Sjs10LpiwBbNNHBmulARgRONNMgik6tpNIS0tk9eke0lCX42bDFtAD4
+-----END RSA PRIVATE KEY-----
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index ace0e6d..4b277ae 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -37,7 +37,6 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
 
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -629,12 +628,9 @@
         assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode());
     }
 
-    // TODO: Allow installing test app along atomically with module metadata package so that
-    // a failed test app will be flagged as a failed mainline app
     /**
      * Test bad update automatic rollback.
      */
-    @Ignore
     @Test
     public void testBadUpdateRollback() throws Exception {
         BroadcastReceiver crashCountReceiver = null;