Merge "Add feature flag for Safety Hub"
diff --git a/Android.bp b/Android.bp
index b715b73..e669307 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1567,6 +1567,10 @@
 droidstubs {
     name: "hiddenapi-mappings",
     defaults: ["metalava-api-stubs-default"],
+    srcs: [
+        ":openjdk_java_files",
+        ":non_openjdk_java_files",
+    ],
     arg_files: [
         "core/res/AndroidManifest.xml",
     ],
diff --git a/api/current.txt b/api/current.txt
index 70d7fd2..f387c5d 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -13434,6 +13434,7 @@
     method public boolean clipRect(float, float, float, float);
     method public boolean clipRect(int, int, int, int);
     method public void concat(android.graphics.Matrix);
+    method public void disableZ();
     method public void drawARGB(int, int, int, int);
     method public void drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint);
     method public void drawArc(float, float, float, float, float, float, boolean, android.graphics.Paint);
@@ -13468,6 +13469,7 @@
     method public void drawRect(android.graphics.RectF, android.graphics.Paint);
     method public void drawRect(android.graphics.Rect, android.graphics.Paint);
     method public void drawRect(float, float, float, float, android.graphics.Paint);
+    method public void drawRenderNode(android.graphics.RenderNode);
     method public void drawRoundRect(android.graphics.RectF, float, float, android.graphics.Paint);
     method public void drawRoundRect(float, float, float, float, float, float, android.graphics.Paint);
     method public void drawText(char[], int, int, float, float, android.graphics.Paint);
@@ -13479,6 +13481,7 @@
     method public void drawTextRun(char[], int, int, int, int, float, float, boolean, android.graphics.Paint);
     method public void drawTextRun(java.lang.CharSequence, int, int, int, int, float, float, boolean, android.graphics.Paint);
     method public void drawVertices(android.graphics.Canvas.VertexMode, int, float[], int, float[], int, int[], int, short[], int, int, android.graphics.Paint);
+    method public void enableZ();
     method public boolean getClipBounds(android.graphics.Rect);
     method public final android.graphics.Rect getClipBounds();
     method public int getDensity();
@@ -14468,6 +14471,9 @@
     ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode);
   }
 
+  public final class RecordingCanvas extends android.graphics.Canvas {
+  }
+
   public final class Rect implements android.os.Parcelable {
     ctor public Rect();
     ctor public Rect(int, int, int, int);
@@ -14604,6 +14610,77 @@
     method public final boolean next(android.graphics.Rect);
   }
 
+  public class RenderNode {
+    method public int computeApproximateMemoryUsage();
+    method public static android.graphics.RenderNode create(java.lang.String);
+    method public void discardDisplayList();
+    method public void endRecording();
+    method public float getAlpha();
+    method public int getAmbientShadowColor();
+    method public int getBottom();
+    method public float getCameraDistance();
+    method public boolean getClipToOutline();
+    method public float getElevation();
+    method public int getHeight();
+    method public void getInverseMatrix(android.graphics.Matrix);
+    method public int getLeft();
+    method public void getMatrix(android.graphics.Matrix);
+    method public float getPivotX();
+    method public float getPivotY();
+    method public int getRight();
+    method public float getRotation();
+    method public float getRotationX();
+    method public float getRotationY();
+    method public float getScaleX();
+    method public float getScaleY();
+    method public int getSpotShadowColor();
+    method public int getTop();
+    method public float getTranslationX();
+    method public float getTranslationY();
+    method public float getTranslationZ();
+    method public int getWidth();
+    method public boolean hasDisplayList();
+    method public boolean hasIdentityMatrix();
+    method public boolean hasOverlappingRendering();
+    method public boolean hasShadow();
+    method public boolean isForceDarkAllowed();
+    method public boolean isPivotExplicitlySet();
+    method public boolean offsetLeftAndRight(int);
+    method public boolean offsetTopAndBottom(int);
+    method public boolean resetPivot();
+    method public boolean setAlpha(float);
+    method public boolean setAmbientShadowColor(int);
+    method public boolean setBottom(int);
+    method public boolean setCameraDistance(float);
+    method public boolean setClipBounds(android.graphics.Rect);
+    method public boolean setClipToBounds(boolean);
+    method public boolean setClipToOutline(boolean);
+    method public boolean setElevation(float);
+    method public boolean setForceDarkAllowed(boolean);
+    method public boolean setHasOverlappingRendering(boolean);
+    method public boolean setLeft(int);
+    method public boolean setLeftTopRightBottom(int, int, int, int);
+    method public boolean setOutline(android.graphics.Outline);
+    method public boolean setPivotX(float);
+    method public boolean setPivotY(float);
+    method public boolean setProjectBackwards(boolean);
+    method public boolean setProjectionReceiver(boolean);
+    method public boolean setRight(int);
+    method public boolean setRotation(float);
+    method public boolean setRotationX(float);
+    method public boolean setRotationY(float);
+    method public boolean setScaleX(float);
+    method public boolean setScaleY(float);
+    method public boolean setSpotShadowColor(int);
+    method public boolean setTop(int);
+    method public boolean setTranslationX(float);
+    method public boolean setTranslationY(float);
+    method public boolean setTranslationZ(float);
+    method public boolean setUseCompositingLayer(boolean, android.graphics.Paint);
+    method public android.graphics.RecordingCanvas startRecording(int, int);
+    method public android.graphics.RecordingCanvas startRecording();
+  }
+
   public class Shader {
     ctor public deprecated Shader();
     method public boolean getLocalMatrix(android.graphics.Matrix);
@@ -14694,8 +14771,7 @@
     ctor public Typeface.CustomFallbackBuilder(android.graphics.fonts.FontFamily);
     method public android.graphics.Typeface build();
     method public android.graphics.Typeface.CustomFallbackBuilder setFallback(java.lang.String);
-    method public android.graphics.Typeface.CustomFallbackBuilder setItalic(boolean);
-    method public android.graphics.Typeface.CustomFallbackBuilder setWeight(int);
+    method public android.graphics.Typeface.CustomFallbackBuilder setStyle(android.graphics.fonts.FontStyle);
   }
 
   public class Xfermode {
@@ -15340,9 +15416,8 @@
     method public java.nio.ByteBuffer getBuffer();
     method public java.io.File getFile();
     method public android.os.LocaleList getLocaleList();
-    method public int getSlant();
+    method public android.graphics.fonts.FontStyle getStyle();
     method public int getTtcIndex();
-    method public int getWeight();
   }
 
   public static class Font.Builder {
@@ -22714,6 +22789,7 @@
     field public static final int ENCODING_AC3 = 5; // 0x5
     field public static final int ENCODING_AC4 = 17; // 0x11
     field public static final int ENCODING_DEFAULT = 1; // 0x1
+    field public static final int ENCODING_DOLBY_MAT = 19; // 0x13
     field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe
     field public static final int ENCODING_DTS = 7; // 0x7
     field public static final int ENCODING_DTS_HD = 8; // 0x8
@@ -34041,6 +34117,7 @@
     field public static final java.lang.String DISALLOW_INSTALL_APPS = "no_install_apps";
     field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";
     field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY = "no_install_unknown_sources_globally";
+    field public static final java.lang.String DISALLOW_INTELLIGENCE_CAPTURE = "no_intelligence_capture";
     field public static final java.lang.String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
     field public static final java.lang.String DISALLOW_MOUNT_PHYSICAL_MEDIA = "no_physical_media";
     field public static final java.lang.String DISALLOW_NETWORK_RESET = "no_network_reset";
@@ -37291,6 +37368,7 @@
     field public static final java.lang.String ACTION_APPLICATION_SETTINGS = "android.settings.APPLICATION_SETTINGS";
     field public static final java.lang.String ACTION_APP_NOTIFICATION_SETTINGS = "android.settings.APP_NOTIFICATION_SETTINGS";
     field public static final java.lang.String ACTION_APP_SEARCH_SETTINGS = "android.settings.APP_SEARCH_SETTINGS";
+    field public static final java.lang.String ACTION_APP_USAGE_SETTINGS = "android.settings.action.APP_USAGE_SETTINGS";
     field public static final java.lang.String ACTION_BATTERY_SAVER_SETTINGS = "android.settings.BATTERY_SAVER_SETTINGS";
     field public static final java.lang.String ACTION_BLUETOOTH_SETTINGS = "android.settings.BLUETOOTH_SETTINGS";
     field public static final java.lang.String ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS";
@@ -42847,7 +42925,6 @@
     method public int getLevel();
     method public int getRsrp();
     method public int getRsrq();
-    method public int getRssi();
     method public int getRssnr();
     method public int getTimingAdvance();
     method public void writeToParcel(android.os.Parcel, int);
@@ -51484,6 +51561,17 @@
 
 }
 
+package android.view.intelligence {
+
+  public final class IntelligenceManager {
+    method public void disableContentCapture();
+    method public android.content.ComponentName getIntelligenceServiceComponentName();
+    method public boolean isContentCaptureEnabled();
+    field public static final int FLAG_USER_INPUT = 1; // 0x1
+  }
+
+}
+
 package android.view.textclassifier {
 
   public final class ConversationActions implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index a04a398..3074efd 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -24,6 +24,7 @@
     field public static final java.lang.String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
     field public static final java.lang.String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE";
     field public static final java.lang.String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
+    field public static final java.lang.String BIND_INTELLIGENCE_SERVICE = "android.permission.BIND_INTELLIGENCE_SERVICE";
     field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
     field public static final java.lang.String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
@@ -614,6 +615,14 @@
 
 }
 
+package android.app.assist {
+
+  public static class AssistStructure.ViewNode {
+    ctor public AssistStructure.ViewNode();
+  }
+
+}
+
 package android.app.backup {
 
   public class BackupDataInput {
@@ -4861,6 +4870,36 @@
 
 }
 
+package android.service.intelligence {
+
+  public abstract class IntelligenceService extends android.app.Service {
+    ctor public IntelligenceService();
+    method public abstract void onContentCaptureEvent(android.service.intelligence.InteractionSessionId, java.util.List<android.view.intelligence.ContentCaptureEvent>);
+    method public void onCreateInteractionSession(android.service.intelligence.InteractionContext, android.service.intelligence.InteractionSessionId);
+    method public void onDestroyInteractionSession(android.service.intelligence.InteractionSessionId);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.intelligence.IntelligenceService";
+  }
+
+  public final class InteractionContext implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.content.ComponentName getActivityComponent();
+    method public int getDisplayId();
+    method public int getFlags();
+    method public int getTaskId();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.intelligence.InteractionContext> CREATOR;
+    field public static final int FLAG_DISABLED_BY_APP = 1; // 0x1
+    field public static final int FLAG_DISABLED_BY_FLAG_SECURE = 2; // 0x2
+  }
+
+  public final class InteractionSessionId implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.intelligence.InteractionSessionId> CREATOR;
+  }
+
+}
+
 package android.service.notification {
 
   public final class Adjustment implements android.os.Parcelable {
@@ -6898,6 +6937,40 @@
 
 }
 
+package android.view.intelligence {
+
+  public final class ContentCaptureEvent implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getEventTime();
+    method public int getFlags();
+    method public android.view.autofill.AutofillId getId();
+    method public java.lang.CharSequence getText();
+    method public int getType();
+    method public android.view.intelligence.ViewNode getViewNode();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.intelligence.ContentCaptureEvent> CREATOR;
+    field public static final int TYPE_ACTIVITY_PAUSED = 3; // 0x3
+    field public static final int TYPE_ACTIVITY_RESUMED = 2; // 0x2
+    field public static final int TYPE_ACTIVITY_STARTED = 1; // 0x1
+    field public static final int TYPE_ACTIVITY_STOPPED = 4; // 0x4
+    field public static final int TYPE_VIEW_ADDED = 5; // 0x5
+    field public static final int TYPE_VIEW_REMOVED = 6; // 0x6
+    field public static final int TYPE_VIEW_TEXT_CHANGED = 7; // 0x7
+  }
+
+  public final class IntelligenceManager {
+    method public java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
+    method public java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
+    method public void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
+    method public void setPackageContentCaptureEnabled(java.lang.String, boolean);
+  }
+
+  public final class ViewNode extends android.app.assist.AssistStructure.ViewNode {
+    method public android.view.autofill.AutofillId getParentAutofillId();
+  }
+
+}
+
 package android.webkit {
 
   public abstract class CookieManager {
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 7382cd3..43e926f 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -2299,84 +2299,3 @@
 Lorg/ccil/cowan/tagsoup/Schema;->thePrefix:Ljava/lang/String;
 Lorg/ccil/cowan/tagsoup/Schema;->theRoot:Lorg/ccil/cowan/tagsoup/ElementType;
 Lorg/ccil/cowan/tagsoup/Schema;->theURI:Ljava/lang/String;
-Lsun/misc/Cleaner;->clean()V
-Lsun/misc/Unsafe;->addressSize()I
-Lsun/misc/Unsafe;->allocateInstance(Ljava/lang/Class;)Ljava/lang/Object;
-Lsun/misc/Unsafe;->allocateMemory(J)J
-Lsun/misc/Unsafe;->arrayBaseOffset(Ljava/lang/Class;)I
-Lsun/misc/Unsafe;->arrayIndexScale(Ljava/lang/Class;)I
-Lsun/misc/Unsafe;->compareAndSwapInt(Ljava/lang/Object;JII)Z
-Lsun/misc/Unsafe;->compareAndSwapLong(Ljava/lang/Object;JJJ)Z
-Lsun/misc/Unsafe;->compareAndSwapObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z
-Lsun/misc/Unsafe;->copyMemory(JJJ)V
-Lsun/misc/Unsafe;->copyMemoryFromPrimitiveArray(Ljava/lang/Object;JJJ)V
-Lsun/misc/Unsafe;->copyMemoryToPrimitiveArray(JLjava/lang/Object;JJ)V
-Lsun/misc/Unsafe;->freeMemory(J)V
-Lsun/misc/Unsafe;->fullFence()V
-Lsun/misc/Unsafe;->getAndAddInt(Ljava/lang/Object;JI)I
-Lsun/misc/Unsafe;->getAndAddLong(Ljava/lang/Object;JJ)J
-Lsun/misc/Unsafe;->getAndSetInt(Ljava/lang/Object;JI)I
-Lsun/misc/Unsafe;->getAndSetLong(Ljava/lang/Object;JJ)J
-Lsun/misc/Unsafe;->getAndSetObject(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;
-Lsun/misc/Unsafe;->getArrayBaseOffsetForComponentType(Ljava/lang/Class;)I
-Lsun/misc/Unsafe;->getArrayIndexScaleForComponentType(Ljava/lang/Class;)I
-Lsun/misc/Unsafe;->getBoolean(Ljava/lang/Object;J)Z
-Lsun/misc/Unsafe;->getByte(J)B
-Lsun/misc/Unsafe;->getByte(Ljava/lang/Object;J)B
-Lsun/misc/Unsafe;->getChar(J)C
-Lsun/misc/Unsafe;->getChar(Ljava/lang/Object;J)C
-Lsun/misc/Unsafe;->getDouble(J)D
-Lsun/misc/Unsafe;->getDouble(Ljava/lang/Object;J)D
-Lsun/misc/Unsafe;->getFloat(J)F
-Lsun/misc/Unsafe;->getFloat(Ljava/lang/Object;J)F
-Lsun/misc/Unsafe;->getInt(J)I
-Lsun/misc/Unsafe;->getInt(Ljava/lang/Object;J)I
-Lsun/misc/Unsafe;->getIntVolatile(Ljava/lang/Object;J)I
-Lsun/misc/Unsafe;->getLong(J)J
-Lsun/misc/Unsafe;->getLong(Ljava/lang/Object;J)J
-Lsun/misc/Unsafe;->getLongVolatile(Ljava/lang/Object;J)J
-Lsun/misc/Unsafe;->getObject(Ljava/lang/Object;J)Ljava/lang/Object;
-Lsun/misc/Unsafe;->getObjectVolatile(Ljava/lang/Object;J)Ljava/lang/Object;
-Lsun/misc/Unsafe;->getShort(J)S
-Lsun/misc/Unsafe;->getShort(Ljava/lang/Object;J)S
-Lsun/misc/Unsafe;->getUnsafe()Lsun/misc/Unsafe;
-Lsun/misc/Unsafe;->INVALID_FIELD_OFFSET:I
-Lsun/misc/Unsafe;->loadFence()V
-Lsun/misc/Unsafe;->objectFieldOffset(Ljava/lang/reflect/Field;)J
-Lsun/misc/Unsafe;->pageSize()I
-Lsun/misc/Unsafe;->park(ZJ)V
-Lsun/misc/Unsafe;->putBoolean(Ljava/lang/Object;JZ)V
-Lsun/misc/Unsafe;->putByte(JB)V
-Lsun/misc/Unsafe;->putByte(Ljava/lang/Object;JB)V
-Lsun/misc/Unsafe;->putChar(JC)V
-Lsun/misc/Unsafe;->putChar(Ljava/lang/Object;JC)V
-Lsun/misc/Unsafe;->putDouble(JD)V
-Lsun/misc/Unsafe;->putDouble(Ljava/lang/Object;JD)V
-Lsun/misc/Unsafe;->putFloat(JF)V
-Lsun/misc/Unsafe;->putFloat(Ljava/lang/Object;JF)V
-Lsun/misc/Unsafe;->putInt(JI)V
-Lsun/misc/Unsafe;->putInt(Ljava/lang/Object;JI)V
-Lsun/misc/Unsafe;->putIntVolatile(Ljava/lang/Object;JI)V
-Lsun/misc/Unsafe;->putLong(JJ)V
-Lsun/misc/Unsafe;->putLong(Ljava/lang/Object;JJ)V
-Lsun/misc/Unsafe;->putLongVolatile(Ljava/lang/Object;JJ)V
-Lsun/misc/Unsafe;->putObject(Ljava/lang/Object;JLjava/lang/Object;)V
-Lsun/misc/Unsafe;->putObjectVolatile(Ljava/lang/Object;JLjava/lang/Object;)V
-Lsun/misc/Unsafe;->putOrderedInt(Ljava/lang/Object;JI)V
-Lsun/misc/Unsafe;->putOrderedLong(Ljava/lang/Object;JJ)V
-Lsun/misc/Unsafe;->putOrderedObject(Ljava/lang/Object;JLjava/lang/Object;)V
-Lsun/misc/Unsafe;->putShort(JS)V
-Lsun/misc/Unsafe;->putShort(Ljava/lang/Object;JS)V
-Lsun/misc/Unsafe;->setMemory(JJB)V
-Lsun/misc/Unsafe;->storeFence()V
-Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe;
-Lsun/misc/Unsafe;->THE_ONE:Lsun/misc/Unsafe;
-Lsun/misc/Unsafe;->unpark(Ljava/lang/Object;)V
-Lsun/misc/URLClassPath$JarLoader;->getJarFile()Ljava/util/jar/JarFile;
-Lsun/misc/URLClassPath;->lmap:Ljava/util/HashMap;
-Lsun/misc/URLClassPath;->loaders:Ljava/util/ArrayList;
-Lsun/misc/URLClassPath;->urls:Ljava/util/Stack;
-Lsun/nio/ch/DirectBuffer;->cleaner()Lsun/misc/Cleaner;
-Lsun/security/x509/AlgorithmId;->get(Ljava/lang/String;)Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/AlgorithmId;->getName()Ljava/lang/String;
-Lsun/security/x509/AVA;->hasRFC2253Keyword()Z
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 9d68133..e2f2075 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -31,7 +31,10 @@
  * Rule instance information for zen mode.
  */
 public final class AutomaticZenRule implements Parcelable {
-
+    /* @hide */
+    private static final int ENABLED = 1;
+    /* @hide */
+    private static final int DISABLED = 0;
     private boolean enabled = false;
     private String name;
     private @InterruptionFilter int interruptionFilter;
@@ -39,6 +42,7 @@
     private ComponentName owner;
     private long creationTime;
     private ZenPolicy mZenPolicy;
+    private boolean mModified = false;
 
     /**
      * Creates an automatic zen rule.
@@ -101,8 +105,8 @@
     }
 
     public AutomaticZenRule(Parcel source) {
-        enabled = source.readInt() == 1;
-        if (source.readInt() == 1) {
+        enabled = source.readInt() == ENABLED;
+        if (source.readInt() == ENABLED) {
             name = source.readString();
         }
         interruptionFilter = source.readInt();
@@ -110,6 +114,7 @@
         owner = source.readParcelable(null);
         creationTime = source.readLong();
         mZenPolicy = source.readParcelable(null);
+        mModified = source.readInt() == ENABLED;
     }
 
     /**
@@ -148,6 +153,14 @@
     }
 
     /**
+     * Returns whether this rule's name has been modified by the user.
+     * @hide
+     */
+    public boolean isModified() {
+        return mModified;
+    }
+
+    /**
      * Gets the zen policy.
      */
     public ZenPolicy getZenPolicy() {
@@ -191,6 +204,14 @@
     }
 
     /**
+     * Sets modified state of this rule.
+     * @hide
+     */
+    public void setModified(boolean modified) {
+        this.mModified = modified;
+    }
+
+    /**
      * Sets the zen policy.
      */
     public void setZenPolicy(ZenPolicy zenPolicy) {
@@ -204,7 +225,7 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(enabled ? 1 : 0);
+        dest.writeInt(enabled ? ENABLED : DISABLED);
         if (name != null) {
             dest.writeInt(1);
             dest.writeString(name);
@@ -216,6 +237,7 @@
         dest.writeParcelable(owner, 0);
         dest.writeLong(creationTime);
         dest.writeParcelable(mZenPolicy, 0);
+        dest.writeInt(mModified ? ENABLED : DISABLED);
     }
 
     @Override
@@ -237,6 +259,7 @@
         if (o == this) return true;
         final AutomaticZenRule other = (AutomaticZenRule) o;
         return other.enabled == enabled
+                && other.mModified == mModified
                 && Objects.equals(other.name, name)
                 && other.interruptionFilter == interruptionFilter
                 && Objects.equals(other.conditionId, conditionId)
@@ -248,7 +271,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(enabled, name, interruptionFilter, conditionId, owner, creationTime,
-                mZenPolicy);
+                mZenPolicy, mModified);
     }
 
     public static final Parcelable.Creator<AutomaticZenRule> CREATOR
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index fefb8d3..43f902a 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -2,6 +2,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
@@ -713,7 +714,11 @@
 
         ViewNode[] mChildren;
 
-        ViewNode() {
+        // TODO(b/111276913): temporarily made public / @hide until we decide what will be used by
+        // ScreenObservation.
+        /** @hide */
+        @SystemApi
+        public ViewNode() {
         }
 
         ViewNode(ParcelTransferReader reader, int nestingLevel) {
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 0350eff..5f23749 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1064,6 +1064,17 @@
         }
     }
 
+    @UnsupportedAppUsage
+    void setThemeTo(long dstThemePtr, @NonNull AssetManager srcAssetManager, long srcThemePtr) {
+        synchronized (this) {
+            ensureValidLocked();
+            synchronized (srcAssetManager) {
+                srcAssetManager.ensureValidLocked();
+                nativeThemeCopy(mObject, dstThemePtr, srcAssetManager.mObject, srcThemePtr);
+            }
+        }
+    }
+
     @Override
     protected void finalize() throws Throwable {
         if (DEBUG_REFS && mNumRefs != 0) {
@@ -1375,7 +1386,8 @@
     private static native void nativeThemeDestroy(long themePtr);
     private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId,
             boolean force);
-    static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr);
+    private static native void nativeThemeCopy(long dstAssetManagerPtr, long dstThemePtr,
+            long srcAssetManagerPtr, long srcThemePtr);
     static native void nativeThemeClear(long themePtr);
     private static native int nativeThemeGetAttributeValue(long ptr, long themePtr,
             @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve);
diff --git a/core/java/android/content/res/FontResourcesParser.java b/core/java/android/content/res/FontResourcesParser.java
index 6a4aae6..14eb11a 100644
--- a/core/java/android/content/res/FontResourcesParser.java
+++ b/core/java/android/content/res/FontResourcesParser.java
@@ -76,6 +76,10 @@
 
     // A class represents font element in xml file which points a file in resource.
     public static final class FontFileResourceEntry {
+        public static final int RESOLVE_BY_FONT_TABLE = Typeface.RESOLVE_BY_FONT_TABLE;
+        public static final int UPRIGHT = 0;
+        public static final int ITALIC = 1;
+
         private final @NonNull String mFileName;
         private int mWeight;
         private int mItalic;
@@ -216,7 +220,7 @@
         int weight = array.getInt(R.styleable.FontFamilyFont_fontWeight,
                 Typeface.RESOLVE_BY_FONT_TABLE);
         int italic = array.getInt(R.styleable.FontFamilyFont_fontStyle,
-                Typeface.RESOLVE_BY_FONT_TABLE);
+                FontFileResourceEntry.RESOLVE_BY_FONT_TABLE);
         String variationSettings = array.getString(
                 R.styleable.FontFamilyFont_fontVariationSettings);
         int ttcIndex = array.getInt(R.styleable.FontFamilyFont_ttcIndex, 0);
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index dfa30a2..2ad4f62 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -944,7 +944,8 @@
                 }
                 return Typeface.createFromResources(familyEntry, mAssets, file);
             }
-            return Typeface.createFromResources(mAssets, file, value.assetCookie);
+            return new Typeface.Builder(mAssets, file, false /* isAsset */, value.assetCookie)
+                    .build();
         } catch (XmlPullParserException e) {
             Log.e(TAG, "Failed to parse xml resource " + file, e);
         } catch (IOException e) {
@@ -1335,7 +1336,7 @@
         void setTo(ThemeImpl other) {
             synchronized (mKey) {
                 synchronized (other.mKey) {
-                    AssetManager.nativeThemeCopy(mTheme, other.mTheme);
+                    mAssets.setThemeTo(mTheme, other.mAssets, other.mTheme);
 
                     mThemeResId = other.mThemeResId;
                     mKey.setTo(other.getKey());
diff --git a/core/java/android/hardware/location/ContextHubBroadcastReceiver.java b/core/java/android/hardware/location/ContextHubBroadcastReceiver.java
deleted file mode 100644
index e0cc8b7..0000000
--- a/core/java/android/hardware/location/ContextHubBroadcastReceiver.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.location;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Handler;
-
-/**
- * A BroadcastReceiver that can be used with the Context Hub Service notifications.
- *
- * @hide
- */
-public class ContextHubBroadcastReceiver extends BroadcastReceiver {
-    // The context at which this receiver operates in
-    private Context mContext;
-
-    // The handler to post callbacks to when receiving Context Hub Service intents
-    private Handler mHandler;
-
-    // The callback to be invoked when receiving Context Hub Service intents
-    private ContextHubClientCallback mCallback;
-
-    // The string to use as the broadcast action for this receiver
-    private String mAction;
-
-    // True when this receiver is registered to receive Intents, false otherwise
-    private boolean mRegistered = false;
-
-    public ContextHubBroadcastReceiver(Context context, Handler handler,
-                                       ContextHubClientCallback callback, String tag) {
-        mContext = context;
-        mHandler = handler;
-        mCallback = callback;
-        mAction = tag;
-    }
-
-    /**
-     * Registers this receiver to receive Intents from the Context Hub Service. This method must
-     * only be invoked when the receiver is not registered.
-     *
-     * @throws IllegalStateException if the receiver is already registered
-     */
-    public void register() throws IllegalStateException {
-        if (mRegistered) {
-            throw new IllegalStateException(
-                "Cannot register ContextHubBroadcastReceiver multiple times");
-        }
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(mAction);
-        mContext.registerReceiver(this, intentFilter, null /* broadcastPermission */, mHandler);
-        mRegistered = true;
-    }
-
-    /**
-     * Unregisters this receiver. This method must only be invoked if {@link #register()} is
-     * previously invoked.
-     *
-     * @throws IllegalStateException if the receiver is not yet registered
-     */
-    public void unregister() throws IllegalStateException {
-        if (!mRegistered) {
-            throw new IllegalStateException(
-                "Cannot unregister ContextHubBroadcastReceiver when not registered");
-        }
-        mContext.unregisterReceiver(this);
-        mRegistered = false;
-    }
-
-    /**
-     * Creates a new PendingIntent associated with this receiver.
-     *
-     * @param flags the flags {@link PendingIntent.Flags} to use for the PendingIntent
-     *
-     * @return a PendingIntent to receive notifications for this receiver
-     */
-    public PendingIntent getPendingIntent(@PendingIntent.Flags int flags) {
-        return PendingIntent.getBroadcast(
-            mContext, 0 /* requestCode */, new Intent(mAction), flags);
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        // TODO: Implement this
-    }
-}
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 56da719..9f11246 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -19,6 +19,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.app.PendingIntent;
+import android.content.Intent;
 import android.os.RemoteException;
 
 import com.android.internal.util.Preconditions;
@@ -113,7 +114,8 @@
      * describes the Context Hub the intent event was for. The intent will also have an extra
      * {@link ContextHubManager.EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which
      * will contain the type of the event. See {@link ContextHubManager.Event} for description of
-     * each event type, along with event-specific extra fields.
+     * each event type, along with event-specific extra fields. A client can use
+     * {@link ContextHubIntentEvent.fromIntent(Intent)} to parse the Intent generated by the event.
      *
      * When the intent is received, this client can be recreated through
      * {@link ContextHubManager.createClient(PendingIntent, ContextHubInfo,
@@ -126,10 +128,6 @@
      * continued to be maintained at the Context Hub Service until
      * {@link #unregisterIntent(PendingIntent)} is called for registered intents.
      *
-     * See {@link ContextHubBroadcastReceiver} for a helper class to generate the
-     * {@link PendingIntent} through a {@link BroadcastReceiver}, and maps an {@link Intent} to a
-     * {@link ContextHubClientCallback}.
-     *
      * @param pendingIntent the PendingIntent to register for this client
      * @param nanoAppId     the unique ID of the nanoapp to receive events for
      * @return true on success, false otherwise
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index b0b77f3..9acefa5 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -808,7 +808,7 @@
      *
      * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or pendingIntent
      *                                  was not associated with a client
-     * @throws IllegalStateException    if there were too many registered clients at the service
+     * @throws IllegalStateException    if the client is already registered to a valid callback
      * @throws NullPointerException     if pendingIntent, hubInfo, callback, or executor is null
      *
      * @hide
@@ -818,8 +818,24 @@
             @NonNull PendingIntent pendingIntent, @NonNull ContextHubInfo hubInfo,
             @NonNull ContextHubClientCallback callback,
             @NonNull @CallbackExecutor Executor executor) {
-        // TODO: Implement this
-        throw new UnsupportedOperationException("Not implemented yet");
+        Preconditions.checkNotNull(pendingIntent, "PendingIntent cannot be null");
+        Preconditions.checkNotNull(callback, "Callback cannot be null");
+        Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null");
+        Preconditions.checkNotNull(executor, "Executor cannot be null");
+
+        ContextHubClient client = new ContextHubClient(hubInfo);
+        IContextHubClientCallback clientInterface = createClientCallback(
+                client, callback, executor);
+
+        IContextHubClient clientProxy;
+        try {
+            clientProxy = mService.bindClient(pendingIntent, clientInterface, hubInfo.getId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        client.setClientProxy(clientProxy);
+        return client;
     }
 
     /**
@@ -833,7 +849,7 @@
      *
      * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or pendingIntent
      *                                  was not associated with a client
-     * @throws IllegalStateException    if there were too many registered clients at the service
+     * @throws IllegalStateException    if the client is already registered to a valid callback
      * @throws NullPointerException     if pendingIntent, hubInfo, or callback is null
      *
      * @hide
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index 233e857..9b0acdf 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -17,6 +17,7 @@
 package android.hardware.location;
 
 // Declare any non-default types here with import statements
+import android.app.PendingIntent;
 import android.hardware.location.ContextHubInfo;
 import android.hardware.location.ContextHubMessage;
 import android.hardware.location.NanoApp;
@@ -60,6 +61,11 @@
     // Creates a client to send and receive messages
     IContextHubClient createClient(in IContextHubClientCallback client, int contextHubId);
 
+    // Binds an existing client to a new callback interface, provided a previously registered
+    // PendingIntent
+    IContextHubClient bindClient(
+            in PendingIntent pendingIntent, in IContextHubClientCallback client, int contextHubId);
+
     // Returns a list of ContextHub objects of available hubs
     List<ContextHubInfo> getContextHubs();
 
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 1a84197..92b3169 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -936,6 +936,21 @@
     public static final String DISALLOW_AUTOFILL = "no_autofill";
 
     /**
+     * Specifies if the contents of a user's screen is not allowed to be captured for artificial
+     * intelligence purposes.
+     *
+     * <p>Device owner and profile owner can set this restriction. When it is set by device owner,
+     * only the target user will be affected.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_INTELLIGENCE_CAPTURE = "no_intelligence_capture";
+
+    /**
      * Specifies if user switching is blocked on the current user.
      *
      * <p> This restriction can only be set by the device owner, it will be applied to all users.
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index a1d1c57..76607e9 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -15,8 +15,6 @@
  */
 package android.provider;
 
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -25,22 +23,22 @@
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ProviderInfo;
 import android.content.pm.Signature;
 import android.database.Cursor;
 import android.graphics.Typeface;
+import android.graphics.fonts.Font;
+import android.graphics.fonts.FontFamily;
+import android.graphics.fonts.FontStyle;
 import android.graphics.fonts.FontVariationAxis;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
-import android.os.ResultReceiver;
-import android.util.ArraySet;
 import android.util.Log;
 import android.util.LruCache;
 
@@ -49,7 +47,6 @@
 import com.android.internal.util.Preconditions;
 
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -64,11 +61,11 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Utility class to deal with Font ContentProviders.
@@ -636,7 +633,34 @@
         if (uriBuffer.isEmpty()) {
             return null;
         }
-        return new Typeface.Builder(fonts, uriBuffer).build();
+
+        FontFamily.Builder familyBuilder = null;
+        for (FontInfo fontInfo : fonts) {
+            final ByteBuffer buffer = uriBuffer.get(fontInfo.getUri());
+            if (buffer == null) {
+                continue;
+            }
+            try {
+                final Font font = new Font.Builder(buffer)
+                        .setWeight(fontInfo.getWeight())
+                        .setSlant(fontInfo.isItalic()
+                                ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT)
+                        .setTtcIndex(fontInfo.getTtcIndex())
+                        .setFontVariationSettings(fontInfo.getAxes())
+                        .build();
+                if (familyBuilder == null) {
+                    familyBuilder = new FontFamily.Builder(font);
+                } else {
+                    familyBuilder.addFont(font);
+                }
+            } catch (IOException e) {
+                continue;
+            }
+        }
+        if (familyBuilder == null) {
+            return null;
+        }
+        return new Typeface.CustomFallbackBuilder(familyBuilder.build()).build();
     }
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a945917..689f975 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -819,6 +819,15 @@
             "android.settings.action.MANAGE_WRITE_SETTINGS";
 
     /**
+     * Activity Action: Show screen for controlling app usage properties for an app.
+     * Input: Intent's extra EXTRA_PACKAGE_NAME must specify the application package name.
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_APP_USAGE_SETTINGS =
+            "android.settings.action.APP_USAGE_SETTINGS";
+
+    /**
      * Activity Action: Show screen of details about a particular application.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -10276,6 +10285,18 @@
         public static final String WIFI_LINK_SPEED_METRICS_ENABLED =
                 "wifi_link_speed_metrics_enabled";
 
+        /**
+         * Setting to enable the PNO frequency culling optimization.
+         * Disabled by default, and setting it to 1 will enable it.
+         * The value is boolean (0 or 1).
+         * @hide
+         */
+        public static final String WIFI_PNO_FREQUENCY_CULLING_ENABLED =
+                "wifi_pno_frequency_culling_enabled";
+
+        private static final Validator WIFI_PNO_FREQUENCY_CULLING_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
        /**
         * The maximum number of times we will retry a connection to an access
         * point for which we have failed in acquiring an IP address from DHCP.
@@ -12683,6 +12704,8 @@
             VALIDATORS.put(DEVICE_PROVISIONING_MOBILE_DATA_ENABLED, BOOLEAN_VALIDATOR);
             VALIDATORS.put(REQUIRE_PASSWORD_TO_DECRYPT, BOOLEAN_VALIDATOR);
             VALIDATORS.put(DEVICE_DEMO_MODE, BOOLEAN_VALIDATOR);
+            VALIDATORS.put(WIFI_PNO_FREQUENCY_CULLING_ENABLED,
+                    WIFI_PNO_FREQUENCY_CULLING_ENABLED_VALIDATOR);
         }
 
         /**
diff --git a/core/java/android/service/intelligence/IntelligenceService.java b/core/java/android/service/intelligence/IntelligenceService.java
new file mode 100644
index 0000000..4b8825d
--- /dev/null
+++ b/core/java/android/service/intelligence/IntelligenceService.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.intelligence;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.view.intelligence.ContentCaptureEvent;
+
+import java.util.List;
+
+/**
+ * A service used to captures the content of the screen.
+ *
+ * <p>The data collected by this service can be analyzed and combined with other sources to provide
+ * contextual data in other areas of the system such as Autofill.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class IntelligenceService extends Service {
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     * To be supported, the service must also require the
+     * {@link android.Manifest.permission#BIND_INTELLIGENCE_SERVICE} permission so
+     * that other applications can not abuse it.
+     */
+    public static final String SERVICE_INTERFACE =
+            "android.service.intelligence.IntelligenceService";
+
+    /**
+     * Creates a new interaction session.
+     *
+     * @param context interaction context
+     * @param sessionId the session's Id
+     */
+    public void onCreateInteractionSession(@NonNull InteractionContext context,
+            @NonNull InteractionSessionId sessionId) {}
+
+    /**
+     * Notifies the service of {@link ContentCaptureEvent events} associated with a content capture
+     * session.
+     *
+     * @param sessionId the session's Id
+     * @param events the events
+     */
+    public abstract void onContentCaptureEvent(@NonNull InteractionSessionId sessionId,
+            @NonNull List<ContentCaptureEvent> events);
+
+    /**
+     * Destroys the content capture session identified by the specified {@code sessionId}.
+     *
+     * @param sessionId the id of the session to destroy
+     */
+    public void onDestroyInteractionSession(@NonNull InteractionSessionId sessionId) {}
+}
diff --git a/core/java/android/service/intelligence/InteractionContext.java b/core/java/android/service/intelligence/InteractionContext.java
new file mode 100644
index 0000000..4d83820
--- /dev/null
+++ b/core/java/android/service/intelligence/InteractionContext.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.intelligence;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.TaskInfo;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+// TODO(b/111276913): add javadocs / implement Parcelable / implement equals/hashcode/toString
+/** @hide */
+@SystemApi
+public final class InteractionContext implements Parcelable {
+
+    /**
+     * Flag used to indicate that the app explicitly disabled contents capture for the activity
+     * (using
+     * {@link android.view.intelligence.IntelligenceManager#disableContentCapture()}),
+     * in which case the service will just receive activity-level events.
+     */
+    public static final int FLAG_DISABLED_BY_APP = 0x1;
+
+    /**
+     * Flag used to indicate that the activity's window is tagged with
+     * {@link android.view.Display#FLAG_SECURE}, in which case the service will just receive
+     * activity-level events.
+     */
+    public static final int FLAG_DISABLED_BY_FLAG_SECURE = 0x2;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+            FLAG_DISABLED_BY_APP,
+            FLAG_DISABLED_BY_FLAG_SECURE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ContextCreationFlags{}
+
+    /** @hide */
+    InteractionContext() {
+    }
+
+    /**
+     * Gets the id of the {@link TaskInfo task} associated with this context.
+     */
+    public int getTaskId() {
+        //TODO(b/111276913): implement
+        return 108;
+    }
+
+    /**
+     * Gets the activity associated with this context.
+     */
+    public @NonNull ComponentName getActivityComponent() {
+        //TODO(b/111276913): implement
+        return null;
+    }
+
+    /**
+     * Gets the ID of the display associated with this context, as defined by
+     * {G android.hardware.display.DisplayManager#getDisplay(int) DisplayManager.getDisplay()}.
+     */
+    public int getDisplayId() {
+        //TODO(b/111276913): implement
+        return 42;
+    }
+
+    /**
+     * Gets the flags associated with this context.
+     *
+     * @return any combination of {@link #FLAG_DISABLED_BY_FLAG_SECURE} and
+     * {@link #FLAG_DISABLED_BY_APP}.
+     */
+    public @ContextCreationFlags int getFlags() {
+        //TODO(b/111276913): implement
+        return 42;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+    }
+
+    public static final Parcelable.Creator<InteractionContext> CREATOR =
+            new Parcelable.Creator<InteractionContext>() {
+
+        @Override
+        public InteractionContext createFromParcel(Parcel parcel) {
+            // TODO(b/111276913): implement
+            return null;
+        }
+
+        @Override
+        public InteractionContext[] newArray(int size) {
+            return new InteractionContext[size];
+        }
+    };
+}
diff --git a/core/java/android/service/intelligence/InteractionSessionId.java b/core/java/android/service/intelligence/InteractionSessionId.java
new file mode 100644
index 0000000..4c9d706
--- /dev/null
+++ b/core/java/android/service/intelligence/InteractionSessionId.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.intelligence;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+// TODO(b/111276913): add javadocs / implement equals/hashcode/string
+/** @hide */
+@SystemApi
+public final class InteractionSessionId implements Parcelable {
+
+    /** @hide */
+    public InteractionSessionId() {
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+    }
+
+    public static final Parcelable.Creator<InteractionSessionId> CREATOR =
+            new Parcelable.Creator<InteractionSessionId>() {
+
+        @Override
+        public InteractionSessionId createFromParcel(Parcel parcel) {
+            // TODO(b/111276913): implement
+            return null;
+        }
+
+        @Override
+        public InteractionSessionId[] newArray(int size) {
+            return new InteractionSessionId[size];
+        }
+    };
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index dffbbb7..43fcce3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14730,7 +14730,7 @@
      */
     public float getCameraDistance() {
         final float dpi = mResources.getDisplayMetrics().densityDpi;
-        return -(mRenderNode.getCameraDistance() * dpi);
+        return mRenderNode.getCameraDistance() * dpi;
     }
 
     /**
@@ -14776,7 +14776,7 @@
         final float dpi = mResources.getDisplayMetrics().densityDpi;
 
         invalidateViewProperty(true, false);
-        mRenderNode.setCameraDistance(-Math.abs(distance) / dpi);
+        mRenderNode.setCameraDistance(Math.abs(distance) / dpi);
         invalidateViewProperty(false, false);
 
         invalidateParentIfNeededAndWasQuickRejected();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4876148..dd1f640 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6877,7 +6877,7 @@
         RenderNode renderNode = view.mRenderNode;
         info[0]++;
         if (renderNode != null) {
-            info[1] += renderNode.getDebugSize();
+            info[1] += renderNode.computeApproximateMemoryUsage();
         }
 
         if (view instanceof ViewGroup) {
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.java b/core/java/android/view/intelligence/ContentCaptureEvent.java
new file mode 100644
index 0000000..b8330e5
--- /dev/null
+++ b/core/java/android/view/intelligence/ContentCaptureEvent.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.intelligence;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.autofill.AutofillId;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+// TODO(b/111276913): add javadocs / implement Parcelable / implement
+/** @hide */
+@SystemApi
+public final class ContentCaptureEvent implements Parcelable {
+
+    /**
+     * Called when the activity is started.
+     */
+    public static final int TYPE_ACTIVITY_STARTED  = 1;
+
+    /**
+     * Called when the activity is resumed.
+     */
+    public static final int TYPE_ACTIVITY_RESUMED = 2;
+
+    /**
+     * Called when the activity is paused.
+     */
+    public static final int TYPE_ACTIVITY_PAUSED = 3;
+
+    /**
+     * Called when the activity is stopped.
+     */
+    public static final int TYPE_ACTIVITY_STOPPED  = 4;
+
+    /**
+     * Called when a node has been added to the screen and is visible to the user.
+     *
+     * <p>The metadata of the node is available through {@link #getViewNode()}.
+     */
+    public static final int TYPE_VIEW_ADDED = 5;
+
+    /**
+     * Called when a node has been removed from the screen and is not visible to the user anymore.
+     *
+     * <p>The id of the node is available through {@link #getId()}.
+     */
+    public static final int TYPE_VIEW_REMOVED = 6;
+
+    /**
+     * Called when the text of a node has been changed.
+     *
+     * <p>The id of the node is available through {@link #getId()}, and the new text is
+     * available through {@link #getText()}.
+     */
+    public static final int TYPE_VIEW_TEXT_CHANGED = 7;
+
+    // TODO(b/111276913): add event to indicate when FLAG_SECURE was changed?
+
+    /** @hide */
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_ACTIVITY_STARTED,
+            TYPE_ACTIVITY_PAUSED,
+            TYPE_ACTIVITY_RESUMED,
+            TYPE_ACTIVITY_STOPPED,
+            TYPE_VIEW_ADDED,
+            TYPE_VIEW_REMOVED,
+            TYPE_VIEW_TEXT_CHANGED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface EventType{}
+
+    /** @hide */
+    ContentCaptureEvent() {
+    }
+
+    /**
+     * Gets the type of the event.
+     *
+     * @return one of {@link #TYPE_ACTIVITY_STARTED}, {@link #TYPE_ACTIVITY_RESUMED},
+     * {@link #TYPE_ACTIVITY_PAUSED}, {@link #TYPE_ACTIVITY_STOPPED},
+     * {@link #TYPE_VIEW_ADDED}, {@link #TYPE_VIEW_REMOVED}, or {@link #TYPE_VIEW_TEXT_CHANGED}.
+     */
+    public @EventType int getType() {
+        return 42;
+    }
+
+    /**
+     * Gets when the event was generated, in ms.
+     */
+    public long getEventTime() {
+        return 48151623;
+    }
+
+    /**
+     * Gets optional flags associated with the event.
+     *
+     * @return either {@code 0} or
+     * {@link android.view.intelligence.IntelligenceManager#FLAG_USER_INPUT}.
+     */
+    public int getFlags() {
+        return 0;
+    }
+
+    /**
+     * Gets the whole metadata of the node associated with the event.
+     *
+     * <p>Only set on {@link #TYPE_VIEW_ADDED} events.
+     */
+    @Nullable
+    public ViewNode getViewNode() {
+        return null;
+    }
+
+    /**
+     * Gets the {@link AutofillId} of the node associated with the event.
+     *
+     * <p>Only set on {@link #TYPE_VIEW_REMOVED} and {@link #TYPE_VIEW_TEXT_CHANGED} events.
+     */
+    @Nullable
+    public AutofillId getId() {
+        return null;
+    }
+
+    /**
+     * Gets the current text of the node associated with the event.
+     *
+     * <p>Only set on {@link #TYPE_VIEW_TEXT_CHANGED} events.
+     */
+    @Nullable
+    public CharSequence getText() {
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+    }
+
+    public static final Parcelable.Creator<ContentCaptureEvent> CREATOR =
+            new Parcelable.Creator<ContentCaptureEvent>() {
+
+        @Override
+        public ContentCaptureEvent createFromParcel(Parcel parcel) {
+            // TODO(b/111276913): implement
+            return null;
+        }
+
+        @Override
+        public ContentCaptureEvent[] newArray(int size) {
+            return new ContentCaptureEvent[size];
+        }
+    };
+}
diff --git a/core/java/android/view/intelligence/IntelligenceManager.java b/core/java/android/view/intelligence/IntelligenceManager.java
new file mode 100644
index 0000000..5513ce2f
--- /dev/null
+++ b/core/java/android/view/intelligence/IntelligenceManager.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.intelligence;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.content.Context;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Set;
+
+/**
+ * TODO(b/111276913): add javadocs / implement / add SystemService / PackageFeature
+ */
+public final class IntelligenceManager {
+
+    /**
+     * Used to indicate that a text change was caused by user input (for example, through IME).
+     */
+    //TODO(b/111276913): link to notifyTextChanged() method once available
+    public static final int FLAG_USER_INPUT = 0x1;
+
+    private final Context mContext;
+
+    /** @hide */
+    public IntelligenceManager(@NonNull Context context) {
+        mContext = Preconditions.checkNotNull(context, "context cannot be null");
+    }
+
+    /**
+     * Returns the component name of the {@code android.service.intelligence.IntelligenceService}
+     * that is enabled for the current user.
+     */
+    @Nullable
+    public ComponentName getIntelligenceServiceComponentName() {
+        //TODO(b/111276913): implement
+        return null;
+    }
+
+    /**
+     * Checks whether contents capture is enabled for this activity.
+     */
+    public boolean isContentCaptureEnabled() {
+        //TODO(b/111276913): implement
+        return false;
+    }
+
+    /**
+     * Called by apps to disable content capture.
+     *
+     * <p><b>Note: </b> this call is not persisted accross reboots, so apps should typically call
+     * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
+     */
+    public void disableContentCapture() {
+    }
+
+    /**
+     * Called by the the service {@link android.service.intelligence.IntelligenceService}
+     * to define whether content capture should be enabled for activities with such
+     * {@link android.content.ComponentName}.
+     *
+     * <p>Useful to blacklist a particular activity.
+     *
+     * @throws UnsupportedOperationException if not called by the UID that owns the
+     * {@link android.service.intelligence.IntelligenceService} associated with the
+     * current user.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void setActivityContentCaptureEnabled(@NonNull ComponentName activity,
+            boolean enabled) {
+        //TODO(b/111276913): implement
+    }
+
+    /**
+     * Called by the the service {@link android.service.intelligence.IntelligenceService}
+     * to define whether content capture should be enabled for activities of the app with such
+     * {@code packageName}.
+     *
+     * <p>Useful to blacklist any activity from a particular app.
+     *
+     * @throws UnsupportedOperationException if not called by the UID that owns the
+     * {@link android.service.intelligence.IntelligenceService} associated with the
+     * current user.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void setPackageContentCaptureEnabled(@NonNull String packageName, boolean enabled) {
+        //TODO(b/111276913): implement
+    }
+
+    /**
+     * Gets the activities where content capture was disabled by
+     * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}.
+     *
+     * @throws UnsupportedOperationException if not called by the UID that owns the
+     * {@link android.service.intelligence.IntelligenceService} associated with the
+     * current user.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public Set<ComponentName> getContentCaptureDisabledActivities() {
+        //TODO(b/111276913): implement
+        return null;
+    }
+
+    /**
+     * Gets the apps where content capture was disabled by
+     * {@link #setPackageContentCaptureEnabled(String, boolean)}.
+     *
+     * @throws UnsupportedOperationException if not called by the UID that owns the
+     * {@link android.service.intelligence.IntelligenceService} associated with the
+     * current user.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public Set<String> getContentCaptureDisabledPackages() {
+        //TODO(b/111276913): implement
+        return null;
+    }
+}
diff --git a/core/java/android/view/intelligence/ViewNode.java b/core/java/android/view/intelligence/ViewNode.java
new file mode 100644
index 0000000..357ecf5
--- /dev/null
+++ b/core/java/android/view/intelligence/ViewNode.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.intelligence;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.assist.AssistStructure;
+import android.view.autofill.AutofillId;
+
+//TODO(b/111276913): add javadocs / implement Parcelable / implement
+//TODO(b/111276913): for now it's extending ViewNode directly as it needs most of its properties,
+// but it might be better to create a common, abstract android.view.ViewNode class that both extend
+// instead
+/** @hide */
+@SystemApi
+public final class ViewNode extends AssistStructure.ViewNode {
+
+    /** @hide */
+    public ViewNode() {
+    }
+
+    /**
+     * Returns the {@link AutofillId} of this view's parent, if the parent is also part of the
+     * screen observation tree.
+     */
+    @Nullable
+    public AutofillId getParentAutofillId() {
+        //TODO(b/111276913): implement
+        return null;
+    }
+}
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index e7ac566..19d8a83 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -1396,6 +1396,11 @@
         return as;
     }
 
+    // See b/118826162 -- to avoid logspaming, we rate limit the WTF.
+    private static final long INVERSE_PROC_STATE_WTF_MIN_INTERVAL_MS = 10_000L;
+    private long mNextInverseProcStateWtfUptime;
+    private int mSkippedInverseProcStateWtfCount;
+
     public void updateTrackingAssociationsLocked(int curSeq, long now) {
         final int NUM = mTrackingAssociations.size();
         for (int i = NUM - 1; i >= 0; i--) {
@@ -1417,12 +1422,24 @@
                     } else {
                         act.stopActive(now);
                         if (act.mProcState < procState) {
-                            Slog.w(TAG, "Tracking association " + act + " whose proc state "
-                                    + act.mProcState + " is better than process " + proc
-                                    + " proc state " + procState);
+                            final long nowUptime = SystemClock.uptimeMillis();
+                            if (mNextInverseProcStateWtfUptime > nowUptime) {
+                                mSkippedInverseProcStateWtfCount++;
+                            } else {
+                                // TODO We still see it during boot related to GMS-core.
+                                // b/118826162
+                                Slog.wtf(TAG, "Tracking association " + act + " whose proc state "
+                                        + act.mProcState + " is better than process " + proc
+                                        + " proc state " + procState
+                                        + " (" +  mSkippedInverseProcStateWtfCount + " skipped)");
+                                mSkippedInverseProcStateWtfCount = 0;
+                                mNextInverseProcStateWtfUptime =
+                                        nowUptime + INVERSE_PROC_STATE_WTF_MIN_INTERVAL_MS;
+                            }
                         }
                     }
                 } else {
+                    // Don't need rate limiting on it.
                     Slog.wtf(TAG, "Tracking association without process: " + act
                             + " in " + act.getAssociationState());
                 }
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index f7f13a5..99b5f85 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -37,6 +37,7 @@
 #define ENCODING_AAC_XHE        16
 #define ENCODING_AC4            17
 #define ENCODING_E_AC3_JOC      18
+#define ENCODING_DOLBY_MAT      19
 
 #define ENCODING_INVALID    0
 #define ENCODING_DEFAULT    1
@@ -85,6 +86,8 @@
         return AUDIO_FORMAT_E_AC3_JOC;
     case ENCODING_DEFAULT:
         return AUDIO_FORMAT_DEFAULT;
+    case ENCODING_DOLBY_MAT:
+        return AUDIO_FORMAT_MAT;
     default:
         return AUDIO_FORMAT_INVALID;
     }
@@ -134,6 +137,11 @@
         return ENCODING_AC4;
     case AUDIO_FORMAT_E_AC3_JOC:
         return ENCODING_E_AC3_JOC;
+    case AUDIO_FORMAT_MAT:
+    case AUDIO_FORMAT_MAT_1_0:
+    case AUDIO_FORMAT_MAT_2_0:
+    case AUDIO_FORMAT_MAT_2_1:
+        return ENCODING_DOLBY_MAT;
     case AUDIO_FORMAT_DEFAULT:
         return ENCODING_DEFAULT;
     default:
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 830ca83..b2d44e7 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -1208,13 +1208,23 @@
   // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
 }
 
-static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr,
-                            jlong src_theme_ptr) {
+static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_asset_manager_ptr,
+                            jlong dst_theme_ptr, jlong src_asset_manager_ptr, jlong src_theme_ptr) {
   Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr);
   Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr);
-  if (!dst_theme->SetTo(*src_theme)) {
-    jniThrowException(env, "java/lang/IllegalArgumentException",
-                      "Themes are from different AssetManagers");
+
+  if (dst_asset_manager_ptr != src_asset_manager_ptr) {
+    ScopedLock<AssetManager2> dst_assetmanager(AssetManagerFromLong(dst_asset_manager_ptr));
+    CHECK(dst_theme->GetAssetManager() == &(*dst_assetmanager));
+    (void) dst_assetmanager;
+
+    ScopedLock <AssetManager2> src_assetmanager(AssetManagerFromLong(src_asset_manager_ptr));
+    CHECK(src_theme->GetAssetManager() == &(*src_assetmanager));
+    (void) src_assetmanager;
+
+    dst_theme->SetTo(*src_theme);
+  } else {
+    dst_theme->SetTo(*src_theme);
   }
 }
 
@@ -1255,10 +1265,11 @@
   Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
   CHECK(theme->GetAssetManager() == &(*assetmanager));
   (void) assetmanager;
-  (void) theme;
   (void) priority;
   (void) tag;
   (void) prefix;
+
+  theme->Dump();
 }
 
 static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/,
@@ -1377,7 +1388,7 @@
     {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
     {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy},
     {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
-    {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy},
+    {"nativeThemeCopy", "(JJJJ)V", (void*)NativeThemeCopy},
     {"nativeThemeClear", "(J)V", (void*)NativeThemeClear},
     {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
      (void*)NativeThemeGetAttributeValue},
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index bb71a5d..e89b593 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -295,6 +295,22 @@
     return SET_AND_DIRTY(setBottom, bottom, RenderNode::Y);
 }
 
+static jint android_view_RenderNode_getLeft(jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getLeft();
+}
+
+static jint android_view_RenderNode_getTop(jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getTop();
+}
+
+static jint android_view_RenderNode_getRight(jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getRight();
+}
+
+static jint android_view_RenderNode_getBottom(jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getBottom();
+}
+
 static jboolean android_view_RenderNode_setLeftTopRightBottom(jlong renderNodePtr,
         int left, int top, int right, int bottom) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
@@ -645,6 +661,10 @@
     { "nSetTop",               "(JI)Z",  (void*) android_view_RenderNode_setTop },
     { "nSetRight",             "(JI)Z",  (void*) android_view_RenderNode_setRight },
     { "nSetBottom",            "(JI)Z",  (void*) android_view_RenderNode_setBottom },
+    { "nGetLeft",              "(J)I",  (void*) android_view_RenderNode_getLeft },
+    { "nGetTop",               "(J)I",  (void*) android_view_RenderNode_getTop },
+    { "nGetRight",             "(J)I",  (void*) android_view_RenderNode_getRight },
+    { "nGetBottom",            "(J)I",  (void*) android_view_RenderNode_getBottom },
     { "nSetLeftTopRightBottom","(JIIII)Z", (void*) android_view_RenderNode_setLeftTopRightBottom },
     { "nOffsetLeftAndRight",   "(JI)Z",  (void*) android_view_RenderNode_offsetLeftAndRight },
     { "nOffsetTopAndBottom",   "(JI)Z",  (void*) android_view_RenderNode_offsetTopAndBottom },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 48d1dff..093a860 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -401,6 +401,7 @@
     <protected-broadcast android:name="android.telecom.action.DEFAULT_DIALER_CHANGED" />
     <protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED" />
     <protected-broadcast android:name="android.provider.action.SMS_MMS_DB_CREATED" />
+    <protected-broadcast android:name="android.provider.action.SMS_MMS_DB_LOST" />
     <protected-broadcast android:name="android.intent.action.CONTENT_CHANGED" />
     <protected-broadcast android:name="android.provider.Telephony.MMS_DOWNLOADED" />
 
@@ -3018,6 +3019,14 @@
     <permission android:name="android.permission.BIND_TEXTCLASSIFIER_SERVICE"
                 android:protectionLevel="signature" />
 
+    <!-- Must be required by a android.service.intelligence.IntelligenceService,
+         to ensure that only the system can bind to it.
+         @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_INTELLIGENCE_SERVICE"
+                android:protectionLevel="signature" />
+
     <!-- Must be required by hotword enrollment application,
          to ensure that only the system can interact with it.
          @hide <p>Not for use by third-party applications.</p> -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9ea82a9..8b7cafb 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1130,14 +1130,13 @@
     <string name="permdesc_accessFineLocation">This app can get your exact location only when it is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_accessCoarseLocation">access approximate location
-      (network-based)</string>
+    <string name="permlab_accessCoarseLocation">access approximate location (network-based) only in the foreground</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_accessCoarseLocation" product="tablet">This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them.</string>
+    <string name="permdesc_accessCoarseLocation" product="tablet">This app can get your location based on network sources such as cell towers and Wi-Fi networks, but only when when the app is in the foreground. These location services must be turned on and available on your tablet for the app to be able to use them.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_accessCoarseLocation" product="tv">This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your TV for the app to be able to use them.</string>
+    <string name="permdesc_accessCoarseLocation" product="tv">This app can get your location based on network sources such as cell towers and Wi-Fi networks, but only when when the app is in the foreground. These location services must be turned on and available on your TV for the app to be able to use them.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_accessCoarseLocation" product="default">This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them.</string>
+    <string name="permdesc_accessCoarseLocation" product="default">This app can get your location based on network sources such as cell towers and Wi-Fi networks, but only when the app is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_accessBackgroundLocation">access location in the background</string>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 7b3d940..7163769 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -187,7 +187,7 @@
     <shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288" />
 
     <!-- The Netherlands, 4 digits, known premium codes listed, plus EU -->
-    <shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}|2223|6225|2223" />
+    <shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}|2223|6225|2223|1662" />
 
     <!-- Nigeria -->
     <shortcode country="ng" pattern="\\d{1,5}" free="2441" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index c298770..4980210 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -486,6 +486,7 @@
                     Settings.Global.WIFI_IDLE_MS,
                     Settings.Global.WIFI_IS_UNUSABLE_EVENT_METRICS_ENABLED,
                     Settings.Global.WIFI_LINK_SPEED_METRICS_ENABLED,
+                    Settings.Global.WIFI_PNO_FREQUENCY_CULLING_ENABLED,
                     Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
                     Settings.Global.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
                     Settings.Global.WIFI_NETWORK_SHOW_RSSI,
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index e35a3be..3b0dc9d 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -205,11 +205,70 @@
         mBitmap = bitmap;
     }
 
-    /** @hide */
-    public void insertReorderBarrier() {}
+    /**
+     * @deprecated use {@link #enableZ()} instead
+     * @hide */
+    @Deprecated
+    public void insertReorderBarrier() {
+        enableZ();
+    }
 
-    /** @hide */
-    public void insertInorderBarrier() {}
+    /**
+     * @deprecated use {@link #disableZ()} instead
+     * @hide */
+    @Deprecated
+    public void insertInorderBarrier() {
+        disableZ();
+    }
+
+    /**
+     * <p>Enables Z support which defaults to disabled. This allows for RenderNodes drawn with
+     * {@link #drawRenderNode(RenderNode)} to be re-arranged based off of their
+     * {@link RenderNode#getElevation()} and {@link RenderNode#getTranslationZ()}
+     * values. It also enables rendering of shadows for RenderNodes with an elevation or
+     * translationZ.</p>
+     *
+     * <p>Any draw reordering will not be moved before this call. A typical usage of this might
+     * look something like:
+     *
+     * <pre class="prettyprint">
+     *     void draw(Canvas canvas) {
+     *         // Draw any background content
+     *         canvas.drawColor(backgroundColor);
+     *
+     *         // Begin drawing that may be reordered based off of Z
+     *         canvas.enableZ();
+     *         for (RenderNode child : children) {
+     *             canvas.drawRenderNode(child);
+     *         }
+     *         // End drawing that may be reordered based off of Z
+     *         canvas.disableZ();
+     *
+     *         // Draw any overlays
+     *         canvas.drawText("I'm on top of everything!", 0, 0, paint);
+     *     }
+     * </pre>
+     * </p>
+     *
+     * Note: This is not impacted by any {@link #save()} or {@link #restore()} calls as it is not
+     * considered to be part of the current matrix or clip.
+     *
+     * See {@link #disableZ()}
+     */
+    public void enableZ() {
+    }
+
+    /**
+     * Disables Z support, preventing any RenderNodes drawn after this point from being
+     * visually reordered or having shadows rendered.
+     *
+     * Note: This is not impacted by any {@link #save()} or {@link #restore()} calls as it is not
+     * considered to be part of the current matrix or clip.
+     *
+     * See {@link #enableZ()}
+     */
+    public void disableZ() {
+    }
 
     /**
      * Return true if the device that the current layer draws into is opaque
@@ -2110,4 +2169,17 @@
         super.drawVertices(mode, vertexCount, verts, vertOffset, texs, texOffset,
                 colors, colorOffset, indices, indexOffset, indexCount, paint);
     }
+
+    /**
+     * Draws the given RenderNode. This is only supported in hardware rendering, which can be
+     * verified by asserting that {@link #isHardwareAccelerated()} is true. If
+     * {@link #isHardwareAccelerated()} is false then this throws an exception.
+     *
+     * See {@link RenderNode} for more information on what a RenderNode is and how to use it.
+     *
+     * @param renderNode The RenderNode to draw, must be non-null.
+     */
+    public void drawRenderNode(@NonNull RenderNode renderNode) {
+        throw new IllegalArgumentException("Software rendering doesn't support drawRenderNode");
+    }
 }
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index 7af006b..fd5d624 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.util.Pools.SynchronizedPool;
 import android.view.TextureLayer;
 
@@ -27,17 +26,20 @@
 
 /**
  * A Canvas implementation that records view system drawing operations for deferred rendering.
- * This is intended for use with RenderNode. This class keeps a list of all the Paint and
- * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being freed while
+ * This is used in combination with RenderNode. This class keeps a list of all the Paint and
+ * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being released while
  * the RecordingCanvas is still holding a native reference to the memory.
  *
- * @hide
+ * This is obtained by calling {@link RenderNode#startRecording()} and is valid until the matching
+ * {@link RenderNode#endRecording()} is called. It must not be retained beyond that as it is
+ * internally reused.
  */
 public final class RecordingCanvas extends BaseRecordingCanvas {
     // The recording canvas pool should be large enough to handle a deeply nested
     // view hierarchy because display lists are generated recursively.
     private static final int POOL_LIMIT = 25;
 
+    /** @hide */
     public static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB
 
     private static final SynchronizedPool<RecordingCanvas> sPool =
@@ -50,6 +52,7 @@
     private int mWidth;
     private int mHeight;
 
+    /** @hide */
     static RecordingCanvas obtain(@NonNull RenderNode node, int width, int height) {
         if (node == null) throw new IllegalArgumentException("node cannot be null");
         RecordingCanvas canvas = sPool.acquire();
@@ -65,15 +68,18 @@
         return canvas;
     }
 
+    /** @hide */
     void recycle() {
         mNode = null;
         sPool.release(this);
     }
 
+    /** @hide */
     long finishRecording() {
         return nFinishRecording(mNativeCanvasWrapper);
     }
 
+    /** @hide */
     @Override
     public boolean isRecordingFor(Object o) {
         return o == mNode;
@@ -138,12 +144,12 @@
     ///////////////////////////////////////////////////////////////////////////
 
     @Override
-    public void insertReorderBarrier() {
+    public void enableZ() {
         nInsertReorderBarrier(mNativeCanvasWrapper, true);
     }
 
     @Override
-    public void insertInorderBarrier() {
+    public void disableZ() {
         nInsertReorderBarrier(mNativeCanvasWrapper, false);
     }
 
@@ -159,7 +165,6 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
     public void callDrawGLFunction2(long drawGLFunction) {
         nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunction, null);
     }
@@ -178,7 +183,6 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
     public void drawGLFunctor2(long drawGLFunctor, @Nullable Runnable releasedCallback) {
         nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunctor, releasedCallback);
     }
@@ -192,8 +196,8 @@
      *
      * @param renderNode The RenderNode to draw.
      */
-    @UnsupportedAppUsage
-    public void drawRenderNode(RenderNode renderNode) {
+    @Override
+    public void drawRenderNode(@NonNull RenderNode renderNode) {
         nDrawRenderNode(mNativeCanvasWrapper, renderNode.mNativeRenderNode);
     }
 
@@ -225,7 +229,6 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
     public void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
             CanvasProperty<Float> radius, CanvasProperty<Paint> paint) {
         nDrawCircle(mNativeCanvasWrapper, cx.getNativeContainer(), cy.getNativeContainer(),
@@ -254,6 +257,7 @@
                 paint.getNativeContainer());
     }
 
+    /** @hide */
     @Override
     protected void throwIfCannotDraw(Bitmap bitmap) {
         super.throwIfCannotDraw(bitmap);
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 320fb20..12128b3 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.view.NativeVectorDrawableAnimator;
 import android.view.RenderNodeAnimator;
+import android.view.Surface;
 import android.view.View;
 
 import dalvik.annotation.optimization.CriticalNative;
@@ -137,7 +138,28 @@
  * that the RenderNode is only used on the same thread it is drawn with. For example when using
  * RenderNode with a custom View, then that RenderNode must only be used from the UI thread.</p>
  *
- * @hide
+ * <h3>When to re-render</h3>
+ * <p>Many of the RenderNode mutation methods, such as {@link #setTranslationX(float)}, return
+ * a boolean indicating if the value actually changed or not. This is useful in detecting
+ * if a new frame should be rendered or not. A typical usage would look like:
+ * <pre class="prettyprint">
+ *     public void translateTo(int x, int y) {
+ *         boolean needsUpdate = myRenderNode.setTranslationX(x);
+ *         needsUpdate |= myRenderNode.setTranslationY(y);
+ *         if (needsUpdate) {
+ *             myOwningView.invalidate();
+ *         }
+ *     }
+ * </pre>
+ * This is marginally faster than doing a more explicit up-front check if the value changed by
+ * comparing the desired value against {@link #getTranslationX()} as it minimizes JNI transitions.
+ * The actual mechanism of requesting a new frame to be rendered will depend on how this
+ * RenderNode is being drawn. If it's drawn to a containing View, as in the above snippet,
+ * then simply invalidating that View works. If instead the RenderNode is being drawn to a Canvas
+ * directly such as with {@link Surface#lockHardwareCanvas()} then a new frame needs to be drawn
+ * by calling {@link Surface#lockHardwareCanvas()}, re-drawing the root RenderNode or whatever
+ * top-level content is desired, and finally calling {@link Surface#unlockCanvasAndPost(Canvas)}.
+ * </p>
  */
 public class RenderNode {
 
@@ -147,7 +169,9 @@
                 RenderNode.class.getClassLoader(), nGetNativeFinalizer(), 1024);
     }
 
-    /** Not for general use; use only if you are ThreadedRenderer or RecordingCanvas.
+    /**
+     * Not for general use; use only if you are ThreadedRenderer or RecordingCanvas.
+     *
      * @hide
      */
     public final long mNativeRenderNode;
@@ -174,9 +198,13 @@
      * drawing operations, and store / apply render properties when drawn.
      *
      * @param name The name of the RenderNode, used for debugging purpose. May be null.
-     *
      * @return A new RenderNode.
      */
+    public static @NonNull RenderNode create(@Nullable String name) {
+        return new RenderNode(name, null);
+    }
+
+    /** @hide */
     public static RenderNode create(String name, @Nullable AnimationHost animationHost) {
         return new RenderNode(name, animationHost);
     }
@@ -186,6 +214,8 @@
      *
      * Note: This will *NOT* incRef() on the native object, however it will
      * decRef() when it is destroyed. The caller should have already incRef'd it
+     *
+     * @hide
      */
     public static RenderNode adopt(long nativePtr) {
         return new RenderNode(nativePtr);
@@ -220,6 +250,8 @@
 
     /**
      * Enable callbacks for position changes.
+     *
+     * @hide
      */
     public void requestPositionUpdates(PositionUpdateListener listener) {
         nRequestPositionUpdates(mNativeRenderNode, listener);
@@ -234,15 +266,13 @@
      * {@link #endRecording()} must be called when the recording is finished in order to apply
      * the updated display list.
      *
-     * @param width The width of the recording viewport. This will not alter the width of the
-     *              RenderNode itself, that must be set with {@link #setLeft(int)} and
-     *              {@link #setRight(int)}
+     * @param width  The width of the recording viewport. This will not alter the width of the
+     *               RenderNode itself, that must be set with {@link #setLeft(int)} and
+     *               {@link #setRight(int)}
      * @param height The height of the recording viewport. This will not alter the height of the
      *               RenderNode itself, that must be set with {@link #setTop(int)} and
      *               {@link #setBottom(int)}.
-     *
      * @return A canvas to record drawing operations.
-     *
      * @see #endRecording()
      * @see #hasDisplayList()
      */
@@ -261,20 +291,20 @@
      * with {@link #setLeftTopRightBottom(int, int, int, int)}.
      */
     public RecordingCanvas startRecording() {
-        return RecordingCanvas.obtain(this,
-                nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode));
+        return startRecording(nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode));
     }
 
     /**
-     * @deprecated use {@link #startRecording(int, int)} instead
      * @hide
+     * @deprecated use {@link #startRecording(int, int)} instead
      */
     @Deprecated
     public RecordingCanvas start(int width, int height) {
         return startRecording(width, height);
     }
 
-    /**`
+    /**
+     * `
      * Ends the recording for this display list. Calling this method marks
      * the display list valid and {@link #hasDisplayList()} will return true.
      *
@@ -294,8 +324,8 @@
     }
 
     /**
-     * @deprecated use {@link #endRecording()} instead
      * @hide
+     * @deprecated use {@link #endRecording()} instead
      */
     @Deprecated
     public void end(RecordingCanvas canvas) {
@@ -373,21 +403,52 @@
     ///////////////////////////////////////////////////////////////////////////
 
     /**
-     * TODO
+     * @hide
+     * @deprecated use {@link #setUseCompositingLayer(boolean, Paint)} instead
      */
+    @Deprecated
     public boolean setLayerType(int layerType) {
         return nSetLayerType(mNativeRenderNode, layerType);
     }
 
     /**
-     * TODO
+     * @hide
+     * @deprecated use {@link #setUseCompositingLayer(boolean, Paint)} instead
      */
+    @Deprecated
     public boolean setLayerPaint(@Nullable Paint paint) {
         return nSetLayerPaint(mNativeRenderNode, paint != null ? paint.getNativeInstance() : 0);
     }
 
     /**
+     * Controls whether or not to force this RenderNode to render to an intermediate buffer.
+     * Internally RenderNode will already promote itself to a composition layer if it's useful
+     * for performance or required for the current combination of {@link #setAlpha(float)} and
+     * {@link #setHasOverlappingRendering(boolean)}.
+     *
+     * The usage of this is instead to allow for either overriding of the internal behavior
+     * if it's measured to be necessary for the particular rendering content in question or, more
+     * usefully, to add a composition effect to the RenderNode via the optional paint parameter.
+     *
+     * Note: When a RenderNode is using a compositing layer it will also result in
+     * clipToBounds=true behavior.
+     *
+     * @param forceToLayer if true this forces the RenderNode to use an intermediate buffer.
+     *                     Default & generally recommended value is false.
+     * @param paint        The blend mode, alpha, and ColorFilter to apply to the compositing layer.
+     *                     Only applies if forceToLayer is true.
+     * @return true if anything changed, false otherwise
+     */
+    public boolean setUseCompositingLayer(boolean forceToLayer, @Nullable Paint paint) {
+        boolean didChange = nSetLayerType(mNativeRenderNode, forceToLayer ? 2 : 0);
+        didChange |= nSetLayerPaint(mNativeRenderNode,
+                paint != null ? paint.getNativeInstance() : 0);
+        return didChange;
+    }
+
+    /**
      * Sets the clip bounds of the RenderNode.
+     *
      * @param rect the bounds to clip to. If null, the clip bounds are reset
      * @return True if the clip bounds changed, false otherwise
      */
@@ -410,19 +471,19 @@
     }
 
     /**
-     * Sets whether the display list should be drawn immediately after the
-     * closest ancestor display list containing a projection receiver.
+     * Sets whether the RenderNode should be drawn immediately after the
+     * closest ancestor RenderNode containing a projection receiver.
      *
      * @param shouldProject true if the display list should be projected onto a
-     *            containing volume.
+     *                      containing volume.
      */
     public boolean setProjectBackwards(boolean shouldProject) {
         return nSetProjectBackwards(mNativeRenderNode, shouldProject);
     }
 
     /**
-     * Sets whether the display list is a projection receiver - that its parent
-     * DisplayList should draw any descendent DisplayLists with
+     * Sets whether the RenderNode is a projection receiver - that its parent
+     * RenderNode should draw any descendent RenderNodes with
      * ProjectBackwards=true directly on top of it. Default value is false.
      */
     public boolean setProjectionReceiver(boolean shouldRecieve) {
@@ -433,14 +494,18 @@
      * Sets the outline, defining the shape that casts a shadow, and the path to
      * be clipped if setClipToOutline is set.
      *
-     * Deep copies the data into native to simplify reference ownership.
+     * This will make a copy of the provided {@link Outline}, so any future modifications
+     * to the outline will need to call {@link #setOutline(Outline)} with the modified
+     * outline for those changes to be applied.
+     *
+     * @param outline The outline to use for this RenderNode.
      */
     public boolean setOutline(@Nullable Outline outline) {
         if (outline == null) {
             return nSetOutlineNone(mNativeRenderNode);
         }
 
-        switch(outline.mMode) {
+        switch (outline.mMode) {
             case Outline.MODE_EMPTY:
                 return nSetOutlineEmpty(mNativeRenderNode);
             case Outline.MODE_ROUND_RECT:
@@ -457,28 +522,63 @@
     }
 
     /**
+     * Checks if the RenderNode has a shadow. That is, if the combination of {@link #getElevation()}
+     * and {@link #getTranslationZ()} is greater than zero, there is an {@link Outline} set with
+     * a valid shadow caster path, and the provided outline has a non-zero
+     * {@link Outline#getAlpha()}.
+     *
      * @return True if this RenderNode has a shadow, false otherwise
      */
     public boolean hasShadow() {
         return nHasShadow(mNativeRenderNode);
     }
 
-    /** setSpotShadowColor */
+    /**
+     * Sets the color of the spot shadow that is drawn when the RenderNode has a positive Z or
+     * elevation value and is drawn inside of a {@link Canvas#enableZ()} section.
+     * <p>
+     * By default the shadow color is black. Generally, this color will be opaque so the intensity
+     * of the shadow is consistent between different RenderNodes with different colors.
+     * <p>
+     * The opacity of the final spot shadow is a function of the shadow caster height, the
+     * alpha channel of the outlineSpotShadowColor (typically opaque), and the
+     * {@link android.R.attr#spotShadowAlpha} theme attribute
+     *
+     * @param color The color this RenderNode will cast for its elevation spot shadow.
+     */
     public boolean setSpotShadowColor(int color) {
         return nSetSpotShadowColor(mNativeRenderNode, color);
     }
 
-    /** setAmbientShadowColor */
-    public boolean setAmbientShadowColor(int color) {
-        return nSetAmbientShadowColor(mNativeRenderNode, color);
-    }
-
-    /** getSpotShadowColor */
+    /**
+     * @return The shadow color set by {@link #setSpotShadowColor(int)}, or black if nothing
+     * was set
+     */
     public int getSpotShadowColor() {
         return nGetSpotShadowColor(mNativeRenderNode);
     }
 
-    /** getAmbientShadowColor */
+    /**
+     * Sets the color of the ambient shadow that is drawn when the RenderNode has a positive Z or
+     * elevation value and is drawn inside of a {@link Canvas#enableZ()} section.
+     * <p>
+     * By default the shadow color is black. Generally, this color will be opaque so the intensity
+     * of the shadow is consistent between different RenderNodes with different colors.
+     * <p>
+     * The opacity of the final ambient shadow is a function of the shadow caster height, the
+     * alpha channel of the outlineAmbientShadowColor (typically opaque), and the
+     * {@link android.R.attr#ambientShadowAlpha} theme attribute.
+     *
+     * @param color The color this RenderNode will cast for its elevation shadow.
+     */
+    public boolean setAmbientShadowColor(int color) {
+        return nSetAmbientShadowColor(mNativeRenderNode, color);
+    }
+
+    /**
+     * @return The shadow color set by {@link #setAmbientShadowColor(int)}, or black if
+     * nothing was set
+     */
     public int getAmbientShadowColor() {
         return nGetAmbientShadowColor(mNativeRenderNode);
     }
@@ -503,6 +603,8 @@
 
     /**
      * Controls the RenderNode's circular reveal clip.
+     *
+     * @hide
      */
     public boolean setRevealClip(boolean shouldClip,
             float x, float y, float radius) {
@@ -514,6 +616,7 @@
      * transforms (such as {@link #setScaleX(float)}, {@link #setRotation(float)}, etc.)
      *
      * @param matrix A transform matrix to apply to this display list
+     * @hide TODO Do we want this?
      */
     public boolean setStaticMatrix(Matrix matrix) {
         return nSetStaticMatrix(mNativeRenderNode, matrix.native_instance);
@@ -526,6 +629,7 @@
      * for the matrix parameter.
      *
      * @param matrix The matrix, null indicates that the matrix should be cleared.
+     * @hide TODO Do we want this?
      */
     public boolean setAnimationMatrix(Matrix matrix) {
         return nSetAnimationMatrix(mNativeRenderNode,
@@ -536,7 +640,6 @@
      * Sets the translucency level for the display list.
      *
      * @param alpha The translucency of the display list, must be a value between 0.0f and 1.0f
-     *
      * @see View#setAlpha(float)
      * @see #getAlpha()
      */
@@ -548,7 +651,6 @@
      * Returns the translucency level of this display list.
      *
      * @return A value between 0.0f and 1.0f
-     *
      * @see #setAlpha(float)
      */
     public float getAlpha() {
@@ -562,7 +664,6 @@
      *
      * @param hasOverlappingRendering False if the content is guaranteed to be non-overlapping,
      *                                true otherwise.
-     *
      * @see android.view.View#hasOverlappingRendering()
      * @see #hasOverlappingRendering()
      */
@@ -573,17 +674,28 @@
     /** @hide */
     @IntDef({USAGE_BACKGROUND})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface UsageHint {}
+    public @interface UsageHint {
+    }
 
-    /** The default usage hint */
+    /**
+     * The default usage hint
+     *
+     * @hide
+     */
     public static final int USAGE_UNKNOWN = 0;
 
-    /** Usage is background content */
+    /**
+     * Usage is background content
+     *
+     * @hide
+     */
     public static final int USAGE_BACKGROUND = 1;
 
     /**
      * Provides a hint on what this RenderNode's display list content contains. This hint is used
      * for automatic content transforms to improve accessibility or similar.
+     *
+     * @hide
      */
     public void setUsageHint(@UsageHint int usageHint) {
         nSetUsageHint(mNativeRenderNode, usageHint);
@@ -593,7 +705,6 @@
      * Indicates whether the content of this display list overlaps.
      *
      * @return True if this display list renders content which overlaps, false otherwise.
-     *
      * @see #setHasOverlappingRendering(boolean)
      */
     public boolean hasOverlappingRendering() {
@@ -623,7 +734,6 @@
      * Sets the translation value for the display list on the X axis.
      *
      * @param translationX The X axis translation value of the display list, in pixels
-     *
      * @see View#setTranslationX(float)
      * @see #getTranslationX()
      */
@@ -644,7 +754,6 @@
      * Sets the translation value for the display list on the Y axis.
      *
      * @param translationY The Y axis translation value of the display list, in pixels
-     *
      * @see View#setTranslationY(float)
      * @see #getTranslationY()
      */
@@ -684,7 +793,6 @@
      * Sets the rotation value for the display list around the Z axis.
      *
      * @param rotation The rotation value of the display list, in degrees
-     *
      * @see View#setRotation(float)
      * @see #getRotation()
      */
@@ -705,7 +813,6 @@
      * Sets the rotation value for the display list around the X axis.
      *
      * @param rotationX The rotation value of the display list, in degrees
-     *
      * @see View#setRotationX(float)
      * @see #getRotationX()
      */
@@ -726,7 +833,6 @@
      * Sets the rotation value for the display list around the Y axis.
      *
      * @param rotationY The rotation value of the display list, in degrees
-     *
      * @see View#setRotationY(float)
      * @see #getRotationY()
      */
@@ -747,7 +853,6 @@
      * Sets the scale value for the display list on the X axis.
      *
      * @param scaleX The scale value of the display list
-     *
      * @see View#setScaleX(float)
      * @see #getScaleX()
      */
@@ -768,7 +873,6 @@
      * Sets the scale value for the display list on the Y axis.
      *
      * @param scaleY The scale value of the display list
-     *
      * @see View#setScaleY(float)
      * @see #getScaleY()
      */
@@ -789,7 +893,6 @@
      * Sets the pivot value for the display list on the X axis
      *
      * @param pivotX The pivot value of the display list on the X axis, in pixels
-     *
      * @see View#setPivotX(float)
      * @see #getPivotX()
      */
@@ -810,7 +913,6 @@
      * Sets the pivot value for the display list on the Y axis
      *
      * @param pivotY The pivot value of the display list on the Y axis, in pixels
-     *
      * @see View#setPivotY(float)
      * @see #getPivotY()
      */
@@ -827,135 +929,222 @@
         return nGetPivotY(mNativeRenderNode);
     }
 
+    /**
+     * @return Whether or not a pivot was explicitly set with {@link #setPivotX(float)} or
+     * {@link #setPivotY(float)}. If no pivot has been set then the pivot will be the center
+     * of the RenderNode.
+     */
     public boolean isPivotExplicitlySet() {
         return nIsPivotExplicitlySet(mNativeRenderNode);
     }
 
-    /** lint */
+    /**
+     * Clears any pivot previously set by a call to  {@link #setPivotX(float)} or
+     * {@link #setPivotY(float)}. After calling this {@link #isPivotExplicitlySet()} will be false
+     * and the pivot used for rotation will return to default of being centered on the view.
+     */
     public boolean resetPivot() {
         return nResetPivot(mNativeRenderNode);
     }
 
     /**
-     * Sets the camera distance for the display list. Refer to
-     * {@link View#setCameraDistance(float)} for more information on how to
-     * use this property.
+     * <p>Sets the distance along the Z axis (orthogonal to the X/Y plane on which
+     * RenderNodes are drawn) from the camera to this RenderNode. The camera's distance
+     * affects 3D transformations, for instance rotations around the X and Y
+     * axis. If the rotationX or rotationY properties are changed and this view is
+     * large (more than half the size of the screen), it is recommended to always
+     * use a camera distance that's greater than the height (X axis rotation) or
+     * the width (Y axis rotation) of this view.</p>
      *
-     * @param distance The distance in Z of the camera of the display list
+     * <p>The distance of the camera from the drawing plane can have an affect on the
+     * perspective distortion of the RenderNode when it is rotated around the x or y axis.
+     * For example, a large distance will result in a large viewing angle, and there
+     * will not be much perspective distortion of the view as it rotates. A short
+     * distance may cause much more perspective distortion upon rotation, and can
+     * also result in some drawing artifacts if the rotated view ends up partially
+     * behind the camera (which is why the recommendation is to use a distance at
+     * least as far as the size of the view, if the view is to be rotated.)</p>
      *
-     * @see View#setCameraDistance(float)
-     * @see #getCameraDistance()
+     * <p>The distance is expressed in pixels and must always be positive</p>
+     *
+     * @param distance The distance in pixels, must always be positive
+     * @see #setRotationX(float)
+     * @see #setRotationY(float)
      */
     public boolean setCameraDistance(float distance) {
-        return nSetCameraDistance(mNativeRenderNode, distance);
+        if (!Float.isFinite(distance) || distance < 0.0f) {
+            throw new IllegalArgumentException("distance must be finite & positive, given="
+                    + distance);
+        }
+        // Native actually wants this to be negative not positive, so we flip it.
+        return nSetCameraDistance(mNativeRenderNode, -distance);
     }
 
     /**
-     * Returns the distance in Z of the camera of the display list.
+     * Returns the distance in Z of the camera for this RenderNode
      *
+     * @return the distance along the Z axis in pixels.
      * @see #setCameraDistance(float)
      */
     public float getCameraDistance() {
-        return nGetCameraDistance(mNativeRenderNode);
+        return -nGetCameraDistance(mNativeRenderNode);
     }
 
     /**
-     * Sets the left position for the display list.
+     * Sets the left position for the RenderNode.
      *
-     * @param left The left position, in pixels, of the display list
-     *
-     * @see View#setLeft(int)
+     * @param left The left position, in pixels, of the RenderNode
+     * @return true if the value changed, false otherwise
      */
     public boolean setLeft(int left) {
         return nSetLeft(mNativeRenderNode, left);
     }
 
     /**
-     * Sets the top position for the display list.
+     * Sets the top position for the RenderNode.
      *
-     * @param top The top position, in pixels, of the display list
-     *
-     * @see View#setTop(int)
+     * @param top The top position, in pixels, of the RenderNode
+     * @return true if the value changed, false otherwise.
      */
     public boolean setTop(int top) {
         return nSetTop(mNativeRenderNode, top);
     }
 
     /**
-     * Sets the right position for the display list.
+     * Sets the right position for the RenderNode.
      *
-     * @param right The right position, in pixels, of the display list
-     *
-     * @see View#setRight(int)
+     * @param right The right position, in pixels, of the RenderNode
+     * @return true if the value changed, false otherwise.
      */
     public boolean setRight(int right) {
         return nSetRight(mNativeRenderNode, right);
     }
 
     /**
-     * Sets the bottom position for the display list.
+     * Sets the bottom position for the RenderNode.
      *
-     * @param bottom The bottom position, in pixels, of the display list
-     *
-     * @see View#setBottom(int)
+     * @param bottom The bottom position, in pixels, of the RenderNode
+     * @return true if the value changed, false otherwise.
      */
     public boolean setBottom(int bottom) {
         return nSetBottom(mNativeRenderNode, bottom);
     }
 
     /**
-     * Sets the left and top positions for the display list
+     * Gets the left position for the RenderNode.
      *
-     * @param left The left position of the display list, in pixels
-     * @param top The top position of the display list, in pixels
-     * @param right The right position of the display list, in pixels
-     * @param bottom The bottom position of the display list, in pixels
+     * See {@link #setLeft(int)}
      *
-     * @see View#setLeft(int)
-     * @see View#setTop(int)
-     * @see View#setRight(int)
-     * @see View#setBottom(int)
+     * @return the left position in pixels
+     */
+    public int getLeft() {
+        return nGetLeft(mNativeRenderNode);
+    }
+
+    /**
+     * Gets the top position for the RenderNode.
+     *
+     * See {@link #setTop(int)}
+     *
+     * @return the top position in pixels
+     */
+    public int getTop() {
+        return nGetTop(mNativeRenderNode);
+    }
+
+    /**
+     * Gets the right position for the RenderNode.
+     *
+     * See {@link #setRight(int)}
+     *
+     * @return the right position in pixels
+     */
+    public int getRight() {
+        return nGetRight(mNativeRenderNode);
+    }
+
+    /**
+     * Gets the bottom position for the RenderNode.
+     *
+     * See {@link #setBottom(int)}
+     *
+     * @return the bottom position in pixels
+     */
+    public int getBottom() {
+        return nGetBottom(mNativeRenderNode);
+    }
+
+    /**
+     * Gets the width of the RenderNode, which is the right - left.
+     *
+     * @return the width of the RenderNode
+     */
+    public int getWidth() {
+        return nGetWidth(mNativeRenderNode);
+    }
+
+    /**
+     * Gets the height of the RenderNode, which is the bottom - top.
+     *
+     * @return the height of the RenderNode
+     */
+    public int getHeight() {
+        return nGetHeight(mNativeRenderNode);
+    }
+
+    /**
+     * Sets the left, top, right, and bottom of the RenderNode.
+     *
+     * @param left   The left position of the RenderNode, in pixels
+     * @param top    The top position of the RenderNode, in pixels
+     * @param right  The right position of the RenderNode, in pixels
+     * @param bottom The bottom position of the RenderNode, in pixels
+     * @return true if any values changed, false otherwise.
+     * @see #setLeft(int)
+     * @see #setTop(int)
+     * @see #setRight(int)
+     * @see #setBottom(int)
      */
     public boolean setLeftTopRightBottom(int left, int top, int right, int bottom) {
         return nSetLeftTopRightBottom(mNativeRenderNode, left, top, right, bottom);
     }
 
     /**
-     * Offsets the left and right positions for the display list
+     * Offsets the left and right positions for the RenderNode
      *
-     * @param offset The amount that the left and right positions of the display
-     *               list are offset, in pixels
-     *
-     * @see View#offsetLeftAndRight(int)
+     * @param offset The amount that the left and right positions are offset in pixels
+     * @return true if any values changed, false otherwise.
      */
     public boolean offsetLeftAndRight(int offset) {
         return nOffsetLeftAndRight(mNativeRenderNode, offset);
     }
 
     /**
-     * Offsets the top and bottom values for the display list
+     * Offsets the top and bottom values for the RenderNode
      *
-     * @param offset The amount that the top and bottom positions of the display
-     *               list are offset, in pixels
-     *
-     * @see View#offsetTopAndBottom(int)
+     * @param offset The amount that the left and right positions are offset in pixels
+     * @return true if any values changed, false otherwise.
      */
     public boolean offsetTopAndBottom(int offset) {
         return nOffsetTopAndBottom(mNativeRenderNode, offset);
     }
 
     /**
-     * Outputs the display list to the log. This method exists for use by
+     * Outputs the RenderNode to the log. This method exists for use by
      * tools to output display lists for selected nodes to the log.
+     *
+     * @hide TODO: Expose? Should the shape of this be different than forced dump to logcat?
      */
     public void output() {
         nOutput(mNativeRenderNode);
     }
 
     /**
-     * Gets the size of the DisplayList for debug purposes.
+     * Gets the approximate memory usage of the RenderNode for debug purposes. Does not include
+     * the memory usage of any child RenderNodes nor any bitmaps, only the memory usage of
+     * this RenderNode and any data it owns.
      */
-    public int getDebugSize() {
+    public int computeApproximateMemoryUsage() {
         return nGetDebugSize(mNativeRenderNode);
     }
 
@@ -995,13 +1184,16 @@
      * For now this interface exists to de-couple RenderNode from anything View-specific in a
      * bit of a kludge.
      *
-     * @hide */
+     * @hide
+     */
     public interface AnimationHost {
-        /** checkstyle */
+        /** @hide */
         void registerAnimatingRenderNode(RenderNode animator);
-        /** checkstyle */
+
+        /** @hide */
         void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator);
-        /** checkstyle */
+
+        /** @hide */
         boolean isAttached();
     }
 
@@ -1039,14 +1231,18 @@
     private static native long nCreate(String name);
 
     private static native long nGetNativeFinalizer();
+
     private static native void nOutput(long renderNode);
+
     private static native int nGetDebugSize(long renderNode);
+
     private static native void nRequestPositionUpdates(long renderNode,
             PositionUpdateListener callback);
 
     // Animations
 
     private static native void nAddAnimator(long renderNode, long animatorPtr);
+
     private static native void nEndAllAnimators(long renderNode);
 
 
@@ -1069,8 +1265,10 @@
 
     @CriticalNative
     private static native void nGetTransformMatrix(long renderNode, long nativeMatrix);
+
     @CriticalNative
     private static native void nGetInverseTransformMatrix(long renderNode, long nativeMatrix);
+
     @CriticalNative
     private static native boolean nHasIdentityMatrix(long renderNode);
 
@@ -1078,135 +1276,208 @@
 
     @CriticalNative
     private static native boolean nOffsetTopAndBottom(long renderNode, int offset);
+
     @CriticalNative
     private static native boolean nOffsetLeftAndRight(long renderNode, int offset);
+
     @CriticalNative
     private static native boolean nSetLeftTopRightBottom(long renderNode, int left, int top,
             int right, int bottom);
-    @CriticalNative
-    private static native boolean nSetBottom(long renderNode, int bottom);
-    @CriticalNative
-    private static native boolean nSetRight(long renderNode, int right);
-    @CriticalNative
-    private static native boolean nSetTop(long renderNode, int top);
+
     @CriticalNative
     private static native boolean nSetLeft(long renderNode, int left);
+
+    @CriticalNative
+    private static native boolean nSetTop(long renderNode, int top);
+
+    @CriticalNative
+    private static native boolean nSetRight(long renderNode, int right);
+
+    @CriticalNative
+    private static native boolean nSetBottom(long renderNode, int bottom);
+
+    @CriticalNative
+    private static native int nGetLeft(long renderNode);
+
+    @CriticalNative
+    private static native int nGetTop(long renderNode);
+
+    @CriticalNative
+    private static native int nGetRight(long renderNode);
+
+    @CriticalNative
+    private static native int nGetBottom(long renderNode);
+
     @CriticalNative
     private static native boolean nSetCameraDistance(long renderNode, float distance);
+
     @CriticalNative
     private static native boolean nSetPivotY(long renderNode, float pivotY);
+
     @CriticalNative
     private static native boolean nSetPivotX(long renderNode, float pivotX);
+
     @CriticalNative
     private static native boolean nResetPivot(long renderNode);
+
     @CriticalNative
     private static native boolean nSetLayerType(long renderNode, int layerType);
+
     @CriticalNative
     private static native boolean nSetLayerPaint(long renderNode, long paint);
+
     @CriticalNative
     private static native boolean nSetClipToBounds(long renderNode, boolean clipToBounds);
+
     @CriticalNative
     private static native boolean nSetClipBounds(long renderNode, int left, int top,
             int right, int bottom);
+
     @CriticalNative
     private static native boolean nSetClipBoundsEmpty(long renderNode);
+
     @CriticalNative
     private static native boolean nSetProjectBackwards(long renderNode, boolean shouldProject);
+
     @CriticalNative
     private static native boolean nSetProjectionReceiver(long renderNode, boolean shouldRecieve);
+
     @CriticalNative
     private static native boolean nSetOutlineRoundRect(long renderNode, int left, int top,
             int right, int bottom, float radius, float alpha);
+
     @CriticalNative
     private static native boolean nSetOutlineConvexPath(long renderNode, long nativePath,
             float alpha);
+
     @CriticalNative
     private static native boolean nSetOutlineEmpty(long renderNode);
+
     @CriticalNative
     private static native boolean nSetOutlineNone(long renderNode);
+
     @CriticalNative
     private static native boolean nHasShadow(long renderNode);
+
     @CriticalNative
     private static native boolean nSetSpotShadowColor(long renderNode, int color);
+
     @CriticalNative
     private static native boolean nSetAmbientShadowColor(long renderNode, int color);
+
     @CriticalNative
     private static native int nGetSpotShadowColor(long renderNode);
+
     @CriticalNative
     private static native int nGetAmbientShadowColor(long renderNode);
+
     @CriticalNative
     private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline);
+
     @CriticalNative
     private static native boolean nSetRevealClip(long renderNode,
             boolean shouldClip, float x, float y, float radius);
+
     @CriticalNative
     private static native boolean nSetAlpha(long renderNode, float alpha);
+
     @CriticalNative
     private static native boolean nSetHasOverlappingRendering(long renderNode,
             boolean hasOverlappingRendering);
+
     @CriticalNative
     private static native void nSetUsageHint(long renderNode, int usageHint);
+
     @CriticalNative
     private static native boolean nSetElevation(long renderNode, float lift);
+
     @CriticalNative
     private static native boolean nSetTranslationX(long renderNode, float translationX);
+
     @CriticalNative
     private static native boolean nSetTranslationY(long renderNode, float translationY);
+
     @CriticalNative
     private static native boolean nSetTranslationZ(long renderNode, float translationZ);
+
     @CriticalNative
     private static native boolean nSetRotation(long renderNode, float rotation);
+
     @CriticalNative
     private static native boolean nSetRotationX(long renderNode, float rotationX);
+
     @CriticalNative
     private static native boolean nSetRotationY(long renderNode, float rotationY);
+
     @CriticalNative
     private static native boolean nSetScaleX(long renderNode, float scaleX);
+
     @CriticalNative
     private static native boolean nSetScaleY(long renderNode, float scaleY);
+
     @CriticalNative
     private static native boolean nSetStaticMatrix(long renderNode, long nativeMatrix);
+
     @CriticalNative
     private static native boolean nSetAnimationMatrix(long renderNode, long animationMatrix);
 
     @CriticalNative
     private static native boolean nHasOverlappingRendering(long renderNode);
+
     @CriticalNative
     private static native boolean nGetClipToOutline(long renderNode);
+
     @CriticalNative
     private static native float nGetAlpha(long renderNode);
+
     @CriticalNative
     private static native float nGetCameraDistance(long renderNode);
+
     @CriticalNative
     private static native float nGetScaleX(long renderNode);
+
     @CriticalNative
     private static native float nGetScaleY(long renderNode);
+
     @CriticalNative
     private static native float nGetElevation(long renderNode);
+
     @CriticalNative
     private static native float nGetTranslationX(long renderNode);
+
     @CriticalNative
     private static native float nGetTranslationY(long renderNode);
+
     @CriticalNative
     private static native float nGetTranslationZ(long renderNode);
+
     @CriticalNative
     private static native float nGetRotation(long renderNode);
+
     @CriticalNative
     private static native float nGetRotationX(long renderNode);
+
     @CriticalNative
     private static native float nGetRotationY(long renderNode);
+
     @CriticalNative
     private static native boolean nIsPivotExplicitlySet(long renderNode);
+
     @CriticalNative
     private static native float nGetPivotX(long renderNode);
+
     @CriticalNative
     private static native float nGetPivotY(long renderNode);
+
     @CriticalNative
     private static native int nGetWidth(long renderNode);
+
     @CriticalNative
     private static native int nGetHeight(long renderNode);
+
     @CriticalNative
     private static native boolean nSetAllowForceDark(long renderNode, boolean allowForceDark);
+
     @CriticalNative
     private static native boolean nGetAllowForceDark(long renderNode);
 }
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 5c0c38e..a09b063 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -27,10 +27,11 @@
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
+import android.graphics.fonts.Font;
+import android.graphics.fonts.FontFamily;
 import android.graphics.fonts.FontStyle;
 import android.graphics.fonts.FontVariationAxis;
 import android.graphics.fonts.SystemFonts;
-import android.net.Uri;
 import android.os.Build;
 import android.provider.FontRequest;
 import android.provider.FontsContract;
@@ -50,13 +51,10 @@
 
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -126,7 +124,8 @@
 
     // We cannot support sSystemFallbackMap since we will migrate to public FontFamily API.
     @UnsupportedAppUsage
-    static final Map<String, FontFamily[]> sSystemFallbackMap = Collections.emptyMap();
+    static final Map<String, android.graphics.FontFamily[]> sSystemFallbackMap =
+            Collections.emptyMap();
 
     /**
      * @hide
@@ -164,6 +163,9 @@
     private int[] mSupportedAxes;
     private static final int[] EMPTY_AXES = {};
 
+    // The underlying font families.
+    private final FontFamily[] mFamilies;
+
     @UnsupportedAppUsage
     private static void setDefault(Typeface t) {
         sDefaultTypeface = t;
@@ -192,38 +194,6 @@
 
     /**
      * @hide
-     * Used by Resources to load a font resource of type font file.
-     */
-    @Nullable
-    public static Typeface createFromResources(AssetManager mgr, String path, int cookie) {
-        synchronized (sDynamicCacheLock) {
-            final String key = Builder.createAssetUid(
-                    mgr, path, 0 /* ttcIndex */, null /* axes */,
-                    RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */,
-                    DEFAULT_FAMILY);
-            Typeface typeface = sDynamicTypefaceCache.get(key);
-            if (typeface != null) return typeface;
-
-            FontFamily fontFamily = new FontFamily();
-            // TODO: introduce ttc index and variation settings to resource type font.
-            if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */,
-                    0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */,
-                    RESOLVE_BY_FONT_TABLE /* italic */, null /* axes */)) {
-                if (!fontFamily.freeze()) {
-                    return null;
-                }
-                FontFamily[] families = {fontFamily};
-                typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
-                        RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
-                sDynamicTypefaceCache.put(key, typeface);
-                return typeface;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * @hide
      * Used by Resources to load a font resource of type xml.
      */
     @Nullable
@@ -258,21 +228,34 @@
         // family is FontFamilyFilesResourceEntry
         final FontFamilyFilesResourceEntry filesEntry = (FontFamilyFilesResourceEntry) entry;
 
-        FontFamily fontFamily = new FontFamily();
-        for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) {
-            if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(),
-                    0 /* resourceCookie */, false /* isAsset */, fontFile.getTtcIndex(),
-                    fontFile.getWeight(), fontFile.getItalic(),
-                    FontVariationAxis.fromFontVariationSettings(fontFile.getVariationSettings()))) {
-                return null;
+        try {
+            FontFamily.Builder familyBuilder = null;
+            for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) {
+                final Font.Builder fontBuilder = new Font.Builder(mgr, fontFile.getFileName(),
+                        false /* isAsset */, 0 /* cookie */)
+                        .setTtcIndex(fontFile.getTtcIndex())
+                        .setFontVariationSettings(fontFile.getVariationSettings());
+                if (fontFile.getWeight() != Typeface.RESOLVE_BY_FONT_TABLE) {
+                    fontBuilder.setWeight(fontFile.getWeight());
+                }
+                if (fontFile.getItalic() != Typeface.RESOLVE_BY_FONT_TABLE) {
+                    fontBuilder.setSlant(fontFile.getItalic() == FontFileResourceEntry.ITALIC
+                            ?  FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT);
+                }
+
+                if (familyBuilder == null) {
+                    familyBuilder = new FontFamily.Builder(fontBuilder.build());
+                } else {
+                    familyBuilder.addFont(fontBuilder.build());
+                }
             }
+            if (familyBuilder == null) {
+                return Typeface.DEFAULT;
+            }
+            typeface = new Typeface.CustomFallbackBuilder(familyBuilder.build()).build();
+        } catch (IOException e) {
+            typeface = Typeface.DEFAULT;
         }
-        if (!fontFamily.freeze()) {
-            return null;
-        }
-        FontFamily[] familyChain = { fontFamily };
-        typeface = createFromFamiliesWithDefault(familyChain, DEFAULT_FAMILY,
-                RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
         synchronized (sDynamicCacheLock) {
             final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
                     null /* axes */, RESOLVE_BY_FONT_TABLE /* weight */,
@@ -339,15 +322,11 @@
         /** @hide */
         public static final int BOLD_WEIGHT = 700;
 
-        private int mTtcIndex;
-        private FontVariationAxis[] mAxes;
+        // Kept for generating asset cache key.
+        private final AssetManager mAssetManager;
+        private final String mPath;
 
-        private AssetManager mAssetManager;
-        private String mPath;
-        private FileDescriptor mFd;
-
-        private FontsContract.FontInfo[] mFonts;
-        private Map<Uri, ByteBuffer> mFontBuffers;
+        private final Font.Builder mFontBuilder;
 
         private String mFallbackFamilyName;
 
@@ -360,7 +339,9 @@
          * @param path The file object refers to the font file.
          */
         public Builder(@NonNull File path) {
-            mPath = path.getAbsolutePath();
+            mFontBuilder = new Font.Builder(path);
+            mAssetManager = null;
+            mPath = null;
         }
 
         /**
@@ -372,7 +353,9 @@
          * @param fd The file descriptor. The passed fd must be mmap-able.
          */
         public Builder(@NonNull FileDescriptor fd) {
-            mFd = fd;
+            mFontBuilder = new Font.Builder(fd);
+            mAssetManager = null;
+            mPath = null;
         }
 
         /**
@@ -381,7 +364,9 @@
          * @param path The full path to the font file.
          */
         public Builder(@NonNull String path) {
-            mPath = path;
+            mFontBuilder = new Font.Builder(new File(path));
+            mAssetManager = null;
+            mPath = null;
         }
 
         /**
@@ -391,27 +376,22 @@
          * @param path The file name of the font data in the asset directory
          */
         public Builder(@NonNull AssetManager assetManager, @NonNull String path) {
-            mAssetManager = Preconditions.checkNotNull(assetManager);
-            mPath = Preconditions.checkStringNotEmpty(path);
+            this(assetManager, path, true /* is asset */, 0 /* cookie */);
         }
 
         /**
-         * Constracts a builder from an array of FontsContract.FontInfo.
+         * Constructs a builder from an asset manager and a file path in an asset directory.
          *
-         * Since {@link FontsContract.FontInfo} holds information about TTC indices and
-         * variation settings, there is no need to call {@link #setTtcIndex} or
-         * {@link #setFontVariationSettings}. Similary, {@link FontsContract.FontInfo} holds
-         * weight and italic information, so {@link #setWeight} and {@link #setItalic} are used
-         * for style matching during font selection.
-         *
-         * @param fonts The array of {@link FontsContract.FontInfo}
-         * @param buffers The mapping from URI to buffers to be used during building.
+         * @param assetManager The application's asset manager
+         * @param path The file name of the font data in the asset directory
+         * @param cookie a cookie for the asset
          * @hide
          */
-        public Builder(@NonNull FontsContract.FontInfo[] fonts,
-                @NonNull Map<Uri, ByteBuffer> buffers) {
-            mFonts = fonts;
-            mFontBuffers = buffers;
+        public Builder(@NonNull AssetManager assetManager, @NonNull String path, boolean isAsset,
+                int cookie) {
+            mFontBuilder = new Font.Builder(assetManager, path, isAsset, cookie);
+            mAssetManager = assetManager;
+            mPath = path;
         }
 
         /**
@@ -423,6 +403,7 @@
          */
         public Builder setWeight(@IntRange(from = 1, to = 1000) int weight) {
             mWeight = weight;
+            mFontBuilder.setWeight(weight);
             return this;
         }
 
@@ -434,7 +415,8 @@
          * @param italic {@code true} if the font is italic. Otherwise {@code false}.
          */
         public Builder setItalic(boolean italic) {
-            mItalic = italic ? STYLE_ITALIC : STYLE_NORMAL;
+            mItalic = italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
+            mFontBuilder.setSlant(mItalic);
             return this;
         }
 
@@ -446,11 +428,7 @@
          *                 collection, do not call this method or specify 0.
          */
         public Builder setTtcIndex(@IntRange(from = 0) int ttcIndex) {
-            if (mFonts != null) {
-                throw new IllegalArgumentException(
-                        "TTC index can not be specified for FontResult source.");
-            }
-            mTtcIndex = ttcIndex;
+            mFontBuilder.setTtcIndex(ttcIndex);
             return this;
         }
 
@@ -462,14 +440,7 @@
          *                                  format.
          */
         public Builder setFontVariationSettings(@Nullable String variationSettings) {
-            if (mFonts != null) {
-                throw new IllegalArgumentException(
-                        "Font variation settings can not be specified for FontResult source.");
-            }
-            if (mAxes != null) {
-                throw new IllegalStateException("Font variation settings are already set.");
-            }
-            mAxes = FontVariationAxis.fromFontVariationSettings(variationSettings);
+            mFontBuilder.setFontVariationSettings(variationSettings);
             return this;
         }
 
@@ -479,14 +450,7 @@
          * @param axes An array of font variation axis tag-value pairs.
          */
         public Builder setFontVariationSettings(@Nullable FontVariationAxis[] axes) {
-            if (mFonts != null) {
-                throw new IllegalArgumentException(
-                        "Font variation settings can not be specified for FontResult source.");
-            }
-            if (mAxes != null) {
-                throw new IllegalStateException("Font variation settings are already set.");
-            }
-            mAxes = axes;
+            mFontBuilder.setFontVariationSettings(axes);
             return this;
         }
 
@@ -579,91 +543,41 @@
          * @return Newly created Typeface. May return null if some parameters are invalid.
          */
         public Typeface build() {
-            if (mFd != null) {  // Builder is created with file descriptor.
-                try (FileInputStream fis = new FileInputStream(mFd)) {
-                    FileChannel channel = fis.getChannel();
-                    long size = channel.size();
-                    ByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, size);
-
-                    final FontFamily fontFamily = new FontFamily();
-                    if (!fontFamily.addFontFromBuffer(buffer, mTtcIndex, mAxes, mWeight, mItalic)) {
-                        fontFamily.abortCreation();
-                        return resolveFallbackTypeface();
-                    }
-                    if (!fontFamily.freeze()) {
-                        return resolveFallbackTypeface();
-                    }
-                    FontFamily[] families = { fontFamily };
-                    return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight,
-                            mItalic);
-                } catch (IOException e) {
-                    return resolveFallbackTypeface();
-                }
-            } else if (mAssetManager != null) {  // Builder is created with asset manager.
-                final String key = createAssetUid(
-                        mAssetManager, mPath, mTtcIndex, mAxes, mWeight, mItalic,
+            try {
+                final Font font = mFontBuilder.build();
+                final String key = mAssetManager == null ? null : createAssetUid(
+                        mAssetManager, mPath, font.getTtcIndex(), font.getAxes(),
+                        font.getStyle().getWeight(), font.getStyle().getSlant(),
                         mFallbackFamilyName);
-                synchronized (sDynamicCacheLock) {
-                    Typeface typeface = sDynamicTypefaceCache.get(key);
-                    if (typeface != null) return typeface;
-                    final FontFamily fontFamily = new FontFamily();
-                    if (!fontFamily.addFontFromAssetManager(mAssetManager, mPath, mTtcIndex,
-                            true /* isAsset */, mTtcIndex, mWeight, mItalic, mAxes)) {
-                        fontFamily.abortCreation();
-                        return resolveFallbackTypeface();
+                if (key != null) {
+                    // Dynamic cache lookup is only for assets.
+                    synchronized (sDynamicCacheLock) {
+                        final Typeface typeface = sDynamicTypefaceCache.get(key);
+                        if (typeface != null) {
+                            return typeface;
+                        }
                     }
-                    if (!fontFamily.freeze()) {
-                        return resolveFallbackTypeface();
+                }
+                final FontFamily family = new FontFamily.Builder(font).build();
+                final int weight = mWeight == RESOLVE_BY_FONT_TABLE
+                        ? font.getStyle().getWeight() : mWeight;
+                final int slant = mItalic == RESOLVE_BY_FONT_TABLE
+                        ? font.getStyle().getSlant() : mItalic;
+                final CustomFallbackBuilder builder = new CustomFallbackBuilder(family)
+                        .setStyle(new FontStyle(weight, slant));
+                if (mFallbackFamilyName != null) {
+                    builder.setFallback(mFallbackFamilyName);
+                }
+                final Typeface typeface = builder.build();
+                if (key != null) {
+                    synchronized (sDynamicCacheLock) {
+                        sDynamicTypefaceCache.put(key, typeface);
                     }
-                    FontFamily[] families = { fontFamily };
-                    typeface = createFromFamiliesWithDefault(families, mFallbackFamilyName,
-                            mWeight, mItalic);
-                    sDynamicTypefaceCache.put(key, typeface);
-                    return typeface;
                 }
-            } else if (mPath != null) {  // Builder is created with file path.
-                final FontFamily fontFamily = new FontFamily();
-                if (!fontFamily.addFont(mPath, mTtcIndex, mAxes, mWeight, mItalic)) {
-                    fontFamily.abortCreation();
-                    return resolveFallbackTypeface();
-                }
-                if (!fontFamily.freeze()) {
-                    return resolveFallbackTypeface();
-                }
-                FontFamily[] families = { fontFamily };
-                return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight,
-                        mItalic);
-            } else if (mFonts != null) {
-                final FontFamily fontFamily = new FontFamily();
-                boolean atLeastOneFont = false;
-                for (FontsContract.FontInfo font : mFonts) {
-                    final ByteBuffer fontBuffer = mFontBuffers.get(font.getUri());
-                    if (fontBuffer == null) {
-                        continue;  // skip
-                    }
-                    final boolean success = fontFamily.addFontFromBuffer(fontBuffer,
-                            font.getTtcIndex(), font.getAxes(), font.getWeight(),
-                            font.isItalic() ? STYLE_ITALIC : STYLE_NORMAL);
-                    if (!success) {
-                        fontFamily.abortCreation();
-                        return null;
-                    }
-                    atLeastOneFont = true;
-                }
-                if (!atLeastOneFont) {
-                    // No fonts are avaialble. No need to create new Typeface and returns fallback
-                    // Typeface instead.
-                    fontFamily.abortCreation();
-                    return null;
-                }
-                fontFamily.freeze();
-                FontFamily[] families = { fontFamily };
-                return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight,
-                        mItalic);
+                return typeface;
+            } catch (IOException | IllegalArgumentException e) {
+                return resolveFallbackTypeface();
             }
-
-            // Must not reach here.
-            throw new IllegalArgumentException("No source was set.");
         }
     }
 
@@ -713,8 +627,7 @@
         // TODO: Remove package modifier once android.graphics.FontFamily is deprecated.
         private final android.graphics.fonts.FontFamily mFamily;
         private String mFallbackName = null;
-        private @IntRange(from = 0, to = 1000) int mWeight = 400;
-        private boolean mItalic = false;
+        private @Nullable FontStyle mStyle;
 
         /**
          * Constructs a builder with a font family.
@@ -740,35 +653,16 @@
         }
 
         /**
-         * Sets a weight of the Typeface.
+         * Sets a font style of the Typeface.
          *
-         * If the font family doesn't have a font of given weight, system will select the closest
+         * If the font family doesn't have a font of given style, system will select the closest
          * font from font family. For example, if a font family has fonts of 300 weight and 700
          * weight then setWeight(400) is called, system will select the font of 300 weight.
          *
-         * @see Font#FONT_WEIGHT_THIN
-         * @see Font#FONT_WEIGHT_EXTRA_LIGHT
-         * @see Font#FONT_WEIGHT_LIGHT
-         * @see Font#FONT_WEIGHT_NORMAL
-         * @see Font#FONT_WEIGHT_MEDIUM
-         * @see Font#FONT_WEIGHT_SEMI_BOLD
-         * @see Font#FONT_WEIGHT_BOLD
-         * @see Font#FONT_WEIGHT_EXTRA_BOLD
-         * @see Font#FONT_WEIGHT_BLACK
-         * @param weight a weight value
+         * @param style a font style
          */
-        public CustomFallbackBuilder setWeight(@IntRange(from = 0, to = 1000) int weight) {
-            mWeight = weight;
-            return this;
-        }
-
-        /**
-         * Sets a italic style of the Typeface.
-         *
-         * @param italic true if italic, otherwise false
-         */
-        public CustomFallbackBuilder setItalic(boolean italic) {
-            mItalic = italic;
+        public CustomFallbackBuilder setStyle(@NonNull FontStyle style) {
+            mStyle = style;
             return this;
         }
 
@@ -778,14 +672,20 @@
          * @return the Typeface object
          */
         public Typeface build() {
-            final android.graphics.fonts.FontFamily[] fallback =
-                    SystemFonts.getSystemFallback(mFallbackName);
+            final FontFamily[] fallback = SystemFonts.getSystemFallback(mFallbackName);
+            final FontFamily[] fullFamilies = new FontFamily[fallback.length + 1];
             final long[] ptrArray = new long[fallback.length + 1];
             ptrArray[0] = mFamily.getNativePtr();
+            fullFamilies[0] = mFamily;
             for (int i = 0; i < fallback.length; ++i) {
                 ptrArray[i + 1] = fallback[i].getNativePtr();
+                fullFamilies[i + 1] = fallback[i];
             }
-            return new Typeface(nativeCreateFromArray(ptrArray, mWeight, mItalic ? 1 : 0));
+            final int weight = mStyle == null ? 400 : mStyle.getWeight();
+            final int italic =
+                    (mStyle == null || mStyle.getSlant() == FontStyle.FONT_SLANT_UPRIGHT) ?  0 : 1;
+
+            return new Typeface(nativeCreateFromArray(ptrArray, weight, italic), fullFamilies);
         }
     }
 
@@ -850,7 +750,7 @@
                 }
             }
 
-            typeface = new Typeface(nativeCreateFromTypeface(ni, style));
+            typeface = new Typeface(nativeCreateFromTypeface(ni, style), family.mFamilies);
             styles.put(style, typeface);
         }
         return typeface;
@@ -919,7 +819,7 @@
 
             typeface = new Typeface(
                     nativeCreateFromTypefaceWithExactStyle(
-                            base.native_instance, weight, italic));
+                            base.native_instance, weight, italic), base.mFamilies);
             innerCache.put(key, typeface);
         }
         return typeface;
@@ -928,8 +828,9 @@
     /** @hide */
     public static Typeface createFromTypefaceWithVariation(@Nullable Typeface family,
             @NonNull List<FontVariationAxis> axes) {
-        final long ni = family == null ? 0 : family.native_instance;
-        return new Typeface(nativeCreateFromTypefaceWithVariation(ni, axes));
+        final Typeface base = family == null ? Typeface.DEFAULT : family;
+        return new Typeface(nativeCreateFromTypefaceWithVariation(base.native_instance, axes),
+                base.mFamilies);
     }
 
     /**
@@ -1015,7 +916,7 @@
      */
     @Deprecated
     @UnsupportedAppUsage
-    private static Typeface createFromFamilies(FontFamily[] families) {
+    private static Typeface createFromFamilies(android.graphics.FontFamily[] families) {
         long[] ptrArray = new long[families.length];
         for (int i = 0; i < families.length; i++) {
             ptrArray[i] = families[i].mNativePtr;
@@ -1029,14 +930,13 @@
      *
      * @param families array of font families
      */
-    private static Typeface createFromFamilies(
-            @Nullable android.graphics.fonts.FontFamily[] families) {
+    private static Typeface createFromFamilies(@Nullable FontFamily[] families) {
         final long[] ptrArray = new long[families.length];
         for (int i = 0; i < families.length; ++i) {
             ptrArray[i] = families[i].getNativePtr();
         }
         return new Typeface(nativeCreateFromArray(ptrArray,
-                  RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+                  RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE), families);
     }
 
     /**
@@ -1044,8 +944,8 @@
      * TODO: Remove private API use in supportlib: http://b/72665240
      */
     @UnsupportedAppUsage
-    private static Typeface createFromFamiliesWithDefault(FontFamily[] families, int weight,
-                int italic) {
+    private static Typeface createFromFamiliesWithDefault(
+            android.graphics.FontFamily[] families, int weight, int italic) {
         return createFromFamiliesWithDefault(families, DEFAULT_FAMILY, weight, italic);
     }
 
@@ -1063,7 +963,7 @@
      * @param families array of font families
      */
     @UnsupportedAppUsage
-    private static Typeface createFromFamiliesWithDefault(FontFamily[] families,
+    private static Typeface createFromFamiliesWithDefault(android.graphics.FontFamily[] families,
                 String fallbackName, int weight, int italic) {
         android.graphics.fonts.FontFamily[] fallback = SystemFonts.getSystemFallback(fallbackName);
         long[] ptrArray = new long[families.length + fallback.length];
@@ -1084,6 +984,19 @@
         }
 
         native_instance = ni;
+        mFamilies = new FontFamily[0];
+        sRegistry.registerNativeAllocation(this, native_instance);
+        mStyle = nativeGetStyle(ni);
+        mWeight = nativeGetWeight(ni);
+    }
+
+    private Typeface(long ni, @NonNull FontFamily[] families) {
+        if (ni == 0) {
+            throw new IllegalStateException("native typeface cannot be made");
+        }
+
+        native_instance = ni;
+        mFamilies = families;
         sRegistry.registerNativeAllocation(this, native_instance);
         mStyle = nativeGetStyle(ni);
         mWeight = nativeGetWeight(ni);
@@ -1097,9 +1010,9 @@
     /** @hide */
     @VisibleForTesting
     public static void initSystemDefaultTypefaces(Map<String, Typeface> systemFontMap,
-            Map<String, android.graphics.fonts.FontFamily[]> fallbacks,
+            Map<String, FontFamily[]> fallbacks,
             FontConfig.Alias[] aliases) {
-        for (Map.Entry<String, android.graphics.fonts.FontFamily[]> entry : fallbacks.entrySet()) {
+        for (Map.Entry<String, FontFamily[]> entry : fallbacks.entrySet()) {
             systemFontMap.put(entry.getKey(), createFromFamilies(entry.getValue()));
         }
 
@@ -1110,7 +1023,8 @@
             final Typeface base = systemFontMap.get(alias.getToName());
             final int weight = alias.getWeight();
             final Typeface newFace = weight == 400 ? base :
-                    new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
+                    new Typeface(nativeCreateWeightAlias(base.native_instance, weight),
+                            base.mFamilies);
             systemFontMap.put(alias.getName(), newFace);
         }
     }
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 0a8c685..7f165bf 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -151,7 +151,21 @@
          * @param path the file name of the font data in the asset directory
          */
         public Builder(@NonNull AssetManager am, @NonNull String path) {
-            final long nativeAsset = nGetNativeAsset(am, path, true /* is asset */, 0 /* cookie */);
+            this(am, path, true /* is asset */, 0 /* cookie */);
+        }
+
+        /**
+         * Constructs a builder from an asset manager and a file path in an asset directory.
+         *
+         * @param am the application's asset manager
+         * @param path the file name of the font data in the asset directory
+         * @param isAsset true if the undelying data is in asset
+         * @param cookie set asset cookie
+         * @hide
+         */
+        public Builder(@NonNull AssetManager am, @NonNull String path, boolean isAsset,
+                int cookie) {
+            final long nativeAsset = nGetNativeAsset(am, path, isAsset, cookie);
             if (nativeAsset == 0) {
                 mException = new FileNotFoundException("Unable to open " + path);
                 return;
@@ -293,7 +307,7 @@
          * will resolve the style by reading font tables.
          *
          * For example, if you want to use italic font as upright font, call {@code
-         * setSlant(false)} explicitly.
+         * setSlant(FontStyle.FONT_SLANT_UPRIGHT)} explicitly.
          *
          * @return this builder
          */
@@ -447,23 +461,14 @@
     }
 
     /**
-     * Get a weight value associated with this font.
+     * Get a style associated with this font.
      *
      * @see Builder#setWeight(int)
-     * @return a weight value
+     * @see Builder#setSlant(int)
+     * @return a font style
      */
-    public @IntRange(from = 0, to = 1000)int getWeight() {
-        return mFontStyle.getWeight();
-    }
-
-    /**
-     * Get a slant value associated with this font.
-     *
-     * @see Builder#setSlant(boolean)
-     * @return a slant value
-     */
-    public @FontStyle.FontSlant int getSlant() {
-        return mFontStyle.getSlant();
+    public FontStyle getStyle() {
+        return mFontStyle;
     }
 
     /**
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index 52a37da..14d31d9 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -124,7 +124,7 @@
         }
 
         private static int makeStyleIdentifier(@NonNull Font font) {
-            return font.getWeight() | (font.getSlant()  << 16);
+            return font.getStyle().getWeight() | (font.getStyle().getSlant()  << 16);
         }
 
         private static native long nInitBuilder();
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 04cc5bb..288ba32 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -21,6 +21,7 @@
 #include <algorithm>
 #include <iterator>
 #include <set>
+#include <map>
 
 #include "android-base/logging.h"
 #include "android-base/stringprintf.h"
@@ -869,6 +870,17 @@
   }
 }
 
+uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) {
+  for (auto& package_group : package_groups_) {
+    for (auto& package2 : package_group.packages_) {
+      if (package2.loaded_package_ == package) {
+        return package_group.dynamic_ref_table.mAssignedPackageId;
+      }
+    }
+  }
+  return 0;
+}
+
 std::unique_ptr<Theme> AssetManager2::NewTheme() {
   return std::unique_ptr<Theme>(new Theme(this));
 }
@@ -1054,44 +1066,231 @@
   }
 }
 
-bool Theme::SetTo(const Theme& o) {
+void Theme::SetTo(const Theme& o) {
   if (this == &o) {
-    return true;
+    return;
   }
 
   type_spec_flags_ = o.type_spec_flags_;
 
-  const bool copy_only_system = asset_manager_ != o.asset_manager_;
-
-  for (size_t p = 0; p < packages_.size(); p++) {
-    const Package* package = o.packages_[p].get();
-    if (package == nullptr || (copy_only_system && p != 0x01)) {
-      // The other theme doesn't have this package, clear ours.
-      packages_[p].reset();
-      continue;
-    }
-
-    if (packages_[p] == nullptr) {
-      // The other theme has this package, but we don't. Make one.
-      packages_[p].reset(new Package());
-    }
-
-    for (size_t t = 0; t < package->types.size(); t++) {
-      const ThemeType* type = package->types[t].get();
-      if (type == nullptr) {
-        // The other theme doesn't have this type, clear ours.
-        packages_[p]->types[t].reset();
+  if (asset_manager_ == o.asset_manager_) {
+    // The theme comes from the same asset manager so all theme data can be copied exactly
+    for (size_t p = 0; p < packages_.size(); p++) {
+      const Package *package = o.packages_[p].get();
+      if (package == nullptr) {
+        // The other theme doesn't have this package, clear ours.
+        packages_[p].reset();
         continue;
       }
 
-      // Create a new type and update it to theirs.
-      const size_t type_alloc_size = sizeof(ThemeType) + (type->entry_count * sizeof(ThemeEntry));
-      void* copied_data = malloc(type_alloc_size);
-      memcpy(copied_data, type, type_alloc_size);
-      packages_[p]->types[t].reset(reinterpret_cast<ThemeType*>(copied_data));
+      if (packages_[p] == nullptr) {
+        // The other theme has this package, but we don't. Make one.
+        packages_[p].reset(new Package());
+      }
+
+      for (size_t t = 0; t < package->types.size(); t++) {
+        const ThemeType *type = package->types[t].get();
+        if (type == nullptr) {
+          // The other theme doesn't have this type, clear ours.
+          packages_[p]->types[t].reset();
+          continue;
+        }
+
+        // Create a new type and update it to theirs.
+        const size_t type_alloc_size = sizeof(ThemeType) + (type->entry_count * sizeof(ThemeEntry));
+        void *copied_data = malloc(type_alloc_size);
+        memcpy(copied_data, type, type_alloc_size);
+        packages_[p]->types[t].reset(reinterpret_cast<ThemeType *>(copied_data));
+      }
+    }
+  } else {
+    std::map<ApkAssetsCookie, ApkAssetsCookie> src_to_dest_asset_cookies;
+    typedef std::map<int, int> SourceToDestinationRuntimePackageMap;
+    std::map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map;
+
+    // Determine which ApkAssets are loaded in both theme AssetManagers
+    std::vector<const ApkAssets*> src_assets = o.asset_manager_->GetApkAssets();
+    for (size_t i = 0; i < src_assets.size(); i++) {
+      const ApkAssets* src_asset = src_assets[i];
+
+      std::vector<const ApkAssets*> dest_assets = asset_manager_->GetApkAssets();
+      for (size_t j = 0; j < dest_assets.size(); j++) {
+        const ApkAssets* dest_asset = dest_assets[j];
+
+        // Map the runtime package of the source apk asset to the destination apk asset
+        if (src_asset->GetPath() == dest_asset->GetPath()) {
+          const std::vector<std::unique_ptr<const LoadedPackage>>& src_packages =
+              src_asset->GetLoadedArsc()->GetPackages();
+          const std::vector<std::unique_ptr<const LoadedPackage>>& dest_packages =
+              dest_asset->GetLoadedArsc()->GetPackages();
+
+          SourceToDestinationRuntimePackageMap package_map;
+
+          // The source and destination package should have the same number of packages loaded in
+          // the same order.
+          const size_t N = src_packages.size();
+          CHECK(N == dest_packages.size())
+              << " LoadedArsc " << src_asset->GetPath() << " differs number of packages.";
+          for (size_t p = 0; p < N; p++) {
+            auto& src_package = src_packages[p];
+            auto& dest_package = dest_packages[p];
+            CHECK(src_package->GetPackageName() == dest_package->GetPackageName())
+                << " Package " << src_package->GetPackageName() << " differs in load order.";
+
+            int src_package_id = o.asset_manager_->GetAssignedPackageId(src_package.get());
+            int dest_package_id = asset_manager_->GetAssignedPackageId(dest_package.get());
+            package_map[src_package_id] = dest_package_id;
+          }
+
+          src_to_dest_asset_cookies.insert(std::pair<ApkAssetsCookie, ApkAssetsCookie>(i, j));
+          src_asset_cookie_id_map.insert(
+              std::pair<ApkAssetsCookie, SourceToDestinationRuntimePackageMap>(i, package_map));
+          break;
+        }
+      }
+    }
+
+    // Reset the data in the destination theme
+    for (size_t p = 0; p < packages_.size(); p++) {
+      if (packages_[p] != nullptr) {
+        packages_[p].reset();
+      }
+    }
+
+    for (size_t p = 0; p < packages_.size(); p++) {
+      const Package *package = o.packages_[p].get();
+      if (package == nullptr) {
+        continue;
+      }
+
+      for (size_t t = 0; t < package->types.size(); t++) {
+        const ThemeType *type = package->types[t].get();
+        if (type == nullptr) {
+          continue;
+        }
+
+        for (size_t e = 0; e < type->entry_count; e++) {
+          const ThemeEntry &entry = type->entries[e];
+          if (entry.value.dataType == Res_value::TYPE_NULL &&
+              entry.value.data != Res_value::DATA_NULL_EMPTY) {
+            continue;
+          }
+
+          // The package id of the attribute needs to be rewritten to the package id of the value in
+          // the destination
+          int attribute_dest_package_id = p;
+          if (attribute_dest_package_id != 0x01) {
+            // Find the cookie of the attribute resource id
+            FindEntryResult attribute_entry_result;
+            ApkAssetsCookie attribute_cookie =
+                o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ , false,
+                                            &attribute_entry_result);
+
+            // Determine the package id of the attribute in the destination AssetManager
+            auto attribute_package_map = src_asset_cookie_id_map.find(attribute_cookie);
+            if (attribute_package_map == src_asset_cookie_id_map.end()) {
+              continue;
+            }
+            auto attribute_dest_package = attribute_package_map->second.find(
+                attribute_dest_package_id);
+            if (attribute_dest_package == attribute_package_map->second.end()) {
+              continue;
+            }
+            attribute_dest_package_id = attribute_dest_package->second;
+          }
+
+          // If the attribute value represents an attribute or reference, the package id of the
+          // value needs to be rewritten to the package id of the value in the destination
+          uint32_t attribue_data = entry.value.data;
+          if (entry.value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE
+              || entry.value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE
+              || entry.value.dataType == Res_value::TYPE_ATTRIBUTE
+              || entry.value.dataType == Res_value::TYPE_REFERENCE) {
+
+            // Determine the package id of the reference in the destination AssetManager
+            auto value_package_map = src_asset_cookie_id_map.find(entry.cookie);
+            if (value_package_map == src_asset_cookie_id_map.end()) {
+              continue;
+            }
+
+            auto value_dest_package = value_package_map->second.find(
+                get_package_id(entry.value.data));
+            if (value_dest_package == value_package_map->second.end()) {
+              continue;
+            }
+
+            attribue_data = fix_package_id(entry.value.data, value_dest_package->second);
+          }
+
+          // Lazily instantiate the destination package
+          std::unique_ptr<Package>& dest_package = packages_[attribute_dest_package_id];
+          if (dest_package == nullptr) {
+            dest_package.reset(new Package());
+          }
+
+          // Lazily instantiate and resize the destination type
+          util::unique_cptr<ThemeType>& dest_type = dest_package->types[t];
+          if (dest_type == nullptr || dest_type->entry_count < type->entry_count) {
+            const size_t type_alloc_size = sizeof(ThemeType)
+                + (type->entry_count * sizeof(ThemeEntry));
+            void* dest_data = malloc(type_alloc_size);
+            memset(dest_data, 0, type->entry_count * sizeof(ThemeEntry));
+
+            // Copy the existing destination type values if the type is resized
+            if (dest_type != nullptr) {
+              memcpy(dest_data, type, sizeof(ThemeType)
+                                      + (dest_type->entry_count * sizeof(ThemeEntry)));
+            }
+
+            dest_type.reset(reinterpret_cast<ThemeType *>(dest_data));
+            dest_type->entry_count = type->entry_count;
+          }
+
+          // Find the cookie of the value in the destination
+          auto value_dest_cookie = src_to_dest_asset_cookies.find(entry.cookie);
+          if (value_dest_cookie == src_to_dest_asset_cookies.end()) {
+            continue;
+          }
+
+          dest_type->entries[e].cookie = value_dest_cookie->second;
+          dest_type->entries[e].value.dataType = entry.value.dataType;
+          dest_type->entries[e].value.data = attribue_data;
+          dest_type->entries[e].type_spec_flags = entry.type_spec_flags;
+        }
+      }
     }
   }
-  return true;
+}
+
+void Theme::Dump() const {
+  base::ScopedLogSeverity _log(base::INFO);
+  LOG(INFO) << base::StringPrintf("Theme(this=%p, AssetManager2=%p)", this, asset_manager_);
+
+  for (int p = 0; p < packages_.size(); p++) {
+    auto& package = packages_[p];
+    if (package == nullptr) {
+      continue;
+    }
+
+    for (int t = 0; t < package->types.size(); t++) {
+      auto& type = package->types[t];
+      if (type == nullptr) {
+        continue;
+      }
+
+      for (int e = 0; e < type->entry_count; e++) {
+        auto& entry = type->entries[e];
+        if (entry.value.dataType == Res_value::TYPE_NULL &&
+            entry.value.data != Res_value::DATA_NULL_EMPTY) {
+          continue;
+        }
+
+        LOG(INFO) << base::StringPrintf("  entry(0x%08x)=(0x%08x) type=(0x%02x), cookie(%d)",
+                                        make_resid(p, t, e), entry.value.data,
+                                        entry.value.dataType, entry.cookie);
+      }
+    }
+  }
 }
 
 }  // namespace android
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 2f0ee01..5312b06 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -74,6 +74,8 @@
 // AssetManager2 is the main entry point for accessing assets and resources.
 // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets.
 class AssetManager2 {
+  friend Theme;
+
  public:
   struct ResourceName {
     const char* package = nullptr;
@@ -285,6 +287,9 @@
   // been seen while traversing bag parents.
   const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids);
 
+  // Retrieve the assigned package id of the package if loaded into this AssetManager
+  uint8_t GetAssignedPackageId(const LoadedPackage* package);
+
   // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
   // have a longer lifetime.
   std::vector<const ApkAssets*> apk_assets_;
@@ -355,11 +360,14 @@
   bool ApplyStyle(uint32_t resid, bool force = false);
 
   // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme.
-  // Returns false if the AssetManagers of the Themes were not compatible.
-  bool SetTo(const Theme& o);
+  // If `o` does not have the same AssetManager as this theme, only attributes from ApkAssets loaded
+  // into both AssetManagers will be copied to this theme.
+  void SetTo(const Theme& o);
 
   void Clear();
 
+  void Dump() const;
+
   inline const AssetManager2* GetAssetManager() const {
     return asset_manager_;
   }
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index 55d53ed..2c39cee 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -21,12 +21,14 @@
 #include "TestHelpers.h"
 #include "androidfw/ResourceUtils.h"
 #include "data/lib_one/R.h"
+#include "data/lib_two/R.h"
 #include "data/libclient/R.h"
 #include "data/styles/R.h"
 #include "data/system/R.h"
 
 namespace app = com::android::app;
 namespace lib_one = com::android::lib_one;
+namespace lib_two = com::android::lib_two;
 namespace libclient = com::android::libclient;
 
 namespace android {
@@ -263,7 +265,7 @@
   ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree));
 
   // Copy the theme to theme_one.
-  ASSERT_TRUE(theme_one->SetTo(*theme_two));
+  theme_one->SetTo(*theme_two);
 
   // Clear theme_two to make sure we test that there WAS a copy.
   theme_two->Clear();
@@ -279,12 +281,14 @@
   EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags);
 }
 
-TEST_F(ThemeTest, OnlyCopySystemThemeWhenAssetManagersDiffer) {
+TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) {
   AssetManager2 assetmanager_one;
-  assetmanager_one.SetApkAssets({system_assets_.get(), style_assets_.get()});
+  assetmanager_one.SetApkAssets({system_assets_.get(), lib_one_assets_.get(), style_assets_.get(),
+                                 libclient_assets_.get()});
 
   AssetManager2 assetmanager_two;
-  assetmanager_two.SetApkAssets({system_assets_.get(), style_assets_.get()});
+  assetmanager_two.SetApkAssets({system_assets_.get(), lib_two_assets_.get(), lib_one_assets_.get(),
+                                 style_assets_.get()});
 
   auto theme_one = assetmanager_one.NewTheme();
   ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne));
@@ -292,17 +296,34 @@
   auto theme_two = assetmanager_two.NewTheme();
   ASSERT_TRUE(theme_two->ApplyStyle(R::style::Theme_One));
   ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleTwo));
+  ASSERT_TRUE(theme_two->ApplyStyle(fix_package_id(lib_one::R::style::Theme, 0x03),
+                                    false /*force*/));
+  ASSERT_TRUE(theme_two->ApplyStyle(fix_package_id(lib_two::R::style::Theme, 0x02),
+                                    false /*force*/));
 
-  EXPECT_TRUE(theme_one->SetTo(*theme_two));
+  theme_one->SetTo(*theme_two);
 
   Res_value value;
   uint32_t flags;
 
-  // No app resources.
-  EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags));
+  // System resources (present in destination asset manager)
+  EXPECT_EQ(0, theme_one->GetAttribute(R::attr::foreground, &value, &flags));
 
-  // Only system.
-  EXPECT_NE(kInvalidCookie, theme_one->GetAttribute(R::attr::foreground, &value, &flags));
+  // The cookie of the style asset is 3 in the source and 2 in the destination.
+  // Check that the cookie has been rewritten to the destination values
+  EXPECT_EQ(2, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags));
+
+  // The cookie of the lib_one asset is 2 in the source and 1 in the destination.
+  // The package id of the lib_one package is 0x03 in the source and 0x02 in the destination
+  // Check that the cookie and packages have been rewritten to the destination values
+  EXPECT_EQ(1, theme_one->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02), &value,
+                                       &flags));
+  EXPECT_EQ(1, theme_one->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02), &value,
+                                       &flags));
+
+  // attr2 references an attribute in lib_one. Check that the resolution of the attribute value is
+  // correct after the value of attr2 had its package id rewritten to the destination package id
+  EXPECT_EQ(700, value.data);
 }
 
 }  // namespace android
diff --git a/libs/androidfw/tests/data/lib_two/R.h b/libs/androidfw/tests/data/lib_two/R.h
index c04a9d3..92b9cc1 100644
--- a/libs/androidfw/tests/data/lib_two/R.h
+++ b/libs/androidfw/tests/data/lib_two/R.h
@@ -24,12 +24,24 @@
 namespace lib_two {
 
 struct R {
+  struct attr {
+    enum : uint32_t {
+      attr3 = 0x02010000, // default
+    };
+  };
+
   struct string {
     enum : uint32_t {
       LibraryString = 0x02020000,  // default
       foo = 0x02020001, // default
     };
   };
+
+  struct style {
+    enum : uint32_t {
+      Theme = 0x02030000, // default
+    };
+  };
 };
 
 }  // namespace lib_two
diff --git a/libs/androidfw/tests/data/lib_two/lib_two.apk b/libs/androidfw/tests/data/lib_two/lib_two.apk
index ad44f9c..486c230 100644
--- a/libs/androidfw/tests/data/lib_two/lib_two.apk
+++ b/libs/androidfw/tests/data/lib_two/lib_two.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/lib_two/res/values/values.xml b/libs/androidfw/tests/data/lib_two/res/values/values.xml
index f4eea26..340d14c 100644
--- a/libs/androidfw/tests/data/lib_two/res/values/values.xml
+++ b/libs/androidfw/tests/data/lib_two/res/values/values.xml
@@ -15,9 +15,17 @@
 -->
 
 <resources>
+    <public type="attr" name="attr3" id="0x00010000" />
+    <attr name="attr3" format="integer" />
+
     <public type="string" name="LibraryString" id="0x00020000" />
     <string name="LibraryString">Hi from library two</string>
 
     <public type="string" name="foo" id="0x00020001" />
     <string name="foo">Foo from lib_two</string>
+
+    <public type="style" name="Theme" id="0x02030000" />
+    <style name="Theme">
+        <item name="com.android.lib_two:attr3">800</item>
+    </style>
 </resources>
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 753557c..75a6e72 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -286,7 +286,6 @@
 }
 
 void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
-    outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
     if (isHardware()) {
         outBitmap->allocPixels(SkImageInfo::Make(info().width(), info().height(),
                                                  info().colorType(), info().alphaType(), nullptr));
@@ -321,7 +320,6 @@
         SkBitmap skiaBitmap;
         skiaBitmap.setInfo(info(), rowBytes());
         skiaBitmap.setPixelRef(sk_ref_sp(this), 0, 0);
-        skiaBitmap.setHasHardwareMipMap(mHasHardwareMipMap);
         // Note we don't cache in this case, because the raster image holds a pointer to this Bitmap
         // internally and ~Bitmap won't be invoked.
         // TODO: refactor Bitmap to not derive from SkPixelRef, which would allow caching here.
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 3fa73a4..596b8af 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -81,6 +81,11 @@
 }
 
 void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) {
+    if (mCurrentBarrier && enableReorder) {
+        // Already in a re-order section, nothing to do
+        return;
+    }
+
     if (nullptr != mCurrentBarrier) {
         // finish off the existing chunk
         SkDrawable* drawable =
@@ -89,9 +94,8 @@
         drawDrawable(drawable);
     }
     if (enableReorder) {
-        mCurrentBarrier = (StartReorderBarrierDrawable*)
-                                  mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
-                                          mDisplayList.get());
+        mCurrentBarrier = mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
+                mDisplayList.get());
         drawDrawable(mCurrentBarrier);
     }
 }
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index b34f270..3e3e651 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -273,6 +273,11 @@
      * supports {@link #ENCODING_E_AC3} but not {@link #ENCODING_E_AC3_JOC}.
      **/
     public static final int ENCODING_E_AC3_JOC = 18;
+    /** Audio data format: Dolby MAT (Metadata-enhanced Audio Transmission)
+     * Dolby MAT bitstreams are used to transmit Dolby TrueHD, channel-based PCM, or PCM with
+     * metadata (object audio) over HDMI (e.g. Dolby Atmos content).
+     **/
+    public static final int ENCODING_DOLBY_MAT = 19;
 
     /** @hide */
     public static String toLogFriendlyEncoding(int enc) {
@@ -313,6 +318,8 @@
                 return "ENCODING_AC4";
             case ENCODING_E_AC3_JOC:
                 return "ENCODING_E_AC3_JOC";
+            case ENCODING_DOLBY_MAT:
+                return "ENCODING_DOLBY_MAT";
             default :
                 return "invalid encoding " + enc;
         }
@@ -520,26 +527,27 @@
     public static boolean isValidEncoding(int audioFormat)
     {
         switch (audioFormat) {
-        case ENCODING_PCM_16BIT:
-        case ENCODING_PCM_8BIT:
-        case ENCODING_PCM_FLOAT:
-        case ENCODING_AC3:
-        case ENCODING_E_AC3:
-        case ENCODING_DTS:
-        case ENCODING_DTS_HD:
-        case ENCODING_MP3:
-        case ENCODING_AAC_LC:
-        case ENCODING_AAC_HE_V1:
-        case ENCODING_AAC_HE_V2:
-        case ENCODING_IEC61937:
-        case ENCODING_DOLBY_TRUEHD:
-        case ENCODING_AAC_ELD:
-        case ENCODING_AAC_XHE:
-        case ENCODING_AC4:
-        case ENCODING_E_AC3_JOC:
-            return true;
-        default:
-            return false;
+            case ENCODING_PCM_16BIT:
+            case ENCODING_PCM_8BIT:
+            case ENCODING_PCM_FLOAT:
+            case ENCODING_AC3:
+            case ENCODING_E_AC3:
+            case ENCODING_DTS:
+            case ENCODING_DTS_HD:
+            case ENCODING_MP3:
+            case ENCODING_AAC_LC:
+            case ENCODING_AAC_HE_V1:
+            case ENCODING_AAC_HE_V2:
+            case ENCODING_IEC61937:
+            case ENCODING_DOLBY_TRUEHD:
+            case ENCODING_AAC_ELD:
+            case ENCODING_AAC_XHE:
+            case ENCODING_AC4:
+            case ENCODING_E_AC3_JOC:
+            case ENCODING_DOLBY_MAT:
+                return true;
+            default:
+                return false;
         }
     }
 
@@ -547,26 +555,27 @@
     public static boolean isPublicEncoding(int audioFormat)
     {
         switch (audioFormat) {
-        case ENCODING_PCM_16BIT:
-        case ENCODING_PCM_8BIT:
-        case ENCODING_PCM_FLOAT:
-        case ENCODING_AC3:
-        case ENCODING_E_AC3:
-        case ENCODING_DTS:
-        case ENCODING_DTS_HD:
-        case ENCODING_MP3:
-        case ENCODING_AAC_LC:
-        case ENCODING_AAC_HE_V1:
-        case ENCODING_AAC_HE_V2:
-        case ENCODING_IEC61937:
-        case ENCODING_DOLBY_TRUEHD:
-        case ENCODING_AAC_ELD:
-        case ENCODING_AAC_XHE:
-        case ENCODING_AC4:
-        case ENCODING_E_AC3_JOC:
-            return true;
-        default:
-            return false;
+            case ENCODING_PCM_16BIT:
+            case ENCODING_PCM_8BIT:
+            case ENCODING_PCM_FLOAT:
+            case ENCODING_AC3:
+            case ENCODING_E_AC3:
+            case ENCODING_DTS:
+            case ENCODING_DTS_HD:
+            case ENCODING_MP3:
+            case ENCODING_AAC_LC:
+            case ENCODING_AAC_HE_V1:
+            case ENCODING_AAC_HE_V2:
+            case ENCODING_IEC61937:
+            case ENCODING_DOLBY_TRUEHD:
+            case ENCODING_AAC_ELD:
+            case ENCODING_AAC_XHE:
+            case ENCODING_AC4:
+            case ENCODING_E_AC3_JOC:
+            case ENCODING_DOLBY_MAT:
+                return true;
+            default:
+                return false;
         }
     }
 
@@ -575,29 +584,30 @@
     public static boolean isEncodingLinearPcm(int audioFormat)
     {
         switch (audioFormat) {
-        case ENCODING_PCM_16BIT:
-        case ENCODING_PCM_8BIT:
-        case ENCODING_PCM_FLOAT:
-        case ENCODING_DEFAULT:
-            return true;
-        case ENCODING_AC3:
-        case ENCODING_E_AC3:
-        case ENCODING_DTS:
-        case ENCODING_DTS_HD:
-        case ENCODING_MP3:
-        case ENCODING_AAC_LC:
-        case ENCODING_AAC_HE_V1:
-        case ENCODING_AAC_HE_V2:
-        case ENCODING_IEC61937: // wrapped in PCM but compressed
-        case ENCODING_DOLBY_TRUEHD:
-        case ENCODING_AAC_ELD:
-        case ENCODING_AAC_XHE:
-        case ENCODING_AC4:
-        case ENCODING_E_AC3_JOC:
-            return false;
-        case ENCODING_INVALID:
-        default:
-            throw new IllegalArgumentException("Bad audio format " + audioFormat);
+            case ENCODING_PCM_16BIT:
+            case ENCODING_PCM_8BIT:
+            case ENCODING_PCM_FLOAT:
+            case ENCODING_DEFAULT:
+                return true;
+            case ENCODING_AC3:
+            case ENCODING_E_AC3:
+            case ENCODING_DTS:
+            case ENCODING_DTS_HD:
+            case ENCODING_MP3:
+            case ENCODING_AAC_LC:
+            case ENCODING_AAC_HE_V1:
+            case ENCODING_AAC_HE_V2:
+            case ENCODING_IEC61937: // wrapped in PCM but compressed
+            case ENCODING_DOLBY_TRUEHD:
+            case ENCODING_AAC_ELD:
+            case ENCODING_AAC_XHE:
+            case ENCODING_AC4:
+            case ENCODING_E_AC3_JOC:
+            case ENCODING_DOLBY_MAT:
+                return false;
+            case ENCODING_INVALID:
+            default:
+                throw new IllegalArgumentException("Bad audio format " + audioFormat);
         }
     }
 
@@ -605,29 +615,30 @@
     public static boolean isEncodingLinearFrames(int audioFormat)
     {
         switch (audioFormat) {
-        case ENCODING_PCM_16BIT:
-        case ENCODING_PCM_8BIT:
-        case ENCODING_PCM_FLOAT:
-        case ENCODING_IEC61937: // same size as stereo PCM
-        case ENCODING_DEFAULT:
-            return true;
-        case ENCODING_AC3:
-        case ENCODING_E_AC3:
-        case ENCODING_DTS:
-        case ENCODING_DTS_HD:
-        case ENCODING_MP3:
-        case ENCODING_AAC_LC:
-        case ENCODING_AAC_HE_V1:
-        case ENCODING_AAC_HE_V2:
-        case ENCODING_DOLBY_TRUEHD:
-        case ENCODING_AAC_ELD:
-        case ENCODING_AAC_XHE:
-        case ENCODING_AC4:
-        case ENCODING_E_AC3_JOC:
-            return false;
-        case ENCODING_INVALID:
-        default:
-            throw new IllegalArgumentException("Bad audio format " + audioFormat);
+            case ENCODING_PCM_16BIT:
+            case ENCODING_PCM_8BIT:
+            case ENCODING_PCM_FLOAT:
+            case ENCODING_IEC61937: // same size as stereo PCM
+            case ENCODING_DEFAULT:
+                return true;
+            case ENCODING_AC3:
+            case ENCODING_E_AC3:
+            case ENCODING_DTS:
+            case ENCODING_DTS_HD:
+            case ENCODING_MP3:
+            case ENCODING_AAC_LC:
+            case ENCODING_AAC_HE_V1:
+            case ENCODING_AAC_HE_V2:
+            case ENCODING_DOLBY_TRUEHD:
+            case ENCODING_AAC_ELD:
+            case ENCODING_AAC_XHE:
+            case ENCODING_AC4:
+            case ENCODING_E_AC3_JOC:
+            case ENCODING_DOLBY_MAT:
+                return false;
+            case ENCODING_INVALID:
+            default:
+                throw new IllegalArgumentException("Bad audio format " + audioFormat);
         }
     }
     /**
@@ -867,6 +878,7 @@
                 case ENCODING_AAC_XHE:
                 case ENCODING_AC4:
                 case ENCODING_E_AC3_JOC:
+                case ENCODING_DOLBY_MAT:
                     mEncoding = encoding;
                     break;
                 case ENCODING_INVALID:
@@ -1083,7 +1095,8 @@
         ENCODING_AAC_ELD,
         ENCODING_AAC_XHE,
         ENCODING_AC4,
-        ENCODING_E_AC3_JOC }
+        ENCODING_E_AC3_JOC,
+        ENCODING_DOLBY_MAT }
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface Encoding {}
@@ -1098,6 +1111,7 @@
             ENCODING_DOLBY_TRUEHD,
             ENCODING_AC4,
             ENCODING_E_AC3_JOC,
+            ENCODING_DOLBY_MAT,
     };
 
     /** @hide */
@@ -1109,7 +1123,8 @@
             ENCODING_AAC_LC,
             ENCODING_DOLBY_TRUEHD,
             ENCODING_AC4,
-            ENCODING_E_AC3_JOC }
+            ENCODING_E_AC3_JOC,
+            ENCODING_DOLBY_MAT }
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface SurroundSoundEncoding {}
@@ -1141,6 +1156,8 @@
                 return "Dolby AC-4";
             case ENCODING_E_AC3_JOC:
                 return "Dolby Atmos in Dolby Digital Plus";
+            case ENCODING_DOLBY_MAT:
+                return "Dolby MAT";
             default:
                 return "Unknown surround sound format";
         }
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 0462347..9baeaaa 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -64,6 +64,7 @@
         <item name="android:paddingBottom">@dimen/bottom_text_spacing_digital</item>
         <item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item>
         <item name="android:fontFeatureSettings">@*android:string/config_headlineFontFeatureSettings</item>
+        <item name="android:ellipsize">none</item>
     </style>
 
     <style name="BouncerSecurityContainer">
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 4c24a21..f81ffe9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
 import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 import java.util.ArrayList;
 import java.util.List;
@@ -88,11 +89,13 @@
         mNetworkController = Dependency.get(NetworkController.class);
         mSecurityController = Dependency.get(SecurityController.class);
 
+        Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
         mNetworkController.addCallback(this);
         mSecurityController.addCallback(this);
     }
 
     public void destroy() {
+        Dependency.get(TunerService.class).removeTunable(this);
         mNetworkController.removeCallback(this);
         mSecurityController.removeCallback(this);
     }
@@ -137,6 +140,7 @@
             mBlockWifi = blockWifi || mForceBlockWifi;
             // Re-register to get new callbacks.
             mNetworkController.removeCallback(this);
+            mNetworkController.addCallback(this);
         }
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index d3842b7..4205ac7 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -21,14 +21,11 @@
 
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sFullScreenMode;
-import static com.android.server.autofill.Helper.sPartitionMaxCount;
-import static com.android.server.autofill.Helper.sVisibleDatasetsMaxCount;
 import static com.android.server.autofill.Helper.sVerbose;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
-import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityThread;
 import android.content.BroadcastReceiver;
@@ -38,13 +35,10 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.graphics.Rect;
-import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.Parcelable;
 import android.os.RemoteCallback;
@@ -53,7 +47,6 @@
 import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.service.autofill.FillEventHistory;
 import android.service.autofill.UserData;
@@ -63,7 +56,6 @@
 import android.util.LocalLog;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillManagerInternal;
@@ -73,14 +65,12 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
+import com.android.server.AbstractMasterSystemService;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
-import com.android.server.SystemService;
 import com.android.server.autofill.ui.AutoFillUI;
 
 import java.io.FileDescriptor;
@@ -98,10 +88,13 @@
  * {@link AutofillManagerServiceImpl} per user; the real work is done by
  * {@link AutofillManagerServiceImpl} itself.
  */
-public final class AutofillManagerService extends SystemService {
+public final class AutofillManagerService
+        extends AbstractMasterSystemService<AutofillManagerServiceImpl> {
 
     private static final String TAG = "AutofillManagerService";
 
+    private static final Object sLock = AutofillManagerService.class;
+
     static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
 
     private static final char COMPAT_PACKAGE_DELIMITER = ':';
@@ -109,27 +102,28 @@
     private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_BEGIN = '[';
     private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_END = ']';
 
-    private final Context mContext;
+
+    /**
+     * Maximum number of partitions that can be allowed in a session.
+     *
+     * <p>Can be modified using {@code cmd autofill set max_partitions} or through
+     * {@link android.provider.Settings.Global#AUTOFILL_MAX_PARTITIONS_SIZE}.
+     */
+    @GuardedBy("sLock")
+    private static int sPartitionMaxCount = AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE;
+
+    /**
+     * Maximum number of visible datasets in the dataset picker UI, or {@code 0} to use default
+     * value from resources.
+     *
+     * <p>Can be modified using {@code cmd autofill set max_visible_datasets} or through
+     * {@link android.provider.Settings.Global#AUTOFILL_MAX_VISIBLE_DATASETS}.
+     */
+    @GuardedBy("sLock")
+    private static int sVisibleDatasetsMaxCount = 0;
+
     private final AutoFillUI mUi;
 
-    private final Object mLock = new Object();
-
-    /**
-     * Cache of {@link AutofillManagerServiceImpl} per user id.
-     * <p>
-     * It has to be mapped by user id because the same current user could have simultaneous sessions
-     * associated to different user profiles (for example, in a multi-window environment or when
-     * device has work profiles).
-     */
-    @GuardedBy("mLock")
-    private SparseArray<AutofillManagerServiceImpl> mServicesCache = new SparseArray<>();
-
-    /**
-     * Users disabled due to {@link UserManager} restrictions.
-     */
-    @GuardedBy("mLock")
-    private final SparseBooleanArray mDisabledUsers = new SparseBooleanArray();
-
     private final LocalLog mRequestsHistory = new LocalLog(20);
     private final LocalLog mUiLatencyHistory = new LocalLog(20);
     private final LocalLog mWtfHistory = new LocalLog(50);
@@ -148,22 +142,19 @@
                 // beneath it is brought back to top. Ideally, we should just hide the UI and
                 // bring it back when the activity resumes.
                 synchronized (mLock) {
-                    for (int i = 0; i < mServicesCache.size(); i++) {
-                        mServicesCache.valueAt(i).destroyFinishedSessionsLocked();
-                    }
+                    visitServicesLocked((s) -> s.destroyFinishedSessionsLocked());
                 }
-
                 mUi.hideAll(null);
             }
         }
     };
 
+    // TODO(b/117779333): move to superclass / create super-class for ShellCommand
     @GuardedBy("mLock")
     private boolean mAllowInstantService;
 
     public AutofillManagerService(Context context) {
-        super(context);
-        mContext = context;
+        super(context, UserManager.DISALLOW_AUTOFILL);
         mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext());
 
         setLogLevelFromSettings();
@@ -172,199 +163,91 @@
 
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        mContext.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler());
+        context.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler());
+    }
 
-        // Hookup with UserManager to disable service when necessary.
-        final UserManager um = context.getSystemService(UserManager.class);
-        final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
-        final List<UserInfo> users = um.getUsers();
-        for (int i = 0; i < users.size(); i++) {
-            final int userId = users.get(i).id;
-            final boolean disabled = umi.getUserRestriction(userId, UserManager.DISALLOW_AUTOFILL);
-            if (disabled) {
-                Slog.i(TAG, "Disabling Autofill for user " + userId);
-                mDisabledUsers.put(userId, disabled);
-            }
+    @Override // from MasterSystemService
+    protected String getServiceSettingsProperty() {
+        return Settings.Secure.AUTOFILL_SERVICE;
+    }
+
+    @Override // from MasterSystemService
+    protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
+            @NonNull ContentObserver observer) {
+        resolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, observer,
+                UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.AUTOFILL_LOGGING_LEVEL), false, observer,
+                UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE), false, observer,
+                UserHandle.USER_ALL);
+        resolver.registerContentObserver(Settings.Global.getUriFor(
+                Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS), false, observer,
+                UserHandle.USER_ALL);
+    }
+
+    @Override // from MasterSystemService
+    protected void onSettingsChanged(int userId, @NonNull String property) {
+        switch (property) {
+            case Settings.Global.AUTOFILL_LOGGING_LEVEL:
+                setLogLevelFromSettings();
+                break;
+            case Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE:
+                setMaxPartitionsFromSettings();
+                break;
+            case Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS:
+                setMaxVisibleDatasetsFromSettings();
+                break;
+            default:
+                Slog.w(TAG, "Unexpected property (" + property + "); updating cache instead");
+                // fall through
+            case Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES:
+                synchronized (mLock) {
+                    updateCachedServiceLocked(userId);
+                }
         }
-        umi.addUserRestrictionsListener((userId, newRestrictions, prevRestrictions) -> {
-            final boolean disabledNow =
-                    newRestrictions.getBoolean(UserManager.DISALLOW_AUTOFILL, false);
-            synchronized (mLock) {
-                final boolean disabledBefore = mDisabledUsers.get(userId);
-                if (disabledBefore == disabledNow) {
-                    // Nothing changed, do nothing.
-                    if (sDebug) {
-                        Slog.d(TAG, "Autofill restriction did not change for user " + userId);
-                        return;
-                    }
-                }
-                Slog.i(TAG, "Updating Autofill for user " + userId + ": disabled=" + disabledNow);
-                mDisabledUsers.put(userId, disabledNow);
-                updateCachedServiceLocked(userId, disabledNow);
-            }
-        });
-        startTrackingPackageChanges();
     }
 
-    private void startTrackingPackageChanges() {
-        PackageMonitor monitor = new PackageMonitor() {
-            @Override
-            public void onSomePackagesChanged() {
-                synchronized (mLock) {
-                    updateCachedServiceLocked(getChangingUserId());
-                }
-            }
-
-            @Override
-            public void onPackageUpdateFinished(String packageName, int uid) {
-                synchronized (mLock) {
-                    final String activePackageName = getActiveAutofillServicePackageName();
-                    if (packageName.equals(activePackageName)) {
-                        removeCachedServiceLocked(getChangingUserId());
-                    } else {
-                        handlePackageUpdateLocked(packageName);
-                    }
-                }
-            }
-
-            @Override
-            public void onPackageRemoved(String packageName, int uid) {
-                synchronized (mLock) {
-                    final int userId = getChangingUserId();
-                    final AutofillManagerServiceImpl userState = peekServiceForUserLocked(userId);
-                    if (userState != null) {
-                        final ComponentName componentName = userState.getServiceComponentName();
-                        if (componentName != null) {
-                            if (packageName.equals(componentName.getPackageName())) {
-                                handleActiveAutofillServiceRemoved(userId);
-                            }
-                        }
-                    }
-                }
-            }
-
-            @Override
-            public boolean onHandleForceStop(Intent intent, String[] packages,
-                    int uid, boolean doit) {
-                synchronized (mLock) {
-                    final String activePackageName = getActiveAutofillServicePackageName();
-                    for (String pkg : packages) {
-                        if (pkg.equals(activePackageName)) {
-                            if (!doit) {
-                                return true;
-                            }
-                            removeCachedServiceLocked(getChangingUserId());
-                        } else {
-                          handlePackageUpdateLocked(pkg);
-                        }
-                    }
-                }
-                return false;
-            }
-
-            private void handleActiveAutofillServiceRemoved(int userId) {
-                removeCachedServiceLocked(userId);
-                Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                        Settings.Secure.AUTOFILL_SERVICE, null, userId);
-            }
-
-            private String getActiveAutofillServicePackageName() {
-                final int userId = getChangingUserId();
-                final AutofillManagerServiceImpl userState = peekServiceForUserLocked(userId);
-                if (userState == null) {
-                    return null;
-                }
-                final ComponentName serviceComponent = userState.getServiceComponentName();
-                if (serviceComponent == null) {
-                    return null;
-                }
-                return serviceComponent.getPackageName();
-            }
-
-            @GuardedBy("mLock")
-            private void handlePackageUpdateLocked(String packageName) {
-                final int size = mServicesCache.size();
-                for (int i = 0; i < size; i++) {
-                    mServicesCache.valueAt(i).handlePackageUpdateLocked(packageName);
-                }
-            }
-        };
-
-        // package changes
-        monitor.register(mContext, null,  UserHandle.ALL, true);
+    @Override // from MasterSystemService
+    protected AutofillManagerServiceImpl newServiceLocked(int resolvedUserId, boolean disabled) {
+        return new AutofillManagerServiceImpl(this, mLock, mRequestsHistory,
+                mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi, mAutofillCompatState,
+                disabled);
     }
 
-    @Override
+    @Override // MasterSystemService
+    protected AutofillManagerServiceImpl removeCachedServiceLocked(int userId) {
+        final AutofillManagerServiceImpl service = super.removeCachedServiceLocked(userId);
+        if (service != null) {
+            service.destroyLocked();
+            mAutofillCompatState.removeCompatibilityModeRequests(userId);
+        }
+        return service;
+    }
+
+    @Override // from MasterSystemService
+    protected void onServiceEnabledLocked(@NonNull AutofillManagerServiceImpl service, int userId) {
+        addCompatibilityModeRequestsLocked(service, userId);
+    }
+
+    @Override // from SystemService
     public void onStart() {
         publishBinderService(AUTOFILL_MANAGER_SERVICE, new AutoFillManagerServiceStub());
         publishLocalService(AutofillManagerInternal.class, mLocalService);
     }
 
-    @Override
-    public void onBootPhase(int phase) {
-        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
-            new SettingsObserver(BackgroundThread.getHandler());
-        }
-    }
-
-    @Override
-    public void onUnlockUser(int userId) {
-        synchronized (mLock) {
-            updateCachedServiceLocked(userId);
-        }
-    }
-
-    @Override
+    @Override // from SystemService
     public void onSwitchUser(int userHandle) {
         if (sDebug) Slog.d(TAG, "Hiding UI when user switched");
         mUi.hideAll(null);
     }
 
-    @Override
-    public void onCleanupUser(int userId) {
-        synchronized (mLock) {
-            removeCachedServiceLocked(userId);
-        }
-    }
-
-    /**
-     * Gets the service instance for an user.
-     *
-     * @return service instance.
-     */
-    @GuardedBy("mLock")
-    @NonNull
-    AutofillManagerServiceImpl getServiceForUserLocked(int userId) {
-        final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
-                Binder.getCallingUid(), userId, false, false, null, null);
-        AutofillManagerServiceImpl service = mServicesCache.get(resolvedUserId);
-        if (service == null) {
-            service = new AutofillManagerServiceImpl(mContext, mLock, mRequestsHistory,
-                    mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi,
-                    mAutofillCompatState, mDisabledUsers.get(resolvedUserId));
-            mServicesCache.put(userId, service);
-            addCompatibilityModeRequestsLocked(service, userId);
-        }
-        return service;
-    }
-
-    /**
-     * Peeks the service instance for a user.
-     *
-     * @return service instance or {@code null} if not already present
-     */
-    @GuardedBy("mLock")
-    @Nullable
-    AutofillManagerServiceImpl peekServiceForUserLocked(int userId) {
-        final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
-                Binder.getCallingUid(), userId, false, false, null, null);
-        return mServicesCache.get(resolvedUserId);
-    }
-
     // Called by Shell command.
     void destroySessions(int userId, IResultReceiver receiver) {
         Slog.i(TAG, "destroySessions() for userId " + userId);
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
         synchronized (mLock) {
             if (userId != UserHandle.USER_ALL) {
@@ -373,10 +256,7 @@
                     service.destroySessionsLocked();
                 }
             } else {
-                final int size = mServicesCache.size();
-                for (int i = 0; i < size; i++) {
-                    mServicesCache.valueAt(i).destroySessionsLocked();
-                }
+                visitServicesLocked((s) -> s.destroySessionsLocked());
             }
         }
 
@@ -390,7 +270,7 @@
     // Called by Shell command.
     void listSessions(int userId, IResultReceiver receiver) {
         Slog.i(TAG, "listSessions() for userId " + userId);
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
         final Bundle resultData = new Bundle();
         final ArrayList<String> sessions = new ArrayList<>();
@@ -402,10 +282,7 @@
                     service.listSessionsLocked(sessions);
                 }
             } else {
-                final int size = mServicesCache.size();
-                for (int i = 0; i < size; i++) {
-                    mServicesCache.valueAt(i).listSessionsLocked(sessions);
-                }
+                visitServicesLocked((s) -> s.listSessionsLocked(sessions));
             }
         }
 
@@ -420,25 +297,22 @@
     // Called by Shell command.
     void reset() {
         Slog.i(TAG, "reset()");
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
         synchronized (mLock) {
-            final int size = mServicesCache.size();
-            for (int i = 0; i < size; i++) {
-                mServicesCache.valueAt(i).destroyLocked();
-            }
-            mServicesCache.clear();
+            visitServicesLocked((s) -> s.destroyLocked());
+            clearCacheLocked();
         }
     }
 
     // Called by Shell command.
     void setLogLevel(int level) {
         Slog.i(TAG, "setLogLevel(): " + level);
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
         final long token = Binder.clearCallingIdentity();
         try {
-            Settings.Global.putInt(mContext.getContentResolver(),
+            Settings.Global.putInt(getContext().getContentResolver(),
                     Settings.Global.AUTOFILL_LOGGING_LEVEL, level);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -447,7 +321,7 @@
 
     private void setLogLevelFromSettings() {
         final int level = Settings.Global.getInt(
-                mContext.getContentResolver(),
+                getContext().getContentResolver(),
                 Settings.Global.AUTOFILL_LOGGING_LEVEL, AutofillManager.DEFAULT_LOGGING_LEVEL);
         boolean debug = false;
         boolean verbose = false;
@@ -465,14 +339,13 @@
                     + ", verbose=" + verbose);
         }
         synchronized (mLock) {
-            setDebugLocked(debug);
-            setVerboseLocked(verbose);
+            setLoggingLevelsLocked(debug, verbose);
         }
     }
 
     // Called by Shell command.
     int getLogLevel() {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
         synchronized (mLock) {
             if (sVerbose) return AutofillManager.FLAG_ADD_CLIENT_VERBOSE;
@@ -483,7 +356,7 @@
 
     // Called by Shell command.
     int getMaxPartitions() {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
         synchronized (mLock) {
             return sPartitionMaxCount;
@@ -492,12 +365,12 @@
 
     // Called by Shell command.
     void setMaxPartitions(int max) {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         Slog.i(TAG, "setMaxPartitions(): " + max);
 
         final long token = Binder.clearCallingIdentity();
         try {
-            Settings.Global.putInt(mContext.getContentResolver(),
+            Settings.Global.putInt(getContext().getContentResolver(),
                     Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE, max);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -505,33 +378,33 @@
     }
 
     private void setMaxPartitionsFromSettings() {
-        final int max = Settings.Global.getInt(mContext.getContentResolver(),
+        final int max = Settings.Global.getInt(getContext().getContentResolver(),
                 Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
                 AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE);
         if (sDebug) Slog.d(TAG, "setMaxPartitionsFromSettings(): " + max);
 
-        synchronized (mLock) {
+        synchronized (sLock) {
             sPartitionMaxCount = max;
         }
     }
 
     // Called by Shell command.
     int getMaxVisibleDatasets() {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
-        synchronized (mLock) {
+        synchronized (sLock) {
             return sVisibleDatasetsMaxCount;
         }
     }
 
     // Called by Shell command.
     void setMaxVisibleDatasets(int max) {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         Slog.i(TAG, "setMaxVisibleDatasets(): " + max);
 
         final long token = Binder.clearCallingIdentity();
         try {
-            Settings.Global.putInt(mContext.getContentResolver(),
+            Settings.Global.putInt(getContext().getContentResolver(),
                     Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, max);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -539,11 +412,11 @@
     }
 
     private void setMaxVisibleDatasetsFromSettings() {
-        final int max = Settings.Global.getInt(mContext.getContentResolver(),
+        final int max = Settings.Global.getInt(getContext().getContentResolver(),
                 Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, 0);
 
         if (sDebug) Slog.d(TAG, "setMaxVisibleDatasetsFromSettings(): " + max);
-        synchronized (mLock) {
+        synchronized (sLock) {
             sVisibleDatasetsMaxCount = max;
         }
     }
@@ -551,10 +424,10 @@
     // Called by Shell command.
     void getScore(@Nullable String algorithmName, @NonNull String value1,
             @NonNull String value2, @NonNull RemoteCallback callback) {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
         final FieldClassificationStrategy strategy =
-                new FieldClassificationStrategy(mContext, UserHandle.USER_CURRENT);
+                new FieldClassificationStrategy(getContext(), UserHandle.USER_CURRENT);
 
         strategy.getScores(callback, algorithmName, null,
                 Arrays.asList(AutofillValue.forText(value1)), new String[] { value2 });
@@ -562,19 +435,19 @@
 
     // Called by Shell command.
     Boolean getFullScreenMode() {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         return sFullScreenMode;
     }
 
     // Called by Shell command.
     void setFullScreenMode(@Nullable Boolean mode) {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         sFullScreenMode = mode;
     }
 
     // Called by Shell command.
     boolean getAllowInstantService() {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         synchronized (mLock) {
             return mAllowInstantService;
         }
@@ -582,59 +455,21 @@
 
     // Called by Shell command.
     void setAllowInstantService(boolean mode) {
-        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         Slog.i(TAG, "setAllowInstantService(): " + mode);
         synchronized (mLock) {
             mAllowInstantService = mode;
         }
     }
 
-    private void setDebugLocked(boolean debug) {
+    private void setLoggingLevelsLocked(boolean debug, boolean verbose) {
         com.android.server.autofill.Helper.sDebug = debug;
         android.view.autofill.Helper.sDebug = debug;
-    }
+        this.debug = debug;
 
-    private void setVerboseLocked(boolean verbose) {
         com.android.server.autofill.Helper.sVerbose = verbose;
         android.view.autofill.Helper.sVerbose = verbose;
-    }
-
-    /**
-     * Removes a cached service for a given user.
-     */
-    @GuardedBy("mLock")
-    private void removeCachedServiceLocked(int userId) {
-        final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
-        if (service != null) {
-            mServicesCache.delete(userId);
-            service.destroyLocked();
-            mAutofillCompatState.removeCompatibilityModeRequests(userId);
-        }
-    }
-
-    /**
-     * Updates a cached service for a given user.
-     */
-    @GuardedBy("mLock")
-    private void updateCachedServiceLocked(int userId) {
-        updateCachedServiceLocked(userId, mDisabledUsers.get(userId));
-    }
-
-    /**
-     * Updates a cached service for a given user.
-     */
-    @GuardedBy("mLock")
-    private void updateCachedServiceLocked(int userId, boolean disabled) {
-        AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
-        if (service != null) {
-            service.destroySessionsLocked();
-            service.updateLocked(disabled);
-            if (!service.isEnabledLocked()) {
-                removeCachedServiceLocked(userId);
-            } else {
-                addCompatibilityModeRequestsLocked(service, userId);
-            }
-        }
+        this.verbose = verbose;
     }
 
     private void addCompatibilityModeRequestsLocked(@NonNull AutofillManagerServiceImpl service
@@ -664,7 +499,7 @@
 
     private String getWhitelistedCompatModePackagesFromSettings() {
         return Settings.Global.getString(
-                mContext.getContentResolver(),
+                getContext().getContentResolver(),
                 Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES);
     }
 
@@ -758,6 +593,24 @@
         return compatPackages;
     }
 
+    /**
+     * Gets the maximum number of partitions / fill requests.
+     */
+    public static int getPartitionMaxCount() {
+        synchronized (sLock) {
+            return sPartitionMaxCount;
+        }
+    }
+
+    /**
+     * Gets the maxium number of datasets visible in the UI.
+     */
+    public static int getVisibleDatasetsMaxCount() {
+        synchronized (sLock) {
+            return sVisibleDatasetsMaxCount;
+        }
+    }
+
     private final class LocalService extends AutofillManagerInternal {
         @Override
         public void onBackKeyPressed() {
@@ -889,20 +742,24 @@
         }
 
         private void dump(String prefix, PrintWriter pw) {
-             if (mUserSpecs == null) {
-                 pw.println("N/A");
-                 return;
-             }
-             pw.println();
-             final String prefix2 = prefix + "  ";
-             for (int i = 0; i < mUserSpecs.size(); i++) {
-                 final int user = mUserSpecs.keyAt(i);
-                 pw.print(prefix); pw.print("User: "); pw.println(user);
-                 final ArrayMap<String, PackageCompatState> perUser = mUserSpecs.valueAt(i);
-                 for (int j = 0; j < perUser.size(); j++) {
-                     final String packageName = perUser.keyAt(j);
-                     final PackageCompatState state = perUser.valueAt(j);
-                     pw.print(prefix2); pw.print(packageName); pw.print(": "); pw.println(state);
+            synchronized (mLock) {
+                if (mUserSpecs == null) {
+                    pw.println("N/A");
+                    return;
+                }
+                pw.println();
+                final String prefix2 = prefix + "  ";
+                for (int i = 0; i < mUserSpecs.size(); i++) {
+                    final int user = mUserSpecs.keyAt(i);
+                    pw.print(prefix);
+                    pw.print("User: ");
+                    pw.println(user);
+                    final ArrayMap<String, PackageCompatState> perUser = mUserSpecs.valueAt(i);
+                    for (int j = 0; j < perUser.size(); j++) {
+                        final String packageName = perUser.keyAt(j);
+                        final PackageCompatState state = perUser.valueAt(j);
+                        pw.print(prefix2); pw.print(packageName); pw.print(": "); pw.println(state);
+                    }
                 }
             }
         }
@@ -971,7 +828,7 @@
             Preconditions.checkArgument(userId == UserHandle.getUserId(getCallingUid()), "userId");
 
             try {
-                mContext.getPackageManager().getPackageInfoAsUser(packageName, 0, userId);
+                getContext().getPackageManager().getPackageInfoAsUser(packageName, 0, userId);
             } catch (PackageManager.NameNotFoundException e) {
                 throw new IllegalArgumentException(packageName + " is not a valid package", e);
             }
@@ -1139,7 +996,7 @@
 
             boolean restored = false;
             synchronized (mLock) {
-                final AutofillManagerServiceImpl service = mServicesCache.get(userId);
+                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
                 if (service != null) {
                     restored = service.restoreSession(sessionId, getCallingUid(), activityToken,
                             appCallback);
@@ -1216,7 +1073,7 @@
         public void isServiceSupported(int userId, @NonNull IResultReceiver receiver) {
             boolean supported = false;
             synchronized (mLock) {
-                supported = !mDisabledUsers.get(userId);
+                supported = !isDisabledLocked(userId);
             }
             send(receiver, supported);
         }
@@ -1253,7 +1110,7 @@
 
         @Override
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+            if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
 
             boolean showHistory = true;
             boolean uiOnly = false;
@@ -1280,102 +1137,48 @@
                 return;
             }
 
-            boolean oldDebug = sDebug;
             final String prefix = "  ";
-            final String prefix2 = "    ";
+            boolean realDebug = sDebug;
+            boolean realVerbose = sVerbose;
             try {
+                sDebug = sVerbose = true;
                 synchronized (mLock) {
-                    oldDebug = sDebug;
-                    setDebugLocked(true);
-                    pw.print("Debug mode: "); pw.println(oldDebug);
-                    pw.print("Verbose mode: "); pw.println(sVerbose);
-                    pw.print("Disabled users: "); pw.println(mDisabledUsers);
+                    pw.print("sDebug: "); pw.print(realDebug);
+                    pw.print(" sVerbose: "); pw.println(realVerbose);
+                    // Dump per-user services
+                    dumpLocked("", pw);
                     pw.print("Max partitions per session: "); pw.println(sPartitionMaxCount);
                     pw.print("Max visible datasets: "); pw.println(sVisibleDatasetsMaxCount);
                     if (sFullScreenMode != null) {
                         pw.print("Overridden full-screen mode: "); pw.println(sFullScreenMode);
                     }
                     pw.println("User data constraints: "); UserData.dumpConstraints(prefix, pw);
-                    final int size = mServicesCache.size();
-                    pw.print("Cached services: ");
-                    if (size == 0) {
-                        pw.println("none");
-                    } else {
-                        pw.println(size);
-                        for (int i = 0; i < size; i++) {
-                            pw.print("\nService at index "); pw.println(i);
-                            final AutofillManagerServiceImpl impl = mServicesCache.valueAt(i);
-                            impl.dumpLocked(prefix, pw);
-                        }
-                    }
                     mUi.dump(pw);
                     pw.print("Autofill Compat State: ");
-                    mAutofillCompatState.dump(prefix2, pw);
-                    pw.print(prefix2); pw.print("from settings: ");
+                    mAutofillCompatState.dump(prefix, pw);
+                    pw.print("from settings: ");
                     pw.println(getWhitelistedCompatModePackagesFromSettings());
                     pw.print("Allow instant service: "); pw.println(mAllowInstantService);
-                }
-                if (showHistory) {
-                    pw.println(); pw.println("Requests history:"); pw.println();
-                    mRequestsHistory.reverseDump(fd, pw, args);
-                    pw.println(); pw.println("UI latency history:"); pw.println();
-                    mUiLatencyHistory.reverseDump(fd, pw, args);
-                    pw.println(); pw.println("WTF history:"); pw.println();
-                    mWtfHistory.reverseDump(fd, pw, args);
+                    if (showHistory) {
+                        pw.println(); pw.println("Requests history:"); pw.println();
+                        mRequestsHistory.reverseDump(fd, pw, args);
+                        pw.println(); pw.println("UI latency history:"); pw.println();
+                        mUiLatencyHistory.reverseDump(fd, pw, args);
+                        pw.println(); pw.println("WTF history:"); pw.println();
+                        mWtfHistory.reverseDump(fd, pw, args);
+                    }
                 }
             } finally {
-                setDebugLocked(oldDebug);
+                sDebug = realDebug;
+                sVerbose = realVerbose;
             }
         }
 
         @Override
         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
                 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
-            (new AutofillManagerServiceShellCommand(AutofillManagerService.this)).exec(
+            new AutofillManagerServiceShellCommand(AutofillManagerService.this).exec(
                     this, in, out, err, args, callback, resultReceiver);
         }
     }
-
-    private final class SettingsObserver extends ContentObserver {
-        SettingsObserver(Handler handler) {
-            super(handler);
-            ContentResolver resolver = mContext.getContentResolver();
-            resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.AUTOFILL_SERVICE), false, this, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.USER_SETUP_COMPLETE), false, this, UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, this,
-                    UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.AUTOFILL_LOGGING_LEVEL), false, this,
-                    UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE), false, this,
-                    UserHandle.USER_ALL);
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS), false, this,
-                    UserHandle.USER_ALL);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri, int userId) {
-            if (sVerbose) Slog.v(TAG, "onChange(): uri=" + uri + ", userId=" + userId);
-            switch (uri.getLastPathSegment()) {
-                case Settings.Global.AUTOFILL_LOGGING_LEVEL:
-                    setLogLevelFromSettings();
-                    break;
-                case Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE:
-                    setMaxPartitionsFromSettings();
-                    break;
-                case Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS:
-                    setMaxVisibleDatasetsFromSettings();
-                    break;
-                default:
-                synchronized (mLock) {
-                    updateCachedServiceLocked(userId);
-                }
-            }
-        }
-    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 14d68cb..4810355 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -25,19 +25,14 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityTaskManager;
 import android.app.ActivityManagerInternal;
-import android.app.AppGlobals;
+import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
 import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ServiceInfo;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
 import android.metrics.LogMaker;
 import android.os.AsyncTask;
 import android.os.Binder;
@@ -49,7 +44,6 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.Settings;
 import android.service.autofill.AutofillService;
 import android.service.autofill.AutofillServiceInfo;
@@ -60,7 +54,6 @@
 import android.service.autofill.FillResponse;
 import android.service.autofill.IAutoFillService;
 import android.service.autofill.UserData;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.DebugUtils;
@@ -77,6 +70,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.server.AbstractPerUserSystemService;
 import com.android.server.LocalServices;
 import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
 import com.android.server.autofill.ui.AutoFillUI;
@@ -91,7 +85,8 @@
  * app's {@link IAutoFillService} implementation.
  *
  */
-final class AutofillManagerServiceImpl {
+final class AutofillManagerServiceImpl
+        extends AbstractPerUserSystemService<AutofillManagerServiceImpl> {
 
     private static final String TAG = "AutofillManagerServiceImpl";
     private static final int MAX_SESSION_ID_CREATE_TRIES = 2048;
@@ -99,9 +94,6 @@
     /** Minimum interval to prune abandoned sessions */
     private static final int MAX_ABANDONED_SESSION_MILLIS = 30000;
 
-    private final int mUserId;
-    private final Context mContext;
-    private final Object mLock;
     private final AutoFillUI mUi;
     private final MetricsLogger mMetricsLogger = new MetricsLogger();
 
@@ -132,23 +124,11 @@
     private ArrayMap<ComponentName, Long> mDisabledActivities;
 
     /**
-     * Whether service was disabled for user due to {@link UserManager} restrictions.
-     */
-    @GuardedBy("mLock")
-    private boolean mDisabled;
-
-    /**
      * Data used for field classification.
      */
     @GuardedBy("mLock")
     private UserData mUserData;
 
-    /**
-     * Caches whether the setup completed for the current user.
-     */
-    @GuardedBy("mLock")
-    private boolean mSetupComplete;
-
     private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
 
     /**
@@ -170,116 +150,27 @@
     /** When was {@link PruneTask} last executed? */
     private long mLastPrune = 0;
 
-    AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
+    AutofillManagerServiceImpl(AutofillManagerService master, Object lock, LocalLog requestsHistory,
             LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
             AutofillCompatState autofillCompatState, boolean disabled) {
-        mContext = context;
-        mLock = lock;
+        super(master, lock, userId);
+
         mRequestsHistory = requestsHistory;
         mUiLatencyHistory = uiLatencyHistory;
         mWtfHistory = wtfHistory;
-        mUserId = userId;
         mUi = ui;
-        mFieldClassificationStrategy = new FieldClassificationStrategy(context, userId);
+        mFieldClassificationStrategy = new FieldClassificationStrategy(getContext(), userId);
         mAutofillCompatState = autofillCompatState;
         updateLocked(disabled);
     }
 
     @GuardedBy("mLock")
-    private int getServiceUidLocked() {
-        if (mInfo == null) {
-            Slog.w(TAG,  "getServiceUidLocked(): no mInfo");
-            return -1;
-        }
-        return mInfo.getServiceInfo().applicationInfo.uid;
-    }
-
-
-    @Nullable
-    String[] getUrlBarResourceIdsForCompatMode(@NonNull String packageName) {
-        return mAutofillCompatState.getUrlBarResourceIds(packageName, mUserId);
-    }
-
-    @Nullable
-    String getServicePackageName() {
-        final ComponentName serviceComponent = getServiceComponentName();
-        if (serviceComponent != null) {
-            return serviceComponent.getPackageName();
-        }
-        return null;
-    }
-
-    @Nullable
-    ComponentName getServiceComponentName() {
-        synchronized (mLock) {
-            if (mInfo == null) {
-                return null;
-            }
-            return mInfo.getServiceInfo().getComponentName();
-        }
-    }
-
-    int getTargedSdkLocked() {
-        if (mInfo == null) {
-            return 0;
-        }
-        return mInfo.getServiceInfo().applicationInfo.targetSdkVersion;
-    }
-
-    private boolean isSetupCompletedLocked() {
-        final String setupComplete = Settings.Secure.getStringForUser(
-                mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, mUserId);
-        return "1".equals(setupComplete);
-    }
-
-    private String getComponentNameFromSettings() {
-        return Settings.Secure.getStringForUser(
-                mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId);
-    }
-
-    @GuardedBy("mLock")
-    void updateLocked(boolean disabled) {
-        final boolean wasEnabled = isEnabledLocked();
-        if (sVerbose) {
-            Slog.v(TAG, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled
-                    + ", mSetupComplete= " + mSetupComplete
-                    + ", disabled=" + disabled + ", mDisabled=" + mDisabled);
-        }
-        mSetupComplete = isSetupCompletedLocked();
-        mDisabled = disabled;
-        ComponentName serviceComponent = null;
-        ServiceInfo serviceInfo = null;
-        final String componentName = getComponentNameFromSettings();
-        if (!TextUtils.isEmpty(componentName)) {
-            try {
-                serviceComponent = ComponentName.unflattenFromString(componentName);
-                serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
-                        0, mUserId);
-                if (serviceInfo == null) {
-                    Slog.e(TAG, "Bad AutofillService name: " + componentName);
-                }
-            } catch (RuntimeException | RemoteException e) {
-                Slog.e(TAG, "Error getting service info for '" + componentName + "': " + e);
-                serviceInfo = null;
-            }
-        }
-        try {
-            if (serviceInfo != null) {
-                mInfo = new AutofillServiceInfo(mContext, serviceComponent, mUserId);
-                if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo);
-            } else {
-                mInfo = null;
-                if (sDebug) {
-                    Slog.d(TAG, "Reset component for user " + mUserId + " (" + componentName + ")");
-                }
-            }
-        } catch (Exception e) {
-            Slog.e(TAG, "Bad AutofillServiceInfo for '" + componentName + "': " + e);
-            mInfo = null;
-        }
-        final boolean isEnabled = isEnabledLocked();
-        if (wasEnabled != isEnabled) {
-            if (!isEnabled) {
+    @Override // from PerUserSystemService
+    protected boolean updateLocked(boolean disabled) {
+        destroySessionsLocked();
+        final boolean enabledChanged = super.updateLocked(disabled);
+        if (enabledChanged) {
+            if (!isEnabledLocked()) {
                 final int sessionCount = mSessions.size();
                 for (int i = sessionCount - 1; i >= 0; i--) {
                     final Session session = mSessions.valueAt(i);
@@ -288,6 +179,19 @@
             }
             sendStateToClients(false);
         }
+        return enabledChanged;
+    }
+
+    @Override // from PerUserSystemService
+    protected ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
+            throws NameNotFoundException {
+        mInfo = new AutofillServiceInfo(getContext(), serviceComponent, mUserId);
+        return mInfo.getServiceInfo();
+    }
+
+    @Nullable
+    String[] getUrlBarResourceIdsForCompatMode(@NonNull String packageName) {
+        return mAutofillCompatState.getUrlBarResourceIds(packageName, mUserId);
     }
 
     @GuardedBy("mLock")
@@ -469,7 +373,7 @@
             if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) {
                 mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF,
                         componentName.getPackageName());
-                Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                Settings.Secure.putStringForUser(getContext().getContentResolver(),
                         Settings.Secure.AUTOFILL_SERVICE, null, mUserId);
                 destroySessionsLocked();
             } else {
@@ -501,7 +405,7 @@
 
         assertCallerLocked(componentName, compatMode);
 
-        final Session newSession = new Session(this, mUi, mContext, mHandler, mUserId, mLock,
+        final Session newSession = new Session(this, mUi, getContext(), mHandler, mUserId, mLock,
                 sessionId, taskId, uid, activityToken, appCallbackToken, hasCallback,
                 mUiLatencyHistory, mWtfHistory, mInfo.getServiceInfo().getComponentName(),
                 componentName, compatMode, bindInstantServiceAllowed, flags);
@@ -515,7 +419,7 @@
      */
     private void assertCallerLocked(@NonNull ComponentName componentName, boolean compatMode) {
         final String packageName = componentName.getPackageName();
-        final PackageManager pm = mContext.getPackageManager();
+        final PackageManager pm = getContext().getPackageManager();
         final int callingUid = Binder.getCallingUid();
         final int packageUid;
         try {
@@ -651,7 +555,8 @@
     }
 
     @GuardedBy("mLock")
-    void handlePackageUpdateLocked(String packageName) {
+    @Override // from PerUserSystemService
+    protected void handlePackageUpdateLocked(@NonNull String packageName) {
         final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo();
         if (serviceInfo != null && serviceInfo.packageName.equals(packageName)) {
             resetExtServiceLocked();
@@ -691,29 +596,6 @@
     }
 
     /**
-     * Gets the user-visibile name of the service this service binds to, or {@code null} if the
-     * service is disabled.
-     */
-    @Nullable
-    @GuardedBy("mLock")
-    public CharSequence getServiceLabelLocked() {
-        return mInfo == null ? null : mInfo.getServiceInfo().loadSafeLabel(
-                mContext.getPackageManager(), 0 /* do not ellipsize */,
-                PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
-    }
-
-    /**
-     * Gets the icon of the service this service binds to, or {@code null} if the service is
-     * disabled.
-     */
-    @NonNull
-    @Nullable
-    @GuardedBy("mLock")
-    Drawable getServiceIconLocked() {
-        return mInfo == null ? null : mInfo.getServiceInfo().loadIcon(mContext.getPackageManager());
-    }
-
-    /**
      * Initializes the last fill selection after an autofill service returned a new
      * {@link FillResponse}.
      */
@@ -941,11 +823,13 @@
         return true;
     }
 
+    @Override
     @GuardedBy("mLock")
-    void dumpLocked(String prefix, PrintWriter pw) {
+    protected void dumpLocked(String prefix, PrintWriter pw) {
+        super.dumpLocked(prefix, pw);
+
         final String prefix2 = prefix + "  ";
 
-        pw.print(prefix); pw.print("User: "); pw.println(mUserId);
         pw.print(prefix); pw.print("UID: "); pw.println(getServiceUidLocked());
         pw.print(prefix); pw.print("Autofill Service Info: ");
         if (mInfo == null) {
@@ -958,9 +842,8 @@
         }
         pw.print(prefix); pw.print("Component from settings: ");
             pw.println(getComponentNameFromSettings());
-        pw.print(prefix); pw.print("Default component: ");
-            pw.println(mContext.getString(R.string.config_defaultAutofillService));
-        pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+        pw.print(prefix); pw.print("Default component: "); pw.println(getContext()
+                .getString(R.string.config_defaultAutofillService));
         pw.print(prefix); pw.print("Field classification enabled: ");
             pw.println(isFieldClassificationEnabledLocked());
         pw.print(prefix); pw.print("Compat pkgs: ");
@@ -970,7 +853,6 @@
         } else {
             pw.println(compatPkgs);
         }
-        pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
         pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
 
         pw.print(prefix); pw.print("Disabled apps: ");
@@ -1158,11 +1040,6 @@
         return true;
     }
 
-    @GuardedBy("mLock")
-    boolean isEnabledLocked() {
-        return mSetupComplete && mInfo != null && !mDisabled;
-    }
-
     /**
      * Called by {@link Session} when service asked to disable autofill for an app.
      */
@@ -1270,7 +1147,7 @@
     // Called by internally, no need to check UID.
     boolean isFieldClassificationEnabledLocked() {
         return Settings.Secure.getIntForUser(
-                mContext.getContentResolver(),
+                getContext().getContentResolver(),
                 Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 1,
                 mUserId) == 1;
     }
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 420c2be..3c0da7d 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -28,20 +28,21 @@
 import android.util.Slog;
 import android.view.WindowManager;
 import android.view.autofill.AutofillId;
-import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.ArrayUtils;
 
 import java.io.PrintWriter;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.LinkedList;
 
 public final class Helper {
 
     private static final String TAG = "AutofillHelper";
 
+    // TODO(b/117779333): get rid of sDebug / sVerbose and always use the service variables instead
+
     /**
      * Defines a logging flag that can be dynamically changed at runtime using
      * {@code cmd autofill set log_level debug} or through
@@ -57,23 +58,6 @@
     public static boolean sVerbose = false;
 
     /**
-     * Maximum number of partitions that can be allowed in a session.
-     *
-     * <p>Can be modified using {@code cmd autofill set max_partitions} or through
-     * {@link android.provider.Settings.Global#AUTOFILL_MAX_PARTITIONS_SIZE}.
-     */
-    static int sPartitionMaxCount = AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE;
-
-    /**
-     * Maximum number of visible datasets in the dataset picker UI, or {@code 0} to use default
-     * value from resources.
-     *
-     * <p>Can be modified using {@code cmd autofill set max_visible_datasets} or through
-     * {@link android.provider.Settings.Global#AUTOFILL_MAX_VISIBLE_DATASETS}.
-     */
-    public static int sVisibleDatasetsMaxCount = 0;
-
-    /**
      * When non-null, overrides whether the UI should be shown on full-screen mode.
      *
      * <p>Note: access to this variable is not synchronized because it's "final" on real usage -
@@ -162,7 +146,7 @@
 
     private static ViewNode findViewNode(@NonNull AssistStructure structure,
             @NonNull ViewNodeFilter filter) {
-        final LinkedList<ViewNode> nodesToProcess = new LinkedList<>();
+        final ArrayDeque<ViewNode> nodesToProcess = new ArrayDeque<>();
         final int numWindowNodes = structure.getWindowNodeCount();
         for (int i = 0; i < numWindowNodes; i++) {
             nodesToProcess.add(structure.getWindowNodeAt(i).getRootViewNode());
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index d1b09ca..9aa9d7c 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -18,7 +18,6 @@
 
 import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
 
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sVerbose;
 
@@ -26,17 +25,11 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.content.IntentSender;
-import android.content.ServiceConnection;
-import android.os.Build;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
 import android.os.ICancellationSignal;
+import android.os.IInterface;
 import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
 import android.service.autofill.AutofillService;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
@@ -47,59 +40,17 @@
 import android.text.format.DateUtils;
 import android.util.Slog;
 
-import com.android.internal.annotations.GuardedBy;
-import com.android.server.FgThread;
+import com.android.server.AbstractRemoteService;
 
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
+final class RemoteFillService extends AbstractRemoteService {
 
-/**
- * This class represents a remote fill service. It abstracts away the binding
- * and unbinding from the remote implementation.
- *
- * <p>Clients can call methods of this class without worrying about when and
- * how to bind/unbind/timeout. All state of this class is modified on a handler
- * thread.
- */
-final class RemoteFillService implements DeathRecipient {
-    private static final String LOG_TAG = "RemoteFillService";
-    // How long after the last interaction with the service we would unbind
     private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
-
-    // How long after we make a remote request to a fill service we timeout
     private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
 
-    private static final int MSG_UNBIND = 3;
-
-    private final Context mContext;
-
-    private final ComponentName mComponentName;
-
-    private final Intent mIntent;
-
     private final FillServiceCallbacks mCallbacks;
-
-    private final int mUserId;
-
-    private final ServiceConnection mServiceConnection = new RemoteServiceConnection();
-
-    private final Handler mHandler;
-
-    private final boolean mBindInstantServiceAllowed;
-
     private IAutoFillService mAutoFillService;
 
-    private boolean mBinding;
-
-    private boolean mDestroyed;
-
-    private boolean mServiceDied;
-
-    private boolean mCompleted;
-
-    private PendingRequest mPendingRequest;
-
-    public interface FillServiceCallbacks {
+    public interface FillServiceCallbacks extends VultureCallback {
         void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
                 @NonNull String servicePackageName, int requestFlags);
         void onFillRequestFailure(int requestId, @Nullable CharSequence message);
@@ -109,48 +60,42 @@
         // TODO(b/80093094): add timeout here too?
         void onSaveRequestFailure(@Nullable CharSequence message,
                 @NonNull String servicePackageName);
-        void onServiceDied(RemoteFillService service);
     }
 
-    public RemoteFillService(Context context, ComponentName componentName,
-            int userId, FillServiceCallbacks callbacks, boolean bindInstantServiceAllowed) {
-        mContext = context;
+    RemoteFillService(Context context, ComponentName componentName, int userId,
+            FillServiceCallbacks callbacks, boolean bindInstantServiceAllowed) {
+        super(context, AutofillService.SERVICE_INTERFACE, componentName, userId, callbacks,
+                bindInstantServiceAllowed, sVerbose);
         mCallbacks = callbacks;
-        mComponentName = componentName;
-        mIntent = new Intent(AutofillService.SERVICE_INTERFACE).setComponent(mComponentName);
-        mUserId = userId;
-        mHandler = new Handler(FgThread.getHandler().getLooper());
-        mBindInstantServiceAllowed = bindInstantServiceAllowed;
-    }
-
-    public void destroy() {
-        mHandler.sendMessage(obtainMessage(RemoteFillService::handleDestroy, this));
-    }
-
-    private void handleDestroy() {
-        if (checkIfDestroyed()) return;
-        if (mPendingRequest != null) {
-            mPendingRequest.cancel();
-            mPendingRequest = null;
-        }
-        ensureUnbound();
-        mDestroyed = true;
     }
 
     @Override
-    public void binderDied() {
-        mHandler.sendMessage(obtainMessage(
-                RemoteFillService::handleBinderDied, this));
+    protected void onConnectedStateChanged(boolean state) {
+        if (mAutoFillService == null) {
+            Slog.w(mTag, "onConnectedStateChanged(): null service");
+            return;
+        }
+        try {
+            mAutoFillService.onConnectedStateChanged(state);
+        } catch (Exception e) {
+            Slog.w(mTag, "Exception calling onConnectedStateChanged(): " + e);
+        }
     }
 
-    private void handleBinderDied() {
-        if (checkIfDestroyed()) return;
-        if (mAutoFillService != null) {
-            mAutoFillService.asBinder().unlinkToDeath(this, 0);
-        }
-        mAutoFillService = null;
-        mServiceDied = true;
-        mCallbacks.onServiceDied(this);
+    @Override
+    protected IInterface getServiceInterface(IBinder service) {
+        mAutoFillService = IAutoFillService.Stub.asInterface(service);
+        return mAutoFillService;
+    }
+
+    @Override
+    protected long getTimeoutIdleBindMillis() {
+        return TIMEOUT_IDLE_BIND_MILLIS;
+    }
+
+    @Override
+    protected long getRemoteRequestMillis() {
+        return TIMEOUT_REMOTE_REQUEST_MILLIS;
     }
 
     /**
@@ -162,8 +107,9 @@
      * @return the id of the canceled request, or {@link FillRequest#INVALID_REQUEST_ID} if no
      *         {@link PendingFillRequest} was canceled.
      */
+    // TODO(b/117779333): move this logic to super class (and make mPendingRequest private)
     public int cancelCurrentRequest() {
-        if (mDestroyed) {
+        if (isDestroyed()) {
             return INVALID_REQUEST_ID;
         }
 
@@ -190,118 +136,6 @@
         scheduleRequest(new PendingSaveRequest(request, this));
     }
 
-    private void scheduleRequest(PendingRequest pendingRequest) {
-        mHandler.sendMessage(obtainMessage(
-                RemoteFillService::handlePendingRequest, this, pendingRequest));
-    }
-
-    // Note: we are dumping without a lock held so this is a bit racy but
-    // adding a lock to a class that offloads to a handler thread would
-    // mean adding a lock adding overhead to normal runtime operation.
-    public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
-        String tab = "  ";
-        pw.append(prefix).append("service:").println();
-        pw.append(prefix).append(tab).append("userId=")
-                .append(String.valueOf(mUserId)).println();
-        pw.append(prefix).append(tab).append("componentName=")
-                .append(mComponentName.flattenToString()).println();
-        pw.append(prefix).append(tab).append("destroyed=")
-                .append(String.valueOf(mDestroyed)).println();
-        pw.append(prefix).append(tab).append("bound=")
-                .append(String.valueOf(isBound())).println();
-        pw.append(prefix).append(tab).append("hasPendingRequest=")
-                .append(String.valueOf(mPendingRequest != null)).println();
-        pw.append(prefix).append("mBindInstantServiceAllowed=").println(mBindInstantServiceAllowed);
-        pw.println();
-    }
-
-    private void cancelScheduledUnbind() {
-        mHandler.removeMessages(MSG_UNBIND);
-    }
-
-    private void scheduleUnbind() {
-        cancelScheduledUnbind();
-        mHandler.sendMessageDelayed(
-                obtainMessage(RemoteFillService::handleUnbind, this)
-                        .setWhat(MSG_UNBIND),
-                TIMEOUT_IDLE_BIND_MILLIS);
-    }
-
-    private void handleUnbind() {
-        if (checkIfDestroyed()) return;
-        ensureUnbound();
-    }
-
-    private void handlePendingRequest(PendingRequest pendingRequest) {
-        if (checkIfDestroyed()) return;
-        if (mCompleted) {
-            return;
-        }
-        if (!isBound()) {
-            if (mPendingRequest != null) {
-                mPendingRequest.cancel();
-            }
-            mPendingRequest = pendingRequest;
-            ensureBound();
-        } else {
-            if (sVerbose) Slog.v(LOG_TAG, "[user: " + mUserId + "] handlePendingRequest()");
-            pendingRequest.run();
-            if (pendingRequest.isFinal()) {
-                mCompleted = true;
-            }
-        }
-    }
-
-    private boolean isBound() {
-        return mAutoFillService != null;
-    }
-
-    private void ensureBound() {
-        if (isBound() || mBinding) {
-            return;
-        }
-        if (sVerbose) Slog.v(LOG_TAG, "[user: " + mUserId + "] ensureBound()");
-        mBinding = true;
-
-        int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
-        if (mBindInstantServiceAllowed) {
-            flags |= Context.BIND_ALLOW_INSTANT;
-        }
-
-        final boolean willBind = mContext.bindServiceAsUser(mIntent, mServiceConnection, flags,
-                new UserHandle(mUserId));
-
-        if (!willBind) {
-            Slog.w(LOG_TAG, "[user: " + mUserId + "] could not bind to " + mIntent + " using flags "
-                    + flags);
-            mBinding = false;
-
-            if (!mServiceDied) {
-                handleBinderDied();
-            }
-        }
-    }
-
-    private void ensureUnbound() {
-        if (!isBound() && !mBinding) {
-            return;
-        }
-        if (sVerbose) Slog.v(LOG_TAG, "[user: " + mUserId + "] ensureUnbound()");
-        mBinding = false;
-        if (isBound()) {
-            try {
-                mAutoFillService.onConnectedStateChanged(false);
-            } catch (Exception e) {
-                Slog.w(LOG_TAG, "Exception calling onDisconnected(): " + e);
-            }
-            if (mAutoFillService != null) {
-                mAutoFillService.asBinder().unlinkToDeath(this, 0);
-                mAutoFillService = null;
-            }
-        }
-        mContext.unbindService(mServiceConnection);
-    }
-
     private void dispatchOnFillRequestSuccess(@NonNull PendingFillRequest pendingRequest,
             @Nullable FillResponse response, int requestFlags) {
         mHandler.post(() -> {
@@ -334,12 +168,12 @@
             try {
                 cancellationSignal.cancel();
             } catch (RemoteException e) {
-                Slog.w(LOG_TAG, "Error calling cancellation signal: " + e);
+                Slog.w(mTag, "Error calling cancellation signal: " + e);
             }
         });
     }
 
-    private void dispatchOnSaveRequestSuccess(PendingRequest pendingRequest,
+    private void dispatchOnSaveRequestSuccess(PendingSaveRequest pendingRequest,
             IntentSender intentSender) {
         mHandler.post(() -> {
             if (handleResponseCallbackCommon(pendingRequest)) {
@@ -348,7 +182,7 @@
         });
     }
 
-    private void dispatchOnSaveRequestFailure(PendingRequest pendingRequest,
+    private void dispatchOnSaveRequestFailure(PendingSaveRequest pendingRequest,
             @Nullable CharSequence message) {
         mHandler.post(() -> {
             if (handleResponseCallbackCommon(pendingRequest)) {
@@ -357,162 +191,7 @@
         });
     }
 
-    private boolean handleResponseCallbackCommon(PendingRequest pendingRequest) {
-        if (mDestroyed) {
-            return false;
-        }
-        if (mPendingRequest == pendingRequest) {
-            mPendingRequest = null;
-        }
-        if (mPendingRequest == null) {
-            scheduleUnbind();
-        }
-        return true;
-    }
-
-    private class RemoteServiceConnection implements ServiceConnection {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            if (mDestroyed || !mBinding) {
-                // This is abnormal. Unbinding the connection has been requested already.
-                Slog.wtf(LOG_TAG, "onServiceConnected was dispatched after unbindService.");
-                return;
-            }
-            mBinding = false;
-            mAutoFillService = IAutoFillService.Stub.asInterface(service);
-            try {
-                service.linkToDeath(RemoteFillService.this, 0);
-            } catch (RemoteException re) {
-                handleBinderDied();
-                return;
-            }
-            try {
-                mAutoFillService.onConnectedStateChanged(true);
-            } catch (RemoteException e) {
-                Slog.w(LOG_TAG, "Exception calling onConnected(): " + e);
-            }
-
-            if (mPendingRequest != null) {
-                PendingRequest pendingRequest = mPendingRequest;
-                mPendingRequest = null;
-                handlePendingRequest(pendingRequest);
-            }
-
-            mServiceDied = false;
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            mBinding = true;
-            mAutoFillService = null;
-        }
-    }
-
-    private boolean checkIfDestroyed() {
-        if (mDestroyed) {
-            if (sVerbose) {
-                Slog.v(LOG_TAG, "Not handling operation as service for "
-                        + mComponentName + " is already destroyed");
-            }
-        }
-        return mDestroyed;
-    }
-
-    private static abstract class PendingRequest implements Runnable {
-        protected final Object mLock = new Object();
-        private final WeakReference<RemoteFillService> mWeakService;
-
-        private final Runnable mTimeoutTrigger;
-        private final Handler mServiceHandler;
-
-        @GuardedBy("mLock")
-        private boolean mCancelled;
-
-        @GuardedBy("mLock")
-        private boolean mCompleted;
-
-        PendingRequest(RemoteFillService service) {
-            mWeakService = new WeakReference<>(service);
-            mServiceHandler = service.mHandler;
-            mTimeoutTrigger = () -> {
-                synchronized (mLock) {
-                    if (mCancelled) {
-                        return;
-                    }
-                    mCompleted = true;
-                }
-
-                Slog.w(LOG_TAG, getClass().getSimpleName() + " timed out");
-                final RemoteFillService remoteService = mWeakService.get();
-                if (remoteService != null) {
-                    Slog.w(LOG_TAG, getClass().getSimpleName() + " timed out after "
-                            + TIMEOUT_REMOTE_REQUEST_MILLIS + " ms");
-                    onTimeout(remoteService);
-                }
-            };
-            mServiceHandler.postAtTime(mTimeoutTrigger,
-                    SystemClock.uptimeMillis() + TIMEOUT_REMOTE_REQUEST_MILLIS);
-        }
-
-        protected RemoteFillService getService() {
-            return mWeakService.get();
-        }
-
-        /**
-         * Sub-classes must call this method when the remote service finishes, i.e., when it
-         * called {@code onFill...} or {@code onSave...}.
-         *
-         * @return {@code false} in the service is already finished, {@code true} otherwise.
-         */
-        protected final boolean finish() {
-            synchronized (mLock) {
-                if (mCompleted || mCancelled) {
-                    return false;
-                }
-                mCompleted = true;
-            }
-            mServiceHandler.removeCallbacks(mTimeoutTrigger);
-            return true;
-        }
-
-        @GuardedBy("mLock")
-        protected boolean isCancelledLocked() {
-            return mCancelled;
-        }
-
-        /**
-         * Cancels the service.
-         *
-         * @return {@code false} if service is already canceled, {@code true} otherwise.
-         */
-        boolean cancel() {
-            synchronized (mLock) {
-                if (mCancelled || mCompleted) {
-                    return false;
-                }
-                mCancelled = true;
-            }
-
-            mServiceHandler.removeCallbacks(mTimeoutTrigger);
-            return true;
-        }
-
-        /**
-         * Called by the self-destructure timeout when the AutofilllService didn't reply to the
-         * request on time.
-         */
-        abstract void onTimeout(RemoteFillService remoteService);
-
-        /**
-         * @return whether this request leads to a final state where no
-         * other requests can be made.
-         */
-        boolean isFinal() {
-            return false;
-        }
-    }
-
-    private static final class PendingFillRequest extends PendingRequest {
+    private static final class PendingFillRequest extends PendingRequest<RemoteFillService> {
         private final FillRequest mRequest;
         private final IFillCallback mCallback;
         private ICancellationSignal mCancellation;
@@ -534,7 +213,7 @@
                             try {
                                 cancellation.cancel();
                             } catch (RemoteException e) {
-                                Slog.e(LOG_TAG, "Error requesting a cancellation", e);
+                                Slog.e(mTag, "Error requesting a cancellation", e);
                             }
                         }
                     }
@@ -565,7 +244,7 @@
         }
 
         @Override
-        void onTimeout(RemoteFillService remoteService) {
+        protected void onTimeout(RemoteFillService remoteService) {
             // NOTE: Must make these 2 calls asynchronously, because the cancellation signal is
             // handled by the service, which could block.
             final ICancellationSignal cancellation;
@@ -582,17 +261,17 @@
         public void run() {
             synchronized (mLock) {
                 if (isCancelledLocked()) {
-                    if (sDebug) Slog.d(LOG_TAG, "run() called after canceled: " + mRequest);
+                    if (sDebug) Slog.d(mTag, "run() called after canceled: " + mRequest);
                     return;
                 }
             }
             final RemoteFillService remoteService = getService();
             if (remoteService != null) {
-                if (sVerbose) Slog.v(LOG_TAG, "calling onFillRequest() for id=" + mRequest.getId());
+                if (sVerbose) Slog.v(mTag, "calling onFillRequest() for id=" + mRequest.getId());
                 try {
                     remoteService.mAutoFillService.onFillRequest(mRequest, mCallback);
                 } catch (RemoteException e) {
-                    Slog.e(LOG_TAG, "Error calling on fill request", e);
+                    Slog.e(mTag, "Error calling on fill request", e);
 
                     remoteService.dispatchOnFillRequestFailure(PendingFillRequest.this, null);
                 }
@@ -611,14 +290,14 @@
                 try {
                     cancellation.cancel();
                 } catch (RemoteException e) {
-                    Slog.e(LOG_TAG, "Error cancelling a fill request", e);
+                    Slog.e(mTag, "Error cancelling a fill request", e);
                 }
             }
             return true;
         }
     }
 
-    private static final class PendingSaveRequest extends PendingRequest {
+    private static final class PendingSaveRequest extends PendingRequest<RemoteFillService> {
         private final SaveRequest mRequest;
         private final ISaveCallback mCallback;
 
@@ -653,7 +332,7 @@
         }
 
         @Override
-        void onTimeout(RemoteFillService remoteService) {
+        protected void onTimeout(RemoteFillService remoteService) {
             remoteService.dispatchOnSaveRequestFailure(PendingSaveRequest.this, null);
         }
 
@@ -661,11 +340,11 @@
         public void run() {
             final RemoteFillService remoteService = getService();
             if (remoteService != null) {
-                if (sVerbose) Slog.v(LOG_TAG, "calling onSaveRequest()");
+                if (sVerbose) Slog.v(mTag, "calling onSaveRequest()");
                 try {
                     remoteService.mAutoFillService.onSaveRequest(mRequest, mCallback);
                 } catch (RemoteException e) {
-                    Slog.e(LOG_TAG, "Error calling on save request", e);
+                    Slog.e(mTag, "Error calling on save request", e);
 
                     remoteService.dispatchOnSaveRequestFailure(PendingSaveRequest.this, null);
                 }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index f85749a..09f915e 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -27,7 +27,6 @@
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.autofill.Helper.getNumericValue;
 import static com.android.server.autofill.Helper.sDebug;
-import static com.android.server.autofill.Helper.sPartitionMaxCount;
 import static com.android.server.autofill.Helper.sVerbose;
 import static com.android.server.autofill.Helper.toArray;
 import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION;
@@ -65,7 +64,6 @@
 import android.service.autofill.Dataset;
 import android.service.autofill.FieldClassification;
 import android.service.autofill.FieldClassification.Match;
-import android.text.TextUtils;
 import android.service.autofill.FillContext;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
@@ -75,10 +73,10 @@
 import android.service.autofill.SaveRequest;
 import android.service.autofill.UserData;
 import android.service.autofill.ValueFinder;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LocalLog;
-import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -94,6 +92,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.AbstractRemoteService;
 import com.android.server.autofill.ui.AutoFillUI;
 import com.android.server.autofill.ui.PendingUi;
 
@@ -903,9 +902,9 @@
                 this, authenticationId, intent, fillInIntent));
     }
 
-    // FillServiceCallbacks
+    // VultureCallback
     @Override
-    public void onServiceDied(RemoteFillService service) {
+    public void onServiceDied(AbstractRemoteService service) {
         Slog.w(TAG, "removing session because service died");
         forceRemoveSelfLocked();
     }
@@ -2095,9 +2094,9 @@
         }
 
         final int numResponses = mResponses.size();
-        if (numResponses >= sPartitionMaxCount) {
+        if (numResponses >= AutofillManagerService.getPartitionMaxCount()) {
             Slog.e(TAG, "Not starting a new partition on " + id + " because session " + this.id
-                    + " reached maximum of " + sPartitionMaxCount);
+                    + " reached maximum of " + AutofillManagerService.getPartitionMaxCount());
             return false;
         }
 
@@ -2289,7 +2288,7 @@
                 requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags);
 
                 // Remove the UI if the ViewState has changed.
-                if (mCurrentViewId != viewState.id) {
+                if (!Objects.equals(mCurrentViewId, viewState.id)) {
                     mUi.hideFillUi(this);
                     mCurrentViewId = viewState.id;
                 }
@@ -2298,7 +2297,7 @@
                 viewState.update(value, virtualBounds, flags);
                 break;
             case ACTION_VIEW_EXITED:
-                if (mCurrentViewId == viewState.id) {
+                if (Objects.equals(mCurrentViewId, viewState.id)) {
                     if (sVerbose) Slog.d(TAG, "Exiting view " + id);
                     mUi.hideFillUi(this);
                     mCurrentViewId = null;
@@ -3077,7 +3076,9 @@
 
     private void wtf(@Nullable Exception e, String fmt, Object...args) {
         final String message = String.format(fmt, args);
-        mWtfHistory.log(message);
+        synchronized (mLock) {
+            mWtfHistory.log(message);
+        }
 
         if (e != null) {
             Slog.wtf(TAG, message, e);
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index f79f6ff..d1fe970c 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -19,25 +19,22 @@
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sFullScreenMode;
 import static com.android.server.autofill.Helper.sVerbose;
-import static com.android.server.autofill.Helper.sVisibleDatasetsMaxCount;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.PendingIntent;
 import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.view.ContextThemeWrapper;
-import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.PackageManager;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.service.autofill.Dataset;
 import android.service.autofill.Dataset.DatasetFieldFilter;
 import android.service.autofill.FillResponse;
 import android.text.TextUtils;
 import android.util.Slog;
 import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -60,6 +57,7 @@
 
 import com.android.internal.R;
 import com.android.server.UiThread;
+import com.android.server.autofill.AutofillManagerService;
 import com.android.server.autofill.Helper;
 
 import java.io.PrintWriter;
@@ -193,8 +191,8 @@
             }
         });
 
-        if (sVisibleDatasetsMaxCount > 0) {
-            mVisibleDatasetsMaxCount = sVisibleDatasetsMaxCount;
+        if (AutofillManagerService.getVisibleDatasetsMaxCount() > 0) {
+            mVisibleDatasetsMaxCount = AutofillManagerService.getVisibleDatasetsMaxCount();
             if (sVerbose) {
                 Slog.v(TAG, "overriding maximum visible datasets to " + mVisibleDatasetsMaxCount);
             }
diff --git a/services/core/java/com/android/server/AbstractMasterSystemService.java b/services/core/java/com/android/server/AbstractMasterSystemService.java
new file mode 100644
index 0000000..c955daf
--- /dev/null
+++ b/services/core/java/com/android/server/AbstractMasterSystemService.java
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.UserManagerInternal;
+import android.provider.Settings;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Base class for {@link SystemService SystemServices} that support multi user.
+ *
+ * <p>Subclasses of this service are just a facade for the service binder calls - the "real" work
+ * is done by the {@link AbstractPerUserSystemService} subclasses, which are automatically managed
+ * through an user -> service cache.
+ *
+ * <p>It also takes care of other plumbing tasks such as:
+ *
+ * <ul>
+ *   <li>Disabling the service when {@link UserManager} restrictions change.
+ *   <li>Refreshing the service when its underlying
+ *   {@link #getServiceSettingsProperty() Settings property} changed.
+ *   <li>Calling the service when other Settings properties changed.
+ * </ul>
+ *
+ * <p>See {@code com.android.server.autofill.AutofillManagerService} for a concrete
+ * (no pun intended) example of how to use it.
+ *
+ * @param <S> "real" service class.
+ *
+ * @hide
+ */
+// TODO(b/117779333): improve javadoc above instead of using Autofill as an example
+public abstract class AbstractMasterSystemService<S extends AbstractPerUserSystemService<S>>
+        extends SystemService {
+
+    /**
+     * Log tag
+     */
+    protected final String mTag = getClass().getSimpleName();
+
+    /**
+     * Lock used to synchronize access to internal state; should be acquired before calling a
+     * method whose name ends with {@code locked}.
+     */
+    protected final Object mLock = new Object();
+
+    /**
+     * Whether the service should log debug statements.
+     */
+    public boolean verbose = false;
+
+    /**
+     * Whether the service should log verbose statements.
+     */
+    public boolean debug = false;
+
+    /**
+     * Users disabled due to {@link UserManager} restrictions, or {@code null} if the service cannot
+     * be disabled through {@link UserManager}.
+     */
+    @GuardedBy("mLock")
+    @Nullable
+    private final SparseBooleanArray mDisabledUsers;
+
+    /**
+     * Cache of services per user id.
+     */
+    @GuardedBy("mLock")
+    private final SparseArray<S> mServicesCache = new SparseArray<>();
+
+    /**
+     * Default constructor.
+     *
+     * @param context system context.
+     * @param disallowProperty when not {@code null}, defines a {@link UserManager} restriction that
+     *        disables the service.
+     */
+    protected AbstractMasterSystemService(@NonNull Context context,
+            @Nullable String disallowProperty) {
+        super(context);
+
+        if (disallowProperty == null) {
+            mDisabledUsers = null;
+        } else {
+            mDisabledUsers = new SparseBooleanArray();
+            // Hookup with UserManager to disable service when necessary.
+            final UserManager um = context.getSystemService(UserManager.class);
+            final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
+            final List<UserInfo> users = um.getUsers();
+            for (int i = 0; i < users.size(); i++) {
+                final int userId = users.get(i).id;
+                final boolean disabled = umi.getUserRestriction(userId, disallowProperty);
+                if (disabled) {
+                    Slog.i(mTag, "Disabling for user " + userId);
+                    mDisabledUsers.put(userId, disabled);
+                }
+            }
+            umi.addUserRestrictionsListener((userId, newRestrictions, prevRestrictions) -> {
+                final boolean disabledNow =
+                        newRestrictions.getBoolean(disallowProperty, false);
+                synchronized (mLock) {
+                    final boolean disabledBefore = mDisabledUsers.get(userId);
+                    if (disabledBefore == disabledNow) {
+                        // Nothing changed, do nothing.
+                        if (debug) {
+                            Slog.d(mTag, "Restriction did not change for user " + userId);
+                            return;
+                        }
+                    }
+                    Slog.i(mTag, "Updating for user " + userId + ": disabled=" + disabledNow);
+                    mDisabledUsers.put(userId, disabledNow);
+                    updateCachedServiceLocked(userId, disabledNow);
+                }
+            });
+        }
+        startTrackingPackageChanges();
+    }
+
+    @Override // from SystemService
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+            new SettingsObserver(BackgroundThread.getHandler());
+        }
+    }
+
+    @Override // from SystemService
+    public void onUnlockUser(int userId) {
+        synchronized (mLock) {
+            updateCachedServiceLocked(userId);
+        }
+    }
+
+    @Override // from SystemService
+    public void onCleanupUser(int userId) {
+        synchronized (mLock) {
+            removeCachedServiceLocked(userId);
+        }
+    }
+
+    /**
+     * Creates a new service that will be added to the cache.
+     *
+     * @param resolvedUserId the resolved user id for the service.
+     * @param disabled whether the service is currently disabled (due to {@link UserManager}
+     * restrictions).
+     *
+     * @return a new instance.
+     */
+    protected abstract S newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled);
+
+    /**
+     * Register the service for extra Settings changes (i.e., other than
+     * {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
+     * {@link #getServiceSettingsProperty()}, which are automatically handled).
+     *
+     * <p> Example:
+     *
+     * <pre><code>
+     * resolver.registerContentObserver(Settings.Global.getUriFor(
+     *     Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, observer,
+     *     UserHandle.USER_ALL);
+     * </code></pre>
+     *
+     * <p><b>NOTE: </p>it doesn't need to register for
+     * {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
+     * {@link #getServiceSettingsProperty()}.
+     *
+     */
+    @SuppressWarnings("unused")
+    protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
+            @NonNull ContentObserver observer) {
+    }
+
+    /**
+     * Callback for Settings changes that were registered though
+     * {@link #registerForExtraSettingsChanges(ContentResolver, ContentObserver)}.
+     *
+     * @param userId user associated with the change
+     * @param property Settings property changed.
+     */
+    protected void onSettingsChanged(@UserIdInt int userId, @NonNull String property) {
+    }
+
+    /**
+     * Gets the service instance for an user, creating an instance if not present in the cache.
+     */
+    @GuardedBy("mLock")
+    @NonNull
+    protected S getServiceForUserLocked(@UserIdInt int userId) {
+        final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, false, null, null);
+        S service = mServicesCache.get(resolvedUserId);
+        if (service == null) {
+            final boolean disabled = isDisabledLocked(userId);
+            service = newServiceLocked(resolvedUserId, disabled);
+            if (!disabled) {
+                onServiceEnabledLocked(service, resolvedUserId);
+            }
+            mServicesCache.put(userId, service);
+        }
+        return service;
+    }
+
+    /**
+     * Gets the <b>existing</b> service instance for a user, returning {@code null} if not already
+     * present in the cache.
+     */
+    @GuardedBy("mLock")
+    @Nullable
+    protected S peekServiceForUserLocked(int userId) {
+        final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, false, null, null);
+        return mServicesCache.get(resolvedUserId);
+    }
+
+    /**
+     * Updates a cached service for a given user.
+     */
+    @GuardedBy("mLock")
+    protected void updateCachedServiceLocked(int userId) {
+        updateCachedServiceLocked(userId, isDisabledLocked(userId));
+    }
+
+    /**
+     * Checks whether the service is disabled (through {@link UserManager} restrictions) for the
+     * given user.
+     */
+    protected boolean isDisabledLocked(int userId) {
+        return mDisabledUsers == null ? false : mDisabledUsers.get(userId);
+    }
+
+    /**
+     * Updates a cached service for a given user.
+     *
+     * @param userId user handle.
+     * @param disabled whether the user is disabled.
+     * @return service for the user.
+     */
+    @GuardedBy("mLock")
+    protected S updateCachedServiceLocked(int userId, boolean disabled) {
+        final S service = getServiceForUserLocked(userId);
+        if (service != null) {
+            service.updateLocked(disabled);
+            if (!service.isEnabledLocked()) {
+                removeCachedServiceLocked(userId);
+            } else {
+                onServiceEnabledLocked(service, userId);
+            }
+        }
+        return service;
+    }
+
+    /**
+     * Gets the Settings property that defines the name of the component name used to bind this
+     * service to an external service, or {@code null} when the service is not defined by such
+     * property (for example, if it's a system service defined by framework resources).
+     */
+    @Nullable
+    protected String getServiceSettingsProperty() {
+        return null;
+    }
+
+    /**
+     * Callback called after a new service was added to the cache, or an existing service that was
+     * previously disabled gets enabled.
+     *
+     * <p>By default doesn't do anything, but can be overridden by subclasses.
+     */
+    @SuppressWarnings("unused")
+    protected void onServiceEnabledLocked(S service, @UserIdInt int userId) {
+    }
+
+    /**
+     * Removes a cached service for a given user.
+     *
+     * @return the removed service;
+     */
+    @GuardedBy("mLock")
+    @NonNull
+    protected S removeCachedServiceLocked(@UserIdInt int userId) {
+        final S service = peekServiceForUserLocked(userId);
+        if (service != null) {
+            mServicesCache.delete(userId);
+        }
+        return service;
+    }
+
+    /**
+     * Visits all services in the cache.
+     */
+    @GuardedBy("mLock")
+    protected void visitServicesLocked(@NonNull Visitor<S> visitor) {
+        final int size = mServicesCache.size();
+        for (int i = 0; i < size; i++) {
+            visitor.visit(mServicesCache.valueAt(i));
+        }
+    }
+
+    /**
+     * Clear the cache by removing all services.
+     */
+    @GuardedBy("mLock")
+    protected void clearCacheLocked() {
+        mServicesCache.clear();
+    }
+
+    // TODO(b/117779333): support proto
+    protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
+        boolean realDebug = debug;
+        boolean realVerbose = verbose;
+
+        try {
+            // Temporarily turn on full logging;
+            debug = verbose = true;
+            final int size = mServicesCache.size();
+            pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
+            pw.print(" Verbose: "); pw.println(realVerbose);
+            pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers);
+            pw.print(prefix); pw.print("Settings property: "); pw.println(
+                    getServiceSettingsProperty());
+            pw.print(prefix); pw.print("Cached services: ");
+            if (size == 0) {
+                pw.println("none");
+            } else {
+                pw.println(size);
+                final String prefix2 = "    ";
+                for (int i = 0; i < size; i++) {
+                    pw.print(prefix); pw.print("Service at "); pw.print(i); pw.println(": ");
+                    final S service = mServicesCache.valueAt(i);
+                    service.dumpLocked(prefix2, pw);
+                    pw.println();
+                }
+            }
+        } finally {
+            debug = realDebug;
+            verbose = realVerbose;
+        }
+    }
+
+    private void startTrackingPackageChanges() {
+        PackageMonitor monitor = new PackageMonitor() {
+            @Override
+            public void onSomePackagesChanged() {
+                synchronized (mLock) {
+                    updateCachedServiceLocked(getChangingUserId());
+                }
+            }
+
+            @Override
+            public void onPackageUpdateFinished(String packageName, int uid) {
+                synchronized (mLock) {
+                    final String activePackageName = getActiveServicePackageName();
+                    if (packageName.equals(activePackageName)) {
+                        removeCachedServiceLocked(getChangingUserId());
+                    } else {
+                        handlePackageUpdateLocked(packageName);
+                    }
+                }
+            }
+
+            @Override
+            public void onPackageRemoved(String packageName, int uid) {
+                synchronized (mLock) {
+                    final int userId = getChangingUserId();
+                    final S service = peekServiceForUserLocked(userId);
+                    if (service != null) {
+                        final ComponentName componentName = service.getServiceComponentName();
+                        if (componentName != null) {
+                            if (packageName.equals(componentName.getPackageName())) {
+                                handleActiveServiceRemoved(userId);
+                            }
+                        }
+                    }
+                }
+            }
+
+            @Override
+            public boolean onHandleForceStop(Intent intent, String[] packages,
+                    int uid, boolean doit) {
+                synchronized (mLock) {
+                    final String activePackageName = getActiveServicePackageName();
+                    for (String pkg : packages) {
+                        if (pkg.equals(activePackageName)) {
+                            if (!doit) {
+                                return true;
+                            }
+                            removeCachedServiceLocked(getChangingUserId());
+                        } else {
+                            handlePackageUpdateLocked(pkg);
+                        }
+                    }
+                }
+                return false;
+            }
+
+            private void handleActiveServiceRemoved(@UserIdInt int userId) {
+                removeCachedServiceLocked(userId);
+                final String serviceSettingsProperty = getServiceSettingsProperty();
+                if (serviceSettingsProperty != null) {
+                    Settings.Secure.putStringForUser(getContext().getContentResolver(),
+                            serviceSettingsProperty, null, userId);
+                }
+            }
+
+            private String getActiveServicePackageName() {
+                final int userId = getChangingUserId();
+                final S service = peekServiceForUserLocked(userId);
+                if (service == null) {
+                    return null;
+                }
+                final ComponentName serviceComponent = service.getServiceComponentName();
+                if (serviceComponent == null) {
+                    return null;
+                }
+                return serviceComponent.getPackageName();
+            }
+
+            @GuardedBy("mLock")
+            private void handlePackageUpdateLocked(String packageName) {
+                visitServicesLocked((s) -> s.handlePackageUpdateLocked(packageName));
+            }
+        };
+
+        // package changes
+        monitor.register(getContext(), null,  UserHandle.ALL, true);
+    }
+
+    /**
+     * Visitor pattern.
+     *
+     * @param <S> visited class.
+     */
+    public interface Visitor<S> {
+        /**
+         * Visits a service.
+         *
+         * @param service the service to be visited.
+         */
+        void visit(@NonNull S service);
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+            ContentResolver resolver = getContext().getContentResolver();
+            final String serviceProperty = getServiceSettingsProperty();
+            if (serviceProperty != null) {
+                resolver.registerContentObserver(Settings.Secure.getUriFor(
+                        serviceProperty), false, this, UserHandle.USER_ALL);
+            }
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.USER_SETUP_COMPLETE), false, this, UserHandle.USER_ALL);
+            registerForExtraSettingsChanges(resolver, this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
+            if (verbose) Slog.v(mTag, "onChange(): uri=" + uri + ", userId=" + userId);
+            final String property = uri.getLastPathSegment();
+            if (property.equals(getServiceSettingsProperty())
+                    || property.equals(Settings.Secure.USER_SETUP_COMPLETE)) {
+                synchronized (mLock) {
+                    updateCachedServiceLocked(userId);
+                }
+            } else {
+                onSettingsChanged(userId, property);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/AbstractPerUserSystemService.java b/services/core/java/com/android/server/AbstractPerUserSystemService.java
new file mode 100644
index 0000000..201abe6
--- /dev/null
+++ b/services/core/java/com/android/server/AbstractPerUserSystemService.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+
+/**
+ * Companion for {@link AbstractMasterSystemService}, it's the base class for the "real" service
+ * implementation.
+ *
+ * @param <S> itself
+ *
+ * @hide
+ */
+public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSystemService<S>> {
+
+    protected final @UserIdInt int mUserId;
+    protected final Object mLock;
+    protected final String mTag = getClass().getSimpleName();
+
+    protected final AbstractMasterSystemService<S> mMaster;
+
+    /**
+     * Whether service was disabled for user due to {@link UserManager} restrictions.
+     */
+    @GuardedBy("mLock")
+    private boolean mDisabled;
+
+    /**
+     * Caches whether the setup completed for the current user.
+     */
+    @GuardedBy("mLock")
+    private boolean mSetupComplete;
+
+    @GuardedBy("mLock")
+    private ServiceInfo mServiceInfo;
+
+    protected AbstractPerUserSystemService(@NonNull AbstractMasterSystemService<S> master,
+            @NonNull Object lock, @UserIdInt int userId) {
+        mMaster = master;
+        mLock = lock;
+        mUserId = userId;
+    }
+
+    /**
+     * Creates a new {@link ServiceInfo} for the given service name.
+     *
+     * @throws NameNotFoundException if the service does not exist.
+     * @throws SecurityException if the service does not have the proper permissions to be bound to.
+     */
+    protected abstract ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
+            throws NameNotFoundException;
+
+    /**
+     * Callback called when an app has been updated.
+     *
+     * @param packageName package of the app being updated.
+     */
+    protected void handlePackageUpdateLocked(@NonNull String packageName) {
+    }
+
+    /**
+     * Gets whether the service is enabled and ready.
+     */
+    @GuardedBy("mLock")
+    protected boolean isEnabledLocked() {
+        return mSetupComplete && mServiceInfo != null && !mDisabled;
+    }
+
+    /**
+     * Updates the state of this service.
+     *
+     * <p>Typically called when the service {@link Settings} property or {@link UserManager}
+     * restriction changed, which includes the initial creation of the service.
+     *
+     * <p>Subclasses can extend this method to provide extra initialization.
+     *
+     * @param disabled whether the service is disabled (due to {@link UserManager} restrictions).
+     *
+     * @return whether the disabled state changed.
+     */
+    @GuardedBy("mLock")
+    @CallSuper
+    protected boolean updateLocked(boolean disabled) {
+
+        final boolean wasEnabled = isEnabledLocked();
+        if (mMaster.verbose) {
+            Slog.v(mTag, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled
+                    + ", mSetupComplete=" + mSetupComplete
+                    + ", disabled=" + disabled + ", mDisabled=" + mDisabled);
+        }
+
+        mSetupComplete = isSetupCompletedLocked();
+        mDisabled = disabled;
+        ComponentName serviceComponent = null;
+        ServiceInfo serviceInfo = null;
+        final String componentName = getComponentNameFromSettings();
+        if (!TextUtils.isEmpty(componentName)) {
+            try {
+                serviceComponent = ComponentName.unflattenFromString(componentName);
+                serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+                        0, mUserId);
+                if (serviceInfo == null) {
+                    Slog.e(mTag, "Bad service name: " + componentName);
+                }
+            } catch (RuntimeException | RemoteException e) {
+                Slog.e(mTag, "Error getting service info for '" + componentName + "': " + e);
+                serviceInfo = null;
+            }
+        }
+        try {
+            if (serviceInfo != null) {
+                mServiceInfo = newServiceInfo(serviceComponent);
+                if (mMaster.debug) {
+                    Slog.d(mTag, "Set component for user " + mUserId + " as " + mServiceInfo);
+                }
+            } else {
+                mServiceInfo = null;
+                if (mMaster.debug) {
+                    Slog.d(mTag, "Reset component for user " + mUserId + ":" + componentName);
+                }
+            }
+        } catch (Exception e) {
+            Slog.e(mTag, "Bad ServiceInfo for '" + componentName + "': " + e);
+            mServiceInfo = null;
+        }
+        return wasEnabled != isEnabledLocked();
+    }
+
+    /**
+     * Gets this UID of the remote service this service binds to, or {@code -1} if the service is
+     * disabled.
+     */
+    @GuardedBy("mLock")
+    protected final int getServiceUidLocked() {
+        if (mServiceInfo == null) {
+            Slog.w(mTag, "getServiceUidLocked(): no mServiceInfo");
+            return Process.INVALID_UID;
+        }
+        return mServiceInfo.applicationInfo.uid;
+    }
+
+    /**
+     * Gets this name of the remote service this service binds to as defined by {@link Settings}.
+     */
+    @Nullable
+    protected final String getComponentNameFromSettings() {
+        final String property = mMaster.getServiceSettingsProperty();
+        return property == null ? null : Settings.Secure
+                .getStringForUser(getContext().getContentResolver(), property, mUserId);
+    }
+
+    /**
+     * Gets the {@link ComponentName} of the remote service this service binds to, or {@code null}
+     * if the service is disabled.
+     */
+    @Nullable
+    public final ComponentName getServiceComponentName() {
+        synchronized (mLock) {
+            return mServiceInfo == null ? null : mServiceInfo.getComponentName();
+        }
+    }
+    /**
+     * Gets the name of the of the app this service binds to, or {@code null} if the service is
+     * disabled.
+     */
+    @Nullable
+    public final String getServicePackageName() {
+        final ComponentName serviceComponent = getServiceComponentName();
+        return serviceComponent == null ? null : serviceComponent.getPackageName();
+    }
+
+    /**
+     * Gets the user-visibile name of the service this service binds to, or {@code null} if the
+     * service is disabled.
+     */
+    @Nullable
+    @GuardedBy("mLock")
+    public final CharSequence getServiceLabelLocked() {
+        return mServiceInfo == null ? null : mServiceInfo.loadSafeLabel(
+                getContext().getPackageManager(), 0 /* do not ellipsize */,
+                PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
+    }
+
+    /**
+     * Gets the icon the service this service binds to, or {@code null} if the service is disabled.
+     */
+    @Nullable
+    @GuardedBy("mLock")
+    public final Drawable getServiceIconLocked() {
+        return mServiceInfo == null ? null
+                : mServiceInfo.loadIcon(getContext().getPackageManager());
+    }
+
+    /**
+     * Whether the service should log debug statements.
+     */
+    public final boolean isDebug() {
+        return mMaster.debug;
+    }
+
+    /**
+     * Whether the service should log verbose statements.
+     */
+    public final boolean isVerbose() {
+        return mMaster.verbose;
+    }
+
+    /**
+     * Gets the target SDK level of the service this service binds to,
+     * or {@code 0} if the service is disabled.
+     */
+    public final int getTargedSdkLocked() {
+        return mServiceInfo == null ? 0 : mServiceInfo.applicationInfo.targetSdkVersion;
+    }
+
+    /**
+     * Gets whether the device already finished setup.
+     */
+    protected final boolean isSetupCompletedLocked() {
+        final String setupComplete = Settings.Secure.getStringForUser(
+                getContext().getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, mUserId);
+        return "1".equals(setupComplete);
+    }
+
+    /**
+     * Gets the context associated with this service.
+     */
+    protected final Context getContext() {
+        return mMaster.getContext();
+    }
+
+    // TODO(b/117779333): support proto
+    @GuardedBy("mLock")
+    protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
+        pw.print(prefix); pw.print("User: "); pw.println(mUserId);
+        pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+        pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
+        pw.print(prefix); pw.print("Service name: "); pw.println(getComponentNameFromSettings());
+    }
+}
diff --git a/services/core/java/com/android/server/AbstractRemoteService.java b/services/core/java/com/android/server/AbstractRemoteService.java
new file mode 100644
index 0000000..1d3a34c
--- /dev/null
+++ b/services/core/java/com/android/server/AbstractRemoteService.java
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+
+/**
+ * Base class representing a remote service.
+ *
+ * <p>It abstracts away the binding and unbinding from the remote implementation, so clients can
+ * call its methods without worrying about when and how to bind/unbind/timeout.
+ *
+ * <p>All state of this class is modified on a handler thread.
+ *
+ * <p>See {@code com.android.server.autofill.RemoteFillService} for a concrete
+ * (no pun intended) example of how to use it.
+ *
+ * @hide
+ */
+//TODO(b/117779333): improve javadoc above instead of using Autofill as an example
+public abstract class AbstractRemoteService implements DeathRecipient {
+
+    private static final int MSG_UNBIND = 1;
+
+    protected static final int LAST_PRIVATE_MSG = MSG_UNBIND;
+
+    // TODO(b/117779333): convert all booleans into an integer / flags
+    public final boolean mVerbose;
+
+    protected final String mTag = getClass().getSimpleName();
+    protected final Handler mHandler;
+    protected final ComponentName mComponentName;
+
+    protected PendingRequest<? extends AbstractRemoteService> mPendingRequest;
+
+    private final Context mContext;
+    private final Intent mIntent;
+    private final VultureCallback mVultureCallback;
+    private final int mUserId;
+    private final ServiceConnection mServiceConnection = new RemoteServiceConnection();
+    private final boolean mBindInstantServiceAllowed;
+    private IInterface mServiceInterface;
+
+    private boolean mBinding;
+    private boolean mDestroyed;
+    private boolean mServiceDied;
+    private boolean mCompleted;
+
+    /**
+     * Callback called when the service dies.
+     */
+    public interface VultureCallback {
+        /**
+         * Called when the service dies.
+         *
+         * @param service service that died!
+         */
+        void onServiceDied(AbstractRemoteService service);
+    }
+
+    public AbstractRemoteService(@NonNull Context context, @NonNull String serviceInterface,
+            @NonNull ComponentName componentName, int userId, @NonNull VultureCallback callback,
+            boolean bindInstantServiceAllowed, boolean verbose) {
+        mContext = context;
+        mVultureCallback = callback;
+        mVerbose = verbose;
+        mComponentName = componentName;
+        mIntent = new Intent(serviceInterface).setComponent(mComponentName);
+        mUserId = userId;
+        mHandler = new Handler(FgThread.getHandler().getLooper());
+        mBindInstantServiceAllowed = bindInstantServiceAllowed;
+    }
+
+    /**
+     * Destroys this service.
+     */
+    public final void destroy() {
+        mHandler.sendMessage(obtainMessage(AbstractRemoteService::handleDestroy, this));
+    }
+
+    /**
+     * Checks whether this service is destroyed.
+     */
+    public final boolean isDestroyed() {
+        return mDestroyed;
+    }
+
+    /**
+     * Callback called when the system connected / disconnected to the service.
+     *
+     * @param state {@code true} when connected, {@code false} when disconnected.
+     */
+    protected void onConnectedStateChanged(boolean state) {
+    }
+
+    /**
+     * Gets the base Binder interface from the service.
+     */
+    @NonNull
+    protected abstract IInterface getServiceInterface(@NonNull IBinder service);
+
+    /**
+     * Defines How long after the last interaction with the service we would unbind.
+     */
+    protected abstract long getTimeoutIdleBindMillis();
+
+    /**
+     * Defines how long after we make a remote request to a fill service we timeout.
+     */
+    protected abstract long getRemoteRequestMillis();
+
+    private void handleDestroy() {
+        if (checkIfDestroyed()) return;
+        if (mPendingRequest != null) {
+            mPendingRequest.cancel();
+            mPendingRequest = null;
+        }
+        ensureUnbound();
+        mDestroyed = true;
+    }
+
+    @Override // from DeathRecipient
+    public void binderDied() {
+        mHandler.sendMessage(obtainMessage(AbstractRemoteService::handleBinderDied, this));
+    }
+
+    private void handleBinderDied() {
+        if (checkIfDestroyed()) return;
+        if (mServiceInterface != null) {
+            mServiceInterface.asBinder().unlinkToDeath(this, 0);
+        }
+        mServiceInterface = null;
+        mServiceDied = true;
+        mVultureCallback.onServiceDied(this);
+    }
+
+    // Note: we are dumping without a lock held so this is a bit racy but
+    // adding a lock to a class that offloads to a handler thread would
+    // mean adding a lock adding overhead to normal runtime operation.
+    /**
+     * Dump it!
+     */
+    public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+        String tab = "  ";
+        pw.append(prefix).append("service:").println();
+        pw.append(prefix).append(tab).append("userId=")
+                .append(String.valueOf(mUserId)).println();
+        pw.append(prefix).append(tab).append("componentName=")
+                .append(mComponentName.flattenToString()).println();
+        pw.append(prefix).append(tab).append("destroyed=")
+                .append(String.valueOf(mDestroyed)).println();
+        pw.append(prefix).append(tab).append("bound=")
+                .append(String.valueOf(isBound())).println();
+        pw.append(prefix).append(tab).append("hasPendingRequest=")
+                .append(String.valueOf(mPendingRequest != null)).println();
+        pw.append(prefix).append("mBindInstantServiceAllowed=").println(mBindInstantServiceAllowed);
+        pw.append(prefix).append("idleTimeout=")
+            .append(Long.toString(getTimeoutIdleBindMillis() / 1000)).append("s").println();
+        pw.append(prefix).append("requestTimeout=")
+            .append(Long.toString(getRemoteRequestMillis() / 1000)).append("s").println();
+        pw.println();
+    }
+
+    protected void scheduleRequest(PendingRequest<? extends AbstractRemoteService> pendingRequest) {
+        mHandler.sendMessage(obtainMessage(
+                AbstractRemoteService::handlePendingRequest, this, pendingRequest));
+    }
+
+    protected void cancelScheduledUnbind() {
+        mHandler.removeMessages(MSG_UNBIND);
+    }
+
+    protected void scheduleUnbind() {
+        cancelScheduledUnbind();
+        mHandler.sendMessageDelayed(obtainMessage(AbstractRemoteService::handleUnbind, this)
+                .setWhat(MSG_UNBIND), getTimeoutIdleBindMillis());
+    }
+
+    private void handleUnbind() {
+        if (checkIfDestroyed()) return;
+
+        ensureUnbound();
+    }
+
+    private void handlePendingRequest(
+            PendingRequest<? extends AbstractRemoteService> pendingRequest) {
+        if (checkIfDestroyed() || mCompleted) return;
+
+        if (!isBound()) {
+            if (mPendingRequest != null) {
+                mPendingRequest.cancel();
+            }
+            mPendingRequest = pendingRequest;
+            ensureBound();
+        } else {
+            if (mVerbose) Slog.v(mTag, "handlePendingRequest(): " + pendingRequest);
+            pendingRequest.run();
+            if (pendingRequest.isFinal()) {
+                mCompleted = true;
+            }
+        }
+    }
+
+    private boolean isBound() {
+        return mServiceInterface != null;
+    }
+
+    private void ensureBound() {
+        if (isBound() || mBinding) return;
+
+        if (mVerbose) Slog.v(mTag, "ensureBound()");
+        mBinding = true;
+
+        int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
+        if (mBindInstantServiceAllowed) {
+            flags |= Context.BIND_ALLOW_INSTANT;
+        }
+
+        final boolean willBind = mContext.bindServiceAsUser(mIntent, mServiceConnection, flags,
+                new UserHandle(mUserId));
+
+        if (!willBind) {
+            Slog.w(mTag, "could not bind to " + mIntent + " using flags " + flags);
+            mBinding = false;
+
+            if (!mServiceDied) {
+                handleBinderDied();
+            }
+        }
+    }
+
+    private void ensureUnbound() {
+        if (!isBound() && !mBinding) return;
+
+        if (mVerbose) Slog.v(mTag, "ensureUnbound()");
+        mBinding = false;
+        if (isBound()) {
+            onConnectedStateChanged(false);
+            if (mServiceInterface != null) {
+                mServiceInterface.asBinder().unlinkToDeath(this, 0);
+                mServiceInterface = null;
+            }
+        }
+        mContext.unbindService(mServiceConnection);
+    }
+
+    private class RemoteServiceConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (mDestroyed || !mBinding) {
+                // This is abnormal. Unbinding the connection has been requested already.
+                Slog.wtf(mTag, "onServiceConnected() was dispatched after unbindService.");
+                return;
+            }
+            mBinding = false;
+            mServiceInterface = getServiceInterface(service);
+            try {
+                service.linkToDeath(AbstractRemoteService.this, 0);
+            } catch (RemoteException re) {
+                handleBinderDied();
+                return;
+            }
+            onConnectedStateChanged(true);
+
+            if (mPendingRequest != null) {
+                final PendingRequest<? extends AbstractRemoteService> pendingRequest =
+                        mPendingRequest;
+                mPendingRequest = null;
+                handlePendingRequest(pendingRequest);
+            }
+
+            mServiceDied = false;
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            mBinding = true;
+            mServiceInterface = null;
+        }
+    }
+
+    private boolean checkIfDestroyed() {
+        if (mDestroyed) {
+            if (mVerbose) {
+                Slog.v(mTag, "Not handling operation as service for " + mComponentName
+                        + " is already destroyed");
+            }
+        }
+        return mDestroyed;
+    }
+
+    protected boolean handleResponseCallbackCommon(
+            PendingRequest<? extends AbstractRemoteService> pendingRequest) {
+        if (isDestroyed()) return false;
+
+        if (mPendingRequest == pendingRequest) {
+            mPendingRequest = null;
+        }
+        if (mPendingRequest == null) {
+            scheduleUnbind();
+        }
+        return true;
+    }
+
+    /**
+     * Base class for the requests serviced by the remote service.
+     *
+     * @param <S> the remote service class
+     */
+    public abstract static class PendingRequest<S extends AbstractRemoteService>
+            implements Runnable {
+        protected final String mTag = getClass().getSimpleName();
+        protected final Object mLock = new Object();
+
+        private final WeakReference<S> mWeakService;
+        private final Runnable mTimeoutTrigger;
+        private final Handler mServiceHandler;
+
+        @GuardedBy("mLock")
+        private boolean mCancelled;
+
+        @GuardedBy("mLock")
+        private boolean mCompleted;
+
+        protected PendingRequest(S service) {
+            mWeakService = new WeakReference<>(service);
+            mServiceHandler = service.mHandler;
+            mTimeoutTrigger = () -> {
+                synchronized (mLock) {
+                    if (mCancelled) {
+                        return;
+                    }
+                    mCompleted = true;
+                }
+
+                Slog.w(mTag, "timed out");
+                final S remoteService = mWeakService.get();
+                if (remoteService != null) {
+                    Slog.w(mTag, " timed out after " + service.getRemoteRequestMillis() + " ms");
+                    onTimeout(remoteService);
+                }
+            };
+            mServiceHandler.postAtTime(mTimeoutTrigger,
+                    SystemClock.uptimeMillis() + service.getRemoteRequestMillis());
+        }
+
+        /**
+         * Gets a reference to the remote service.
+         */
+        protected final S getService() {
+            return mWeakService.get();
+        }
+
+        /**
+         * Subclasses must call this method when the remote service finishes, i.e., when the service
+         * finishes processing a request.
+         *
+         * @return {@code false} in the service is already finished, {@code true} otherwise.
+         */
+        protected final boolean finish() {
+            synchronized (mLock) {
+                if (mCompleted || mCancelled) {
+                    return false;
+                }
+                mCompleted = true;
+            }
+            mServiceHandler.removeCallbacks(mTimeoutTrigger);
+            return true;
+        }
+
+        /**
+         * Checks whether this request was cancelled.
+         */
+        @GuardedBy("mLock")
+        protected final boolean isCancelledLocked() {
+            return mCancelled;
+        }
+
+        /**
+         * Cancels the service.
+         *
+         * @return {@code false} if service is already canceled, {@code true} otherwise.
+         */
+        public boolean cancel() {
+            synchronized (mLock) {
+                if (mCancelled || mCompleted) {
+                    return false;
+                }
+                mCancelled = true;
+            }
+
+            mServiceHandler.removeCallbacks(mTimeoutTrigger);
+            return true;
+        }
+
+        /**
+         * Called by the self-destruct timeout when the remote service didn't reply to the
+         * request on time.
+         */
+        protected abstract void onTimeout(S remoteService);
+
+        /**
+         * Checks whether this request leads to a final state where no other requests can be made.
+         */
+        protected boolean isFinal() {
+            return false;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d631fa8..52b0275 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17355,8 +17355,6 @@
             }
         }
 
-        mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
-
         incrementProcStateSeqAndNotifyAppsLocked();
 
         mNumServiceProcs = mNewNumServiceProcs;
@@ -17618,6 +17616,9 @@
             mHandler.post(new ProcStatsRunnable(ActivityManagerService.this, mProcessStats));
         }
 
+        // Run this after making sure all procstates are updated.
+        mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
+
         if (DEBUG_OOM_ADJ) {
             final long duration = SystemClock.uptimeMillis() - now;
             if (false) {
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
index 60f70c7..6423470 100644
--- a/services/core/java/com/android/server/location/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -159,25 +159,12 @@
 
     /* package */ ContextHubClientBroker(
             Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager,
-            ContextHubInfo contextHubInfo, short hostEndPointId,
-            IContextHubClientCallback callback) {
+            ContextHubInfo contextHubInfo, short hostEndPointId) {
         mContext = context;
         mContextHubProxy = contextHubProxy;
         mClientManager = clientManager;
         mAttachedContextHubInfo = contextHubInfo;
         mHostEndPointId = hostEndPointId;
-        mCallbackInterface = callback;
-    }
-
-    /**
-     * Attaches a death recipient for this client
-     *
-     * @throws RemoteException if the client has already died
-     */
-    /* package */ synchronized void attachDeathRecipient() throws RemoteException {
-        if (mCallbackInterface != null) {
-            mCallbackInterface.asBinder().linkToDeath(this, 0 /* flags */);
-        }
     }
 
     /**
@@ -245,9 +232,15 @@
     public boolean unregisterIntent(PendingIntent pendingIntent) {
         ContextHubServiceUtil.checkPermissions(mContext);
 
+        boolean success = false;
         synchronized (this) {
-            return mPendingIntentRequest.unregister(pendingIntent);
+            success = mPendingIntentRequest.unregister(pendingIntent);
+            if (mCallbackInterface == null) {
+                close();
+            }
         }
+
+        return success;
     }
 
     /**
@@ -276,6 +269,37 @@
     }
 
     /**
+     * Sets the callback interface for this client, only if the callback is currently unregistered.
+     *
+     * Also attaches a death recipient to a ContextHubClientBroker object. If unsuccessful, the
+     * connection is closed.
+     *
+     * @param callback the callback interface
+     * @return true if the callback was successfully set, false otherwise
+     *
+     * @throws IllegalStateException if the client has already been registered to a callback
+     */
+    /* package */
+    synchronized boolean setCallback(IContextHubClientCallback callback) {
+        boolean success = false;
+        if (mCallbackInterface != null) {
+            throw new IllegalStateException("Client is already registered with a callback");
+        } else {
+            mCallbackInterface = callback;
+            try {
+                mCallbackInterface.asBinder().linkToDeath(this, 0 /* flags */);
+                success = true;
+            } catch (RemoteException e) {
+                // The client process has died, so we close the connection.
+                Log.e(TAG, "Failed to attach death recipient to client");
+                close();
+            }
+        }
+
+        return success;
+    }
+
+    /**
      * @return the ID of the context hub this client is attached to
      */
     /* package */ int getAttachedContextHubId() {
@@ -347,6 +371,18 @@
     }
 
     /**
+     * @param intent the PendingIntent to compare to
+     * @return true if the given PendingIntent is currently registered, false otherwise
+     */
+    /* package */ boolean hasPendingIntent(PendingIntent intent) {
+        PendingIntent pendingIntent = null;
+        synchronized (this) {
+            pendingIntent = mPendingIntentRequest.getPendingIntent();
+        }
+        return (pendingIntent != null) && pendingIntent.equals(intent);
+    }
+
+    /**
      * Helper function to invoke a specified client callback, if the connection is open.
      *
      * @param consumer the consumer specifying the callback to invoke
@@ -407,6 +443,9 @@
                 Log.w(TAG, "PendingIntent has been canceled, unregistering from client"
                         + " (host endpoint ID " + mHostEndPointId + ")");
                 mPendingIntentRequest.clear();
+                if (mCallbackInterface == null) {
+                    close();
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index eda8c6f..72879dd 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.location;
 
+import android.app.PendingIntent;
 import android.content.Context;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
 import android.hardware.contexthub.V1_0.IContexthub;
@@ -88,15 +89,9 @@
      */
     /* package */ IContextHubClient registerClient(
             IContextHubClientCallback clientCallback, ContextHubInfo contextHubInfo) {
-        ContextHubClientBroker broker = createNewClientBroker(clientCallback, contextHubInfo);
-
-        try {
-            broker.attachDeathRecipient();
-        } catch (RemoteException e) {
-            // The client process has died, so we close the connection and return null.
-            Log.e(TAG, "Failed to attach death recipient to client");
-            broker.close();
-            return null;
+        ContextHubClientBroker broker = createNewClientBroker(contextHubInfo);
+        if (!broker.setCallback(clientCallback)) {
+            return null; // Client process has died, so we return null
         }
 
         Log.d(TAG, "Registered client with host endpoint ID " + broker.getHostEndPointId());
@@ -104,6 +99,36 @@
     }
 
     /**
+     * Binds a existing and registered client with a new callback interface, provided a previously
+     * registered PendingIntent.
+     *
+     * @param pendingIntent  a previously registered PendingIntent for a registered client
+     * @param clientCallback the callback interface of the client to bind to
+     * @param contextHubId   the ID of the hub this client is attached to
+     *
+     * @return the client interface
+     *
+     * @throws IllegalArgumentException if no matching client is found
+     * @throws IllegalStateException    if the client has already been registered to a callback
+     */
+    /* package */ IContextHubClient bindClient(
+            PendingIntent pendingIntent, IContextHubClientCallback clientCallback,
+            int contextHubId) {
+        ContextHubClientBroker broker = getClientBroker(pendingIntent, contextHubId);
+        if (broker == null) {
+            throw new IllegalArgumentException("Could not find client of Context Hub (ID = "
+                    + contextHubId + ") with PendingIntent");
+        }
+
+        if (!broker.setCallback(clientCallback)) {
+            return null; // Client process has died, so we return null
+        }
+
+        Log.d(TAG, "Re-registered client with host endpoint ID " + broker.getHostEndPointId());
+        return IContextHubClient.Stub.asInterface(broker);
+    }
+
+    /**
      * Handles a message sent from a nanoapp.
      *
      * @param contextHubId the ID of the hub where the nanoapp sent the message from
@@ -182,7 +207,6 @@
      * Creates a new ContextHubClientBroker object for a client and registers it with the client
      * manager.
      *
-     * @param clientCallback the callback interface of the client to register
      * @param contextHubInfo the object describing the hub this client is attached to
      *
      * @return the ContextHubClientBroker object
@@ -190,7 +214,7 @@
      * @throws IllegalStateException if max number of clients have already registered
      */
     private synchronized ContextHubClientBroker createNewClientBroker(
-            IContextHubClientCallback clientCallback, ContextHubInfo contextHubInfo) {
+            ContextHubInfo contextHubInfo) {
         if (mHostEndPointIdToClientMap.size() == MAX_CLIENT_ID + 1) {
             throw new IllegalStateException("Could not register client - max limit exceeded");
         }
@@ -200,8 +224,7 @@
         for (int i = 0; i <= MAX_CLIENT_ID; i++) {
             if (!mHostEndPointIdToClientMap.containsKey((short) id)) {
                 broker = new ContextHubClientBroker(
-                        mContext, mContextHubProxy, this, contextHubInfo, (short) id,
-                        clientCallback);
+                        mContext, mContextHubProxy, this, contextHubInfo, (short) id);
                 mHostEndPointIdToClientMap.put((short) id, broker);
                 mNextHostEndpointId = (id == MAX_CLIENT_ID) ? 0 : id + 1;
                 break;
@@ -236,4 +259,22 @@
             }
         }
     }
+
+    /**
+     * Retrieves a ContextHubClientBroker object with a matching PendingIntent and Context Hub ID.
+     *
+     * @param pendingIntent the PendingIntent to match
+     * @param contextHubId  the ID of the Context Hub the client is attached to
+     * @return the matching ContextHubClientBroker, null if not found
+     */
+    private ContextHubClientBroker getClientBroker(PendingIntent pendingIntent, int contextHubId) {
+        for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
+            if (broker.hasPendingIntent(pendingIntent)
+                    && broker.getAttachedContextHubId() == contextHubId) {
+                return broker;
+            }
+        }
+
+        return null;
+    }
 }
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index e3c2863..215e67c 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.location;
 
+import android.app.PendingIntent;
 import android.content.Context;
 import android.hardware.contexthub.V1_0.AsyncEventType;
 import android.hardware.contexthub.V1_0.ContextHub;
@@ -631,6 +632,37 @@
     }
 
     /**
+     * Recreates and binds a IContextHubClientCallback interface to an existing and registered
+     * client at the service for the specified Context Hub, provided a previously registered
+     * PendingIntent.
+     *
+     * @param pendingIntent  the PendingIntent previously registered for the client
+     * @param clientCallback the client interface to register with the service
+     * @param contextHubId   the ID of the hub this client is attached to
+     * @return the generated client interface, null if registration was unsuccessful
+     *
+     * @throws IllegalArgumentException if contextHubId is not a valid ID
+     * @throws NullPointerException if clientCallback or pendingIntent is null
+     */
+    @Override
+    public IContextHubClient bindClient(
+            PendingIntent pendingIntent, IContextHubClientCallback clientCallback,
+            int contextHubId) throws RemoteException {
+        checkPermissions();
+        if (!isValidContextHubId(contextHubId)) {
+            throw new IllegalArgumentException("Invalid context hub ID " + contextHubId);
+        }
+        if (pendingIntent == null) {
+            throw new NullPointerException("Cannot create client with null pending intent");
+        }
+        if (clientCallback == null) {
+            throw new NullPointerException("Cannot create client with null callback");
+        }
+
+        return mClientManager.bindClient(pendingIntent, clientCallback, contextHubId);
+    }
+
+    /**
      * Loads a nanoapp binary at the specified Context hub.
      *
      * @param contextHubId        the ID of the hub to load the binary
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index fc9bd37..f279af0 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -96,7 +96,7 @@
     private final SettingsObserver mSettingsObserver;
     @VisibleForTesting protected final AppOpsManager mAppOps;
     @VisibleForTesting protected final NotificationManager mNotificationManager;
-    protected ZenModeConfig mDefaultConfig;
+    @VisibleForTesting protected ZenModeConfig mDefaultConfig;
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final ZenModeFiltering mFiltering;
     protected final RingerModeDelegate mRingerModeDelegate = new
@@ -309,9 +309,6 @@
             newConfig = mConfig.copy();
             ZenRule rule = new ZenRule();
             populateZenRule(automaticZenRule, rule, true);
-            if (newConfig.automaticRules.put(rule.id, rule) != null) {
-                rule.modified = true;
-            }
             if (setConfigLocked(newConfig, reason, rule.component, true)) {
                 return rule.id;
             } else {
@@ -341,9 +338,6 @@
                 }
             }
             populateZenRule(automaticZenRule, rule, false);
-            if (newConfig.automaticRules.put(ruleId, rule) != null) {
-                rule.modified = true;
-            }
             return setConfigLocked(newConfig, reason, rule.component, true);
         }
     }
@@ -431,13 +425,16 @@
         updateDefaultAutomaticRuleNames();
         for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
             ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
-            // if default rule wasn't modified, use localized name instead of previous
-            if (currRule != null && !currRule.modified && !defaultRule.name.equals(currRule.name)) {
-                if (canManageAutomaticZenRule(defaultRule)) {
+            // if default rule wasn't user-modified nor enabled, use localized name
+            // instead of previous system name
+            if (currRule != null && !currRule.modified && !currRule.enabled
+                    && !defaultRule.name.equals(currRule.name)) {
+                if (canManageAutomaticZenRule(currRule)) {
                     if (DEBUG) Slog.d(TAG, "Locale change - updating default zen rule name "
                             + "from " + currRule.name + " to " + defaultRule.name);
                     // update default rule (if locale changed, name of rule will change)
-                    updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(defaultRule),
+                    currRule.name = defaultRule.name;
+                    updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
                             "locale changed");
                 }
             }
@@ -481,6 +478,7 @@
         rule.condition = null;
         rule.conditionId = automaticZenRule.getConditionId();
         rule.enabled = automaticZenRule.isEnabled();
+        rule.modified = automaticZenRule.isModified();
         if (automaticZenRule.getZenPolicy() != null) {
             rule.zenPolicy = automaticZenRule.getZenPolicy();
         }
diff --git a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
index b5fe8b1..a907161 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -29,11 +29,9 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -41,12 +39,11 @@
  * Tests for the {@link TaskStack} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.AnimatingAppWindowTokenRegistryTest
+ *  atest FrameworksServicesTests:AnimatingAppWindowTokenRegistryTest
  */
 @SmallTest
 @Presubmit
 @FlakyTest(detail = "Promote once confirmed non-flaky")
-@RunWith(AndroidJUnit4.class)
 public class AnimatingAppWindowTokenRegistryTest extends WindowTestsBase {
 
     @Mock
@@ -56,14 +53,14 @@
     Runnable mMockEndDeferFinishCallback1;
     @Mock
     Runnable mMockEndDeferFinishCallback2;
+
     @Before
     public void setUp() throws Exception {
-        super.setUp();
         MockitoAnnotations.initMocks(this);
     }
 
     @Test
-    public void testDeferring() throws Exception {
+    public void testDeferring() {
         final AppWindowToken window1 = createAppWindowToken(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         final AppWindowToken window2 = createAppWindow(window1.getTask(), ACTIVITY_TYPE_STANDARD,
@@ -85,7 +82,7 @@
     }
 
     @Test
-    public void testContainerRemoved() throws Exception {
+    public void testContainerRemoved() {
         final AppWindowToken window1 = createAppWindowToken(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         final AppWindowToken window2 = createAppWindow(window1.getTask(), ACTIVITY_TYPE_STANDARD,
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java
index fc3ca93..5e12a95 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -27,28 +27,28 @@
 import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
+/**
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:AppTransitionControllerTest
+ */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class AppTransitionControllerTest extends WindowTestsBase {
 
     private AppTransitionController mAppTransitionController;
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
-        mAppTransitionController = new AppTransitionController(sWm, mDisplayContent);
+        mAppTransitionController = new AppTransitionController(mWm, mDisplayContent);
     }
 
     @Test
-    public void testTranslucentOpen() throws Exception {
-        synchronized (sWm.mGlobalLock) {
+    public void testTranslucentOpen() {
+        synchronized (mWm.mGlobalLock) {
             final AppWindowToken behind = createAppWindowToken(mDisplayContent,
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
             final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent,
@@ -64,8 +64,8 @@
     }
 
     @Test
-    public void testTranslucentClose() throws Exception {
-        synchronized (sWm.mGlobalLock) {
+    public void testTranslucentClose() {
+        synchronized (mWm.mGlobalLock) {
             final AppWindowToken behind = createAppWindowToken(mDisplayContent,
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
             final AppWindowToken translucentClosing = createAppWindowToken(mDisplayContent,
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
index ee6fbac..f12619c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -32,81 +32,75 @@
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.spy;
 
-import android.content.Context;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.Display;
 import android.view.IApplicationToken;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link AppTransition}.
  *
- * atest AppTransitionTests
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:AppTransitionTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class AppTransitionTests extends WindowTestsBase {
 
     private DisplayContent mDc;
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
-        final Context context = InstrumentationRegistry.getTargetContext();
-        mDc = sWm.getDefaultDisplayContentLocked();
+        mDc = mWm.getDefaultDisplayContentLocked();
         // For unit test,  we don't need to test performSurfacePlacement to prevent some
         // abnormal interaction with surfaceflinger native side.
-        sWm.mRoot = spy(sWm.mRoot);
-        doNothing().when(sWm.mRoot).performSurfacePlacement(anyBoolean());
+        mWm.mRoot = spy(mWm.mRoot);
+        doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean());
     }
 
     @Test
-    public void testKeyguardOverride() throws Exception {
-        sWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
-        sWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
+    public void testKeyguardOverride() {
+        mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
+        mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
         assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition());
     }
 
     @Test
-    public void testKeyguardKeep() throws Exception {
-        sWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
-        sWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
+    public void testKeyguardKeep() {
+        mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
+        mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
         assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition());
     }
 
     @Test
-    public void testForceOverride() throws Exception {
-        sWm.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */);
+    public void testForceOverride() {
+        mWm.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */);
         mDc.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN,
                 false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */);
         assertEquals(TRANSIT_ACTIVITY_OPEN, mDc.mAppTransition.getAppTransition());
     }
 
     @Test
-    public void testCrashing() throws Exception {
-        sWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
-        sWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
+    public void testCrashing() {
+        mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */);
+        mWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
         assertEquals(TRANSIT_CRASHING_ACTIVITY_CLOSE, mDc.mAppTransition.getAppTransition());
     }
 
     @Test
-    public void testKeepKeyguard_withCrashing() throws Exception {
-        sWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
-        sWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
+    public void testKeepKeyguard_withCrashing() {
+        mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */);
+        mWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
         assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition());
     }
 
     @Test
-    public void testAppTransitionStateForMultiDisplay() throws Exception {
+    public void testAppTransitionStateForMultiDisplay() {
         // Create 2 displays & presume both display the state is ON for ready to display & animate.
         final DisplayContent dc1 = createNewDisplayWithController(Display.STATE_ON);
         final DisplayContent dc2 = createNewDisplayWithController(Display.STATE_ON);
@@ -149,7 +143,7 @@
     }
 
     @Test
-    public void testCleanAppTransitionWhenTaskStackReparent() throws Exception {
+    public void testCleanAppTransitionWhenTaskStackReparent() {
         // Create 2 displays & presume both display the state is ON for ready to display & animate.
         final DisplayContent dc1 = createNewDisplayWithController(Display.STATE_ON);
         final DisplayContent dc2 = createNewDisplayWithController(Display.STATE_ON);
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index fcd8a39e4..415b5d9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -21,6 +21,8 @@
 import static android.content.res.Configuration.EMPTY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -29,10 +31,8 @@
 
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.WindowTestUtils.TestTaskWindowContainerController;
 
@@ -41,16 +41,17 @@
 /**
  * Test class for {@link AppWindowContainerController}.
  *
- * atest FrameworksServicesTests:com.android.server.wm.AppWindowContainerControllerTests
+ * atest FrameworksServicesTests:AppWindowContainerControllerTests
  */
+@FlakyTest(bugId = 74078662)
 @SmallTest
 @Presubmit
-@FlakyTest(bugId = 74078662)
-@org.junit.runner.RunWith(AndroidJUnit4.class)
 public class AppWindowContainerControllerTests extends WindowTestsBase {
 
+    private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
+
     @Test
-    public void testRemoveContainer() throws Exception {
+    public void testRemoveContainer() {
         final WindowTestUtils.TestAppWindowContainerController controller =
                 createAppWindowController();
 
@@ -68,7 +69,7 @@
     }
 
     @Test
-    public void testSetOrientation() throws Exception {
+    public void testSetOrientation() {
         final WindowTestUtils.TestAppWindowContainerController controller =
                 createAppWindowController();
 
@@ -84,7 +85,7 @@
         assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation());
 
         // Reset display frozen state
-        sWm.mDisplayFrozen = false;
+        mWm.mDisplayFrozen = false;
     }
 
     private void assertHasStartingWindow(AppWindowToken atoken) {
@@ -103,10 +104,10 @@
     }
 
     @Test
-    public void testCreateRemoveStartingWindow() throws Exception {
+    public void testCreateRemoveStartingWindow() {
         final WindowTestUtils.TestAppWindowContainerController controller =
                 createAppWindowController();
-        controller.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+        controller.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
                 false, false);
         waitUntilHandlersIdle();
@@ -118,34 +119,34 @@
     }
 
     @Test
-    public void testAddRemoveRace() throws Exception {
-
+    public void testAddRemoveRace() {
         // There was once a race condition between adding and removing starting windows
         for (int i = 0; i < 1000; i++) {
             final WindowTestUtils.TestAppWindowContainerController controller =
                     createAppWindowController();
-            controller.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+            controller.addStartingWindow(mPackageName,
                     android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
                     false, false);
             controller.removeStartingWindow();
             waitUntilHandlersIdle();
             assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
 
-            controller.getAppWindowToken(mDisplayContent).getParent().getParent().removeImmediately();
+            controller.getAppWindowToken(
+                    mDisplayContent).getParent().getParent().removeImmediately();
         }
     }
 
     @Test
-    public void testTransferStartingWindow() throws Exception {
+    public void testTransferStartingWindow() {
         final WindowTestUtils.TestAppWindowContainerController controller1 =
                 createAppWindowController();
         final WindowTestUtils.TestAppWindowContainerController controller2 =
                 createAppWindowController();
-        controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+        controller1.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
                 false, false);
         waitUntilHandlersIdle();
-        controller2.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+        controller2.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
                 true, true, false, true, false, false);
         waitUntilHandlersIdle();
@@ -154,19 +155,19 @@
     }
 
     @Test
-    public void testTransferStartingWindowWhileCreating() throws Exception {
+    public void testTransferStartingWindowWhileCreating() {
         final WindowTestUtils.TestAppWindowContainerController controller1 =
                 createAppWindowController();
         final WindowTestUtils.TestAppWindowContainerController controller2 =
                 createAppWindowController();
-        ((TestWindowManagerPolicy) sWm.mPolicy).setRunnableWhenAddingSplashScreen(() -> {
+        ((TestWindowManagerPolicy) mWm.mPolicy).setRunnableWhenAddingSplashScreen(() -> {
 
             // Surprise, ...! Transfer window in the middle of the creation flow.
-            controller2.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+            controller2.addStartingWindow(mPackageName,
                     android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
                     true, true, false, true, false, false);
         });
-        controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+        controller1.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
                 false, false);
         waitUntilHandlersIdle();
@@ -175,7 +176,7 @@
     }
 
     @Test
-    public void testTryTransferStartingWindowFromHiddenAboveToken() throws Exception {
+    public void testTryTransferStartingWindowFromHiddenAboveToken() {
 
         // Add two tasks on top of each other.
         TestTaskWindowContainerController taskController =
@@ -186,7 +187,7 @@
                 createAppWindowController(taskController);
 
         // Add a starting window.
-        controllerTop.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
+        controllerTop.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
                 false, false);
         waitUntilHandlersIdle();
@@ -202,7 +203,7 @@
     }
 
     @Test
-    public void testReparent() throws Exception {
+    public void testReparent() {
         final StackWindowController stackController =
             createStackControllerOnDisplay(mDisplayContent);
         final WindowTestUtils.TestTaskWindowContainerController taskController1 =
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index e3ab5cf..4522494 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -26,15 +26,14 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
 import android.view.SurfaceControl;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.server.wm.WindowTestUtils.TestAppWindowToken;
 
+import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -43,10 +42,9 @@
  * Animation related tests for the {@link AppWindowToken} class.
  *
  * Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.wm.AppWindowTokenAnimationTests
+ *  atest FrameworksServicesTests:AppWindowTokenAnimationTests
  */
 @SmallTest
-@RunWith(AndroidJUnit4.class)
 public class AppWindowTokenAnimationTests extends WindowTestsBase {
 
     private TestAppWindowToken mToken;
@@ -56,9 +54,8 @@
     @Mock
     private AnimationAdapter mSpec;
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
         MockitoAnnotations.initMocks(this);
 
         mToken = createTestAppWindowToken(mDisplayContent, WINDOWING_MODE_FULLSCREEN,
@@ -67,7 +64,7 @@
     }
 
     @Test
-    public void clipAfterAnim_boundsLayerIsCreated() throws Exception {
+    public void clipAfterAnim_boundsLayerIsCreated() {
         mToken.mNeedsAnimationBoundsLayer = true;
 
         mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
@@ -78,7 +75,7 @@
     }
 
     @Test
-    public void clipAfterAnim_boundsLayerIsDestroyed() throws Exception {
+    public void clipAfterAnim_boundsLayerIsDestroyed() {
         mToken.mNeedsAnimationBoundsLayer = true;
         mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         final SurfaceControl leash = mToken.mSurfaceAnimator.mLeash;
@@ -95,7 +92,7 @@
     }
 
     @Test
-    public void clipAfterAnimCancelled_boundsLayerIsDestroyed() throws Exception {
+    public void clipAfterAnimCancelled_boundsLayerIsDestroyed() {
         mToken.mNeedsAnimationBoundsLayer = true;
         mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         final SurfaceControl leash = mToken.mSurfaceAnimator.mLeash;
@@ -108,7 +105,7 @@
     }
 
     @Test
-    public void clipNoneAnim_boundsLayerIsNotCreated() throws Exception {
+    public void clipNoneAnim_boundsLayerIsNotCreated() {
         mToken.mNeedsAnimationBoundsLayer = false;
 
         mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index 7935ec1..a8e1ed4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -47,31 +47,27 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link AppWindowToken} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.AppWindowTokenTests
+ *  atest FrameworksServicesTests:AppWindowTokenTests
  */
+@FlakyTest(bugId = 68267650)
 @SmallTest
-// TODO: b/68267650
-// @Presubmit
-@RunWith(AndroidJUnit4.class)
+@Presubmit
 public class AppWindowTokenTests extends WindowTestsBase {
 
     TaskStack mStack;
     Task mTask;
     WindowTestUtils.TestAppWindowToken mToken;
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
-
         mStack = createTaskStackOnDisplay(mDisplayContent);
         mTask = createTaskInStack(mStack, 0 /* userId */);
         mToken = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
@@ -81,7 +77,7 @@
 
     @Test
     @Presubmit
-    public void testAddWindow_Order() throws Exception {
+    public void testAddWindow_Order() {
         assertEquals(0, mToken.getWindowsCount());
 
         final WindowState win1 = createWindow(null, TYPE_APPLICATION, mToken, "win1");
@@ -107,7 +103,7 @@
 
     @Test
     @Presubmit
-    public void testFindMainWindow() throws Exception {
+    public void testFindMainWindow() {
         assertNull(mToken.findMainWindow());
 
         final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
@@ -123,7 +119,7 @@
 
     @Test
     @Presubmit
-    public void testGetTopFullscreenWindow() throws Exception {
+    public void testGetTopFullscreenWindow() {
         assertNull(mToken.getTopFullscreenWindow());
 
         final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1");
@@ -138,10 +134,10 @@
     }
 
     @Test
-    public void testLandscapeSeascapeRotationByApp() throws Exception {
+    public void testLandscapeSeascapeRotationByApp() {
         // Some plumbing to get the service ready for rotation updates.
-        sWm.mDisplayReady = true;
-        sWm.mDisplayEnabled = true;
+        mWm.mDisplayReady = true;
+        mWm.mDisplayEnabled = true;
 
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
@@ -151,26 +147,26 @@
 
         // Set initial orientation and update.
         mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-        sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
+        mWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
                 mDisplayContent.getDisplayId());
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
         appWindow.resizeReported = false;
 
         // Update the orientation to perform 180 degree rotation and check that resize was reported.
         mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
-        sWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
+        mWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
                 mDisplayContent.getDisplayId());
-        sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
+        mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
         assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation());
         assertTrue(appWindow.resizeReported);
         appWindow.removeImmediately();
     }
 
     @Test
-    public void testLandscapeSeascapeRotationByPolicy() throws Exception {
+    public void testLandscapeSeascapeRotationByPolicy() {
         // Some plumbing to get the service ready for rotation updates.
-        sWm.mDisplayReady = true;
-        sWm.mDisplayEnabled = true;
+        mWm.mDisplayReady = true;
+        mWm.mDisplayEnabled = true;
 
         final DisplayRotation spiedRotation = spy(mDisplayContent.getDisplayRotation());
         mDisplayContent.setDisplayRotation(spiedRotation);
@@ -194,15 +190,15 @@
 
     private void performRotation(DisplayRotation spiedRotation, int rotationToReport) {
         doReturn(rotationToReport).when(spiedRotation).rotationForOrientation(anyInt(), anyInt());
-        sWm.updateRotation(false, false);
+        mWm.updateRotation(false, false);
         // Prevent the next rotation from being deferred by animation.
-        sWm.mAnimator.setScreenRotationAnimationLocked(mDisplayContent.getDisplayId(), null);
-        sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
+        mWm.mAnimator.setScreenRotationAnimationLocked(mDisplayContent.getDisplayId(), null);
+        mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
     }
 
     @Test
     @Presubmit
-    public void testGetOrientation() throws Exception {
+    public void testGetOrientation() {
         mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
         mToken.setFillsParent(false);
@@ -220,7 +216,7 @@
 
     @Test
     @Presubmit
-    public void testKeyguardFlagsDuringRelaunch() throws Exception {
+    public void testKeyguardFlagsDuringRelaunch() {
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD;
@@ -246,7 +242,7 @@
 
     @Test
     @FlakyTest(detail = "Promote once confirmed non-flaky")
-    public void testStuckExitingWindow() throws Exception {
+    public void testStuckExitingWindow() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
         closingWindow.mAnimatingExit = true;
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index d65055c..1c5391e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -11,12 +11,12 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
 
-import static android.view.Display.DEFAULT_DISPLAY;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
 import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
@@ -24,11 +24,11 @@
 import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 import android.animation.ValueAnimator;
 import android.content.Context;
@@ -37,16 +37,14 @@
 import android.os.Looper;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.BoundsAnimationController.BoundsAnimator;
 import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
 
+import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks
@@ -59,21 +57,20 @@
  * appropriately.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.BoundsAnimationControllerTests
+ *  atest FrameworksServicesTests:BoundsAnimationControllerTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class BoundsAnimationControllerTests extends WindowTestsBase {
 
     /**
      * Mock value animator to simulate updates with.
      */
-    private class MockValueAnimator extends ValueAnimator {
+    private static class MockValueAnimator extends ValueAnimator {
 
         private float mFraction;
 
-        public MockValueAnimator getWithValue(float fraction) {
+        MockValueAnimator getWithValue(float fraction) {
             mFraction = fraction;
             return this;
         }
@@ -87,12 +84,12 @@
     /**
      * Mock app transition to fire notifications to the bounds animator.
      */
-    private class MockAppTransition extends AppTransition {
+    private static class MockAppTransition extends AppTransition {
 
         private AppTransitionListener mListener;
 
-        MockAppTransition(Context context) {
-            super(context, sWm, mDisplayContent);
+        MockAppTransition(Context context, WindowManagerService wm, DisplayContent displayContent) {
+            super(context, wm, displayContent);
         }
 
         @Override
@@ -120,7 +117,7 @@
     /**
      * A test animate bounds user to track callbacks from the bounds animation.
      */
-    private class TestBoundsAnimationTarget implements BoundsAnimationTarget {
+    private static class TestBoundsAnimationTarget implements BoundsAnimationTarget {
 
         boolean mAwaitingAnimationStart;
         boolean mMovedToFullscreen;
@@ -193,21 +190,23 @@
     /**
      * Drives the animations, makes common assertions along the way.
      */
-    private class BoundsAnimationDriver {
+    private static class BoundsAnimationDriver {
 
-        private BoundsAnimationController mController;
-        private TestBoundsAnimationTarget mTarget;
+        private final BoundsAnimationController mController;
+        private final TestBoundsAnimationTarget mTarget;
+        private final MockValueAnimator mMockAnimator;
+
         private BoundsAnimator mAnimator;
-
         private Rect mFrom;
         private Rect mTo;
         private Rect mLargerBounds;
         private Rect mExpectedFinalBounds;
 
         BoundsAnimationDriver(BoundsAnimationController controller,
-                TestBoundsAnimationTarget target) {
+                TestBoundsAnimationTarget target, MockValueAnimator mockValueAnimator) {
             mController = controller;
             mTarget = target;
+            mMockAnimator = mockValueAnimator;
         }
 
         BoundsAnimationDriver start(Rect from, Rect to) {
@@ -222,7 +221,7 @@
 
             // Started, not running
             assertTrue(mTarget.mAwaitingAnimationStart);
-            assertTrue(!mTarget.mAnimationStarted);
+            assertFalse(mTarget.mAnimationStarted);
 
             startImpl(from, to);
 
@@ -236,7 +235,7 @@
             }
 
             // Started and running
-            assertTrue(!mTarget.mAwaitingAnimationStart);
+            assertFalse(mTarget.mAwaitingAnimationStart);
             assertTrue(mTarget.mAnimationStarted);
 
             return this;
@@ -268,7 +267,7 @@
                 assertTrue(mTarget.mForcePipModeChangedCallback);
             } else {
                 // No animation start for replacing animation
-                assertTrue(!mTarget.mAnimationStarted);
+                assertFalse(mTarget.mAnimationStarted);
             }
             mTarget.mAnimationStarted = true;
             return this;
@@ -297,7 +296,7 @@
 
             // Animating to larger size
             if (mFrom.equals(mLargerBounds)) {
-                assertTrue(!mAnimator.animatingToLargerSize());
+                assertFalse(mAnimator.animatingToLargerSize());
             } else if (mTo.equals(mLargerBounds)) {
                 assertTrue(mAnimator.animatingToLargerSize());
             }
@@ -339,10 +338,10 @@
             mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(0.5f));
 
             // Not started, not running, cancel reset
-            assertTrue(!mTarget.mCancelRequested);
+            assertFalse(mTarget.mCancelRequested);
 
             // Stack/task bounds not updated
-            assertTrue(!mTarget.mBoundsUpdated);
+            assertFalse(mTarget.mBoundsUpdated);
 
             // Callback made
             assertTrue(mTarget.mAnimationEnded);
@@ -372,14 +371,10 @@
             return this;
         }
 
-        private Rect getLargerBounds(Rect r1, Rect r2) {
+        private static Rect getLargerBounds(Rect r1, Rect r2) {
             int r1Area = r1.width() * r1.height();
             int r2Area = r2.width() * r2.height();
-            if (r1Area <= r2Area) {
-                return r2;
-            } else {
-                return r1;
-            }
+            return (r1Area <= r2Area) ? r2 : r1;
         }
     }
 
@@ -395,32 +390,29 @@
 
     // Common
     private MockAppTransition mMockAppTransition;
-    private MockValueAnimator mMockAnimator;
     private TestBoundsAnimationTarget mTarget;
     private BoundsAnimationController mController;
     private BoundsAnimationDriver mDriver;
 
     // Temp
-    private Rect mTmpRect = new Rect();
+    private static final Rect sTmpRect = new Rect();
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
-
-        final Context context = InstrumentationRegistry.getTargetContext();
+        final Context context = getInstrumentation().getTargetContext();
         final Handler handler = new Handler(Looper.getMainLooper());
-        mMockAppTransition = new MockAppTransition(context);
-        mMockAnimator = new MockValueAnimator();
+        mMockAppTransition = new MockAppTransition(context, mWm, mDisplayContent);
         mTarget = new TestBoundsAnimationTarget();
         mController = new BoundsAnimationController(context, mMockAppTransition, handler, null);
-        mDriver = new BoundsAnimationDriver(mController, mTarget);
+        final MockValueAnimator mockValueAnimator = new MockValueAnimator();
+        mDriver = new BoundsAnimationDriver(mController, mTarget, mockValueAnimator);
     }
 
     /** BASE TRANSITIONS **/
 
     @UiThreadTest
     @Test
-    public void testFullscreenToFloatingTransition() throws Exception {
+    public void testFullscreenToFloatingTransition() {
         mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0f)
@@ -432,7 +424,7 @@
 
     @UiThreadTest
     @Test
-    public void testFloatingToFullscreenTransition() throws Exception {
+    public void testFloatingToFullscreenTransition() {
         mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
                 .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
                 .update(0f)
@@ -444,7 +436,7 @@
 
     @UiThreadTest
     @Test
-    public void testFloatingToSmallerFloatingTransition() throws Exception {
+    public void testFloatingToSmallerFloatingTransition() {
         mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0f)
@@ -456,7 +448,7 @@
 
     @UiThreadTest
     @Test
-    public void testFloatingToLargerFloatingTransition() throws Exception {
+    public void testFloatingToLargerFloatingTransition() {
         mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0f)
@@ -470,7 +462,7 @@
 
     @UiThreadTest
     @Test
-    public void testFullscreenToFloatingCancelFromTarget() throws Exception {
+    public void testFullscreenToFloatingCancelFromTarget() {
         mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
@@ -480,7 +472,7 @@
 
     @UiThreadTest
     @Test
-    public void testFullscreenToFloatingCancelFromAnimationToSameBounds() throws Exception {
+    public void testFullscreenToFloatingCancelFromAnimationToSameBounds() {
         mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
@@ -491,7 +483,7 @@
 
     @UiThreadTest
     @Test
-    public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() throws Exception {
+    public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() {
         mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
@@ -503,7 +495,7 @@
 
     @UiThreadTest
     @Test
-    public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() throws Exception {
+    public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() {
         // When animating from fullscreen and the animation is interruped, we expect the animation
         // start callback to be made, with a forced pip mode change callback
         mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
@@ -518,7 +510,7 @@
 
     @UiThreadTest
     @Test
-    public void testFloatingToFullscreenCancelFromTarget() throws Exception {
+    public void testFloatingToFullscreenCancelFromTarget() {
         mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
                 .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
@@ -528,7 +520,7 @@
 
     @UiThreadTest
     @Test
-    public void testFloatingToFullscreenCancelFromAnimationToSameBounds() throws Exception {
+    public void testFloatingToFullscreenCancelFromAnimationToSameBounds() {
         mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
                 .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
@@ -539,7 +531,7 @@
 
     @UiThreadTest
     @Test
-    public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() throws Exception {
+    public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() {
         mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
                 .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
@@ -553,7 +545,7 @@
 
     @UiThreadTest
     @Test
-    public void testFloatingToSmallerFloatingCancelFromTarget() throws Exception {
+    public void testFloatingToSmallerFloatingCancelFromTarget() {
         mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
@@ -563,7 +555,7 @@
 
     @UiThreadTest
     @Test
-    public void testFloatingToLargerFloatingCancelFromTarget() throws Exception {
+    public void testFloatingToLargerFloatingCancelFromTarget() {
         mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING)
                 .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
                 .update(0.25f)
@@ -575,7 +567,7 @@
 
     @UiThreadTest
     @Test
-    public void testBoundsAreCopied() throws Exception {
+    public void testBoundsAreCopied() {
         Rect from = new Rect(0, 0, 100, 100);
         Rect to = new Rect(25, 25, 75, 75);
         mDriver.start(from, to)
@@ -588,9 +580,9 @@
     /**
      * @return whether the task and stack bounds would be the same if they were at the same offset.
      */
-    private boolean assertEqualSizeAtOffset(Rect stackBounds, Rect taskBounds) {
-        mTmpRect.set(taskBounds);
-        mTmpRect.offsetTo(stackBounds.left, stackBounds.top);
-        return stackBounds.equals(mTmpRect);
+    private static boolean assertEqualSizeAtOffset(Rect stackBounds, Rect taskBounds) {
+        sTmpRect.set(taskBounds);
+        sTmpRect.offsetTo(stackBounds.left, stackBounds.top);
+        return stackBounds.equals(sTmpRect);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
index 21555e3..b6a7cfb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -32,26 +32,22 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 
-import androidx.test.runner.AndroidJUnit4;
-
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Build/Install/Run:
- * atest FrameworksServicesTests:com.android.server.wm.DimmerTests;
+ *  atest FrameworksServicesTests:DimmerTests;
  */
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class DimmerTests extends WindowTestsBase {
 
-    private class TestWindowContainer extends WindowContainer<TestWindowContainer> {
+    private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         final SurfaceControl mControl = mock(SurfaceControl.class);
         final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
 
-        TestWindowContainer() {
-            super(sWm);
+        TestWindowContainer(WindowManagerService wm) {
+            super(wm);
         }
 
         @Override
@@ -65,13 +61,13 @@
         }
     }
 
-    private class MockSurfaceBuildingContainer extends WindowContainer<TestWindowContainer> {
+    private static class MockSurfaceBuildingContainer extends WindowContainer<TestWindowContainer> {
         final SurfaceSession mSession = new SurfaceSession();
         final SurfaceControl mHostControl = mock(SurfaceControl.class);
         final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class);
 
-        MockSurfaceBuildingContainer() {
-            super(sWm);
+        MockSurfaceBuildingContainer(WindowManagerService wm) {
+            super(wm);
         }
 
         class MockSurfaceBuilder extends SurfaceControl.Builder {
@@ -116,15 +112,14 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
-        mHost = new MockSurfaceBuildingContainer();
+        mHost = new MockSurfaceBuildingContainer(mWm);
         mSurfaceAnimatorStarter = spy(new SurfaceAnimatorStarterImpl());
         mTransaction = mock(SurfaceControl.Transaction.class);
         mDimmer = new Dimmer(mHost, mSurfaceAnimatorStarter);
     }
 
     @Test
-    public void testDimAboveNoChildCreatesSurface() throws Exception {
+    public void testDimAboveNoChildCreatesSurface() {
         final float alpha = 0.8f;
         mDimmer.dimAbove(mTransaction, alpha);
 
@@ -137,7 +132,7 @@
     }
 
     @Test
-    public void testDimAboveNoChildRedundantlyUpdatesAlphaOnExistingSurface() throws Exception {
+    public void testDimAboveNoChildRedundantlyUpdatesAlphaOnExistingSurface() {
         float alpha = 0.8f;
         mDimmer.dimAbove(mTransaction, alpha);
         final SurfaceControl firstSurface = getDimLayer();
@@ -150,7 +145,7 @@
     }
 
     @Test
-    public void testUpdateDimsAppliesSize() throws Exception {
+    public void testUpdateDimsAppliesSize() {
         mDimmer.dimAbove(mTransaction, 0.8f);
 
         int width = 100;
@@ -163,7 +158,7 @@
     }
 
     @Test
-    public void testDimAboveNoChildNotReset() throws Exception {
+    public void testDimAboveNoChildNotReset() {
         mDimmer.dimAbove(mTransaction, 0.8f);
         SurfaceControl dimLayer = getDimLayer();
         mDimmer.resetDimStates();
@@ -174,8 +169,8 @@
     }
 
     @Test
-    public void testDimAboveWithChildCreatesSurfaceAboveChild() throws Exception {
-        TestWindowContainer child = new TestWindowContainer();
+    public void testDimAboveWithChildCreatesSurfaceAboveChild() {
+        TestWindowContainer child = new TestWindowContainer(mWm);
         mHost.addChild(child, 0);
 
         final float alpha = 0.8f;
@@ -189,8 +184,8 @@
     }
 
     @Test
-    public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild() throws Exception {
-        TestWindowContainer child = new TestWindowContainer();
+    public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild() {
+        TestWindowContainer child = new TestWindowContainer(mWm);
         mHost.addChild(child, 0);
 
         final float alpha = 0.8f;
@@ -204,8 +199,8 @@
     }
 
     @Test
-    public void testDimBelowWithChildSurfaceDestroyedWhenReset() throws Exception {
-        TestWindowContainer child = new TestWindowContainer();
+    public void testDimBelowWithChildSurfaceDestroyedWhenReset() {
+        TestWindowContainer child = new TestWindowContainer(mWm);
         mHost.addChild(child, 0);
 
         final float alpha = 0.8f;
@@ -220,8 +215,8 @@
     }
 
     @Test
-    public void testDimBelowWithChildSurfaceNotDestroyedWhenPersisted() throws Exception {
-        TestWindowContainer child = new TestWindowContainer();
+    public void testDimBelowWithChildSurfaceNotDestroyedWhenPersisted() {
+        TestWindowContainer child = new TestWindowContainer(mWm);
         mHost.addChild(child, 0);
 
         final float alpha = 0.8f;
@@ -236,9 +231,9 @@
     }
 
     @Test
-    public void testDimUpdateWhileDimming() throws Exception {
+    public void testDimUpdateWhileDimming() {
         Rect bounds = new Rect();
-        TestWindowContainer child = new TestWindowContainer();
+        TestWindowContainer child = new TestWindowContainer(mWm);
         mHost.addChild(child, 0);
 
         final float alpha = 0.8f;
@@ -258,8 +253,8 @@
     }
 
     @Test
-    public void testRemoveDimImmediately() throws Exception {
-        TestWindowContainer child = new TestWindowContainer();
+    public void testRemoveDimImmediately() {
+        TestWindowContainer child = new TestWindowContainer(mWm);
         mHost.addChild(child, 0);
 
         mDimmer.dimAbove(mTransaction, child, 1);
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 8b75570..dd374e9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -53,7 +53,6 @@
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.util.DisplayMetrics;
-import android.util.SparseIntArray;
 import android.view.DisplayCutout;
 import android.view.Gravity;
 import android.view.MotionEvent;
@@ -61,12 +60,10 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -78,16 +75,15 @@
  * Tests for the {@link DisplayContent} class.
  *
  * Build/Install/Run:
- *  atest com.android.server.wm.DisplayContentTests
+ *  atest FrameworksServicesTests:DisplayContentTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class DisplayContentTests extends WindowTestsBase {
 
     @Test
     @FlakyTest(bugId = 77772044)
-    public void testForAllWindows() throws Exception {
+    public void testForAllWindows() {
         final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION,
                 mDisplayContent, "exiting app");
         final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken;
@@ -108,11 +104,11 @@
     }
 
     @Test
-    public void testForAllWindows_WithAppImeTarget() throws Exception {
+    public void testForAllWindows_WithAppImeTarget() {
         final WindowState imeAppTarget =
                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
 
-        sWm.mInputMethodTarget = imeAppTarget;
+        mWm.mInputMethodTarget = imeAppTarget;
 
         assertForAllWindowsOrder(Arrays.asList(
                 mWallpaperWindow,
@@ -128,8 +124,8 @@
     }
 
     @Test
-    public void testForAllWindows_WithChildWindowImeTarget() throws Exception {
-        sWm.mInputMethodTarget = mChildAppWindowAbove;
+    public void testForAllWindows_WithChildWindowImeTarget() {
+        mWm.mInputMethodTarget = mChildAppWindowAbove;
 
         assertForAllWindowsOrder(Arrays.asList(
                 mWallpaperWindow,
@@ -144,8 +140,8 @@
     }
 
     @Test
-    public void testForAllWindows_WithStatusBarImeTarget() throws Exception {
-        sWm.mInputMethodTarget = mStatusBarWindow;
+    public void testForAllWindows_WithStatusBarImeTarget() {
+        mWm.mInputMethodTarget = mStatusBarWindow;
 
         assertForAllWindowsOrder(Arrays.asList(
                 mWallpaperWindow,
@@ -160,7 +156,7 @@
     }
 
     @Test
-    public void testForAllWindows_WithInBetweenWindowToken() throws Exception {
+    public void testForAllWindows_WithInBetweenWindowToken() {
         // This window is set-up to be z-ordered between some windows that go in the same token like
         // the nav bar and status bar.
         final WindowState voiceInteractionWindow = createWindow(null, TYPE_VOICE_INTERACTION,
@@ -180,7 +176,7 @@
     }
 
     @Test
-    public void testComputeImeTarget() throws Exception {
+    public void testComputeImeTarget() {
         // Verify that an app window can be an ime target.
         final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
         appWin.setHasSurface(true);
@@ -203,7 +199,7 @@
      * container references updates.
      */
     @Test
-    public void testMoveStackBetweenDisplays() throws Exception {
+    public void testMoveStackBetweenDisplays() {
         // Create a second display.
         final DisplayContent dc = createNewDisplay();
 
@@ -233,7 +229,7 @@
      * This tests override configuration updates for display content.
      */
     @Test
-    public void testDisplayOverrideConfigUpdate() throws Exception {
+    public void testDisplayOverrideConfigUpdate() {
         final int displayId = mDisplayContent.getDisplayId();
         final Configuration currentOverrideConfig = mDisplayContent.getOverrideConfiguration();
 
@@ -242,7 +238,7 @@
         newOverrideConfig.densityDpi += 120;
         newOverrideConfig.fontScale += 0.3;
 
-        sWm.setNewDisplayOverrideConfiguration(newOverrideConfig, displayId);
+        mWm.setNewDisplayOverrideConfiguration(newOverrideConfig, displayId);
 
         // Check that override config is applied.
         assertEquals(newOverrideConfig, mDisplayContent.getOverrideConfiguration());
@@ -252,7 +248,7 @@
      * This tests global configuration updates when default display config is updated.
      */
     @Test
-    public void testDefaultDisplayOverrideConfigUpdate() throws Exception {
+    public void testDefaultDisplayOverrideConfigUpdate() {
         final Configuration currentConfig = mDisplayContent.getConfiguration();
 
         // Create new, slightly changed override configuration and apply it to the display.
@@ -260,16 +256,16 @@
         newOverrideConfig.densityDpi += 120;
         newOverrideConfig.fontScale += 0.3;
 
-        sWm.setNewDisplayOverrideConfiguration(newOverrideConfig, DEFAULT_DISPLAY);
+        mWm.setNewDisplayOverrideConfiguration(newOverrideConfig, DEFAULT_DISPLAY);
 
         // Check that global configuration is updated, as we've updated default display's config.
-        Configuration globalConfig = sWm.mRoot.getConfiguration();
+        Configuration globalConfig = mWm.mRoot.getConfiguration();
         assertEquals(newOverrideConfig.densityDpi, globalConfig.densityDpi);
         assertEquals(newOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
 
         // Return back to original values.
-        sWm.setNewDisplayOverrideConfiguration(currentConfig, DEFAULT_DISPLAY);
-        globalConfig = sWm.mRoot.getConfiguration();
+        mWm.setNewDisplayOverrideConfiguration(currentConfig, DEFAULT_DISPLAY);
+        globalConfig = mWm.mRoot.getConfiguration();
         assertEquals(currentConfig.densityDpi, globalConfig.densityDpi);
         assertEquals(currentConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
     }
@@ -278,8 +274,8 @@
      * Tests tapping on a stack in different display results in window gaining focus.
      */
     @Test
-    public void testInputEventBringsCorrectDisplayInFocus() throws Exception {
-        DisplayContent dc0 = sWm.getDefaultDisplayContentLocked();
+    public void testInputEventBringsCorrectDisplayInFocus() {
+        DisplayContent dc0 = mWm.getDefaultDisplayContentLocked();
         // Create a second display
         final DisplayContent dc1 = createNewDisplay();
 
@@ -303,51 +299,51 @@
         // tap on primary display.
         tapOnDisplay(dc0);
         // Check focus is on primary display.
-        assertEquals(sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
+        assertEquals(mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
                 dc0.findFocusedWindow());
 
         // Tap on secondary display.
         tapOnDisplay(dc1);
         // Check focus is on secondary.
-        assertEquals(sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
+        assertEquals(mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
                 dc1.findFocusedWindow());
     }
 
     @Test
-    public void testFocusedWindowMultipleDisplays() throws Exception {
+    public void testFocusedWindowMultipleDisplays() {
         // Create a focusable window and check that focus is calculated correctly
         final WindowState window1 =
                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "window1");
         updateFocusedWindow();
         assertTrue(window1.isFocused());
-        assertEquals(window1, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
+        assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
 
         // Check that a new display doesn't affect focus
         final DisplayContent dc = createNewDisplay();
         updateFocusedWindow();
         assertTrue(window1.isFocused());
-        assertEquals(window1, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
+        assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
 
         // Add a window to the second display, and it should be focused
         final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, dc, "window2");
         updateFocusedWindow();
         assertTrue(window1.isFocused());
         assertTrue(window2.isFocused());
-        assertEquals(window2, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
+        assertEquals(window2, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
 
         // Move the first window to the to including parents, and make sure focus is updated
         window1.getParent().positionChildAt(POSITION_TOP, window1, true);
         updateFocusedWindow();
         assertTrue(window1.isFocused());
         assertTrue(window2.isFocused());
-        assertEquals(window1, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
+        assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
     }
 
     /**
      * This tests setting the maximum ui width on a display.
      */
     @Test
-    public void testMaxUiWidth() throws Exception {
+    public void testMaxUiWidth() {
         // Prevent base display metrics for test from being updated to the value of real display.
         final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo();
         final int baseWidth = 1440;
@@ -439,8 +435,8 @@
     }
 
     @Test
-    public void testDisplayCutout_rot0() throws Exception {
-        synchronized (sWm.getWindowManagerLock()) {
+    public void testDisplayCutout_rot0() {
+        synchronized (mWm.getWindowManagerLock()) {
             final DisplayContent dc = createNewDisplay();
             dc.mInitialDisplayWidth = 200;
             dc.mInitialDisplayHeight = 400;
@@ -458,8 +454,8 @@
     }
 
     @Test
-    public void testDisplayCutout_rot90() throws Exception {
-        synchronized (sWm.getWindowManagerLock()) {
+    public void testDisplayCutout_rot90() {
+        synchronized (mWm.getWindowManagerLock()) {
             // Prevent mInitialDisplayCutout from being updated from real display (e.g. null
             // if the device has no cutout).
             final DisplayContent dc = createDisplayNoUpdateDisplayInfo();
@@ -476,7 +472,8 @@
 
             final Rect r1 = new Rect(left, top, right, bottom);
             final DisplayCutout cutout = new WmDisplayCutout(
-                    fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP), null)
+                    fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP),
+                    null)
                     .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
 
             dc.mInitialDisplayCutout = cutout;
@@ -500,8 +497,8 @@
     }
 
     @Test
-    public void testLayoutSeq_assignedDuringLayout() throws Exception {
-        synchronized (sWm.getWindowManagerLock()) {
+    public void testLayoutSeq_assignedDuringLayout() {
+        synchronized (mWm.getWindowManagerLock()) {
 
             final DisplayContent dc = createNewDisplay();
             final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
@@ -533,7 +530,7 @@
         assertEquals("Visible keyguard must influence device orientation",
                 SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation());
 
-        sWm.setKeyguardGoingAway(true);
+        mWm.setKeyguardGoingAway(true);
         assertEquals("Keyguard that is going away must not influence device orientation",
                 SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
     }
@@ -543,10 +540,10 @@
         final DisplayContent dc = createNewDisplay();
 
         assertTrue(dc.mShouldOverrideDisplayConfiguration);
-        sWm.dontOverrideDisplayInfo(dc.getDisplayId());
+        mWm.dontOverrideDisplayInfo(dc.getDisplayId());
 
         assertFalse(dc.mShouldOverrideDisplayConfiguration);
-        verify(sWm.mDisplayManagerInternal, times(1))
+        verify(mWm.mDisplayManagerInternal, times(1))
                 .setDisplayInfoOverrideFromWindowManager(dc.getDisplayId(), null);
     }
 
@@ -572,7 +569,7 @@
     }
 
     private boolean isOptionsPanelAtRight(int displayId) {
-        return (sWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
+        return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
     }
 
     private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth,
@@ -583,8 +580,8 @@
     }
 
     private void updateFocusedWindow() {
-        synchronized (sWm.mGlobalLock) {
-            sWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false);
+        synchronized (mWm.mGlobalLock) {
+            mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false);
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
index 3be1258..f0c49c8 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
@@ -12,11 +12,12 @@
  * 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.server.wm;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -31,15 +32,13 @@
 import android.view.DisplayInfo;
 import android.view.Surface;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.policy.WindowManagerPolicy;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.io.File;
 
@@ -47,14 +46,13 @@
  * Tests for the {@link DisplaySettings} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.DisplaySettingsTests
+ *  atest FrameworksServicesTests:DisplaySettingsTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class DisplaySettingsTests extends WindowTestsBase {
 
-    private File mTestFolder;
+    private static final File TEST_FOLDER = getInstrumentation().getTargetContext().getCacheDir();
     private DisplaySettings mTarget;
 
     private DisplayContent mPrimaryDisplay;
@@ -62,21 +60,23 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
+        deleteRecursively(TEST_FOLDER);
 
-        mTestFolder = InstrumentationRegistry.getContext().getCacheDir();
-        deleteRecursively(mTestFolder);
+        mWm.setSupportsFreeformWindowManagement(false);
+        mWm.setIsPc(false);
 
-        sWm.setSupportsFreeformWindowManagement(false);
-        sWm.setIsPc(false);
+        mTarget = new DisplaySettings(mWm, TEST_FOLDER);
 
-        mTarget = new DisplaySettings(sWm, mTestFolder);
-
-        mPrimaryDisplay = sWm.getDefaultDisplayContentLocked();
+        mPrimaryDisplay = mWm.getDefaultDisplayContentLocked();
         mSecondaryDisplay = mDisplayContent;
         assertNotEquals(Display.DEFAULT_DISPLAY, mSecondaryDisplay.getDisplayId());
     }
 
+    @After
+    public void tearDown() {
+        deleteRecursively(TEST_FOLDER);
+    }
+
     @Test
     public void testPrimaryDisplayDefaultToFullscreenWithoutFreeformSupport() {
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
@@ -87,7 +87,7 @@
 
     @Test
     public void testPrimaryDisplayDefaultToFullscreenWithFreeformSupportNonPc() {
-        sWm.setSupportsFreeformWindowManagement(true);
+        mWm.setSupportsFreeformWindowManagement(true);
 
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
 
@@ -97,8 +97,8 @@
 
     @Test
     public void testPrimaryDisplayDefaultToFreeformWithFreeformIsPc() {
-        sWm.setSupportsFreeformWindowManagement(true);
-        sWm.setIsPc(true);
+        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.setIsPc(true);
 
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
 
@@ -116,7 +116,7 @@
 
     @Test
     public void testSecondaryDisplayDefaultToFreeformWithFreeformSupportNonPc() {
-        sWm.setSupportsFreeformWindowManagement(true);
+        mWm.setSupportsFreeformWindowManagement(true);
 
         mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
 
@@ -126,8 +126,8 @@
 
     @Test
     public void testSecondaryDisplayDefaultToFreeformWithFreeformSupportIsPc() {
-        sWm.setSupportsFreeformWindowManagement(true);
-        sWm.setIsPc(true);
+        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.setIsPc(true);
 
         mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
 
@@ -157,7 +157,7 @@
         doAnswer(invocation -> {
             ((DisplayInfo) invocation.getArguments()[1]).copyFrom(originalInfo);
             return null;
-        }).when(sWm.mDisplayManagerInternal).getNonOverrideDisplayInfo(anyInt(), any());
+        }).when(mWm.mDisplayManagerInternal).getNonOverrideDisplayInfo(anyInt(), any());
 
         mTarget.setForcedSize(mSecondaryDisplay, 1000 /* width */, 2000 /* height */);
         applySettingsToDisplayByNewInstance(mSecondaryDisplay);
@@ -165,7 +165,7 @@
         assertEquals(1000 /* width */, mSecondaryDisplay.mBaseDisplayWidth);
         assertEquals(2000 /* height */, mSecondaryDisplay.mBaseDisplayHeight);
 
-        sWm.clearForcedDisplaySize(mSecondaryDisplay.getDisplayId());
+        mWm.clearForcedDisplaySize(mSecondaryDisplay.getDisplayId());
         assertEquals(mSecondaryDisplay.mInitialDisplayWidth, mSecondaryDisplay.mBaseDisplayWidth);
         assertEquals(mSecondaryDisplay.mInitialDisplayHeight, mSecondaryDisplay.mBaseDisplayHeight);
     }
@@ -177,7 +177,7 @@
 
         assertEquals(600 /* density */, mSecondaryDisplay.mBaseDisplayDensity);
 
-        sWm.clearForcedDisplayDensityForUser(mSecondaryDisplay.getDisplayId(), 0 /* userId */);
+        mWm.clearForcedDisplayDensityForUser(mSecondaryDisplay.getDisplayId(), 0 /* userId */);
         assertEquals(mSecondaryDisplay.mInitialDisplayDensity,
                 mSecondaryDisplay.mBaseDisplayDensity);
     }
@@ -189,7 +189,7 @@
 
         assertTrue(mSecondaryDisplay.mDisplayScalingDisabled);
 
-        sWm.setForcedDisplayScalingMode(mSecondaryDisplay.getDisplayId(),
+        mWm.setForcedDisplayScalingMode(mSecondaryDisplay.getDisplayId(),
                 DisplayContent.FORCE_SCALING_MODE_AUTO);
         assertFalse(mSecondaryDisplay.mDisplayScalingDisabled);
     }
@@ -298,20 +298,20 @@
      * that also means the previous state must be written correctly.
      */
     private void applySettingsToDisplayByNewInstance(DisplayContent display) {
-        new DisplaySettings(sWm, mTestFolder).applySettingsToDisplayLocked(display);
+        new DisplaySettings(mWm, TEST_FOLDER).applySettingsToDisplayLocked(display);
     }
 
     private static boolean deleteRecursively(File file) {
+        boolean fullyDeleted = true;
         if (file.isFile()) {
             return file.delete();
+        } else if (file.isDirectory()) {
+            final File[] files = file.listFiles();
+            for (File child : files) {
+                fullyDeleted &= deleteRecursively(child);
+            }
+            fullyDeleted &= file.delete();
         }
-
-        boolean fullyDeleted = true;
-        final File[] files = file.listFiles();
-        for (File child : files) {
-            fullyDeleted &= deleteRecursively(child);
-        }
-        fullyDeleted &= file.delete();
         return fullyDeleted;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
index 31b2fef..55e766d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -40,23 +40,24 @@
 import android.view.SurfaceSession;
 import android.view.View;
 
-import com.android.internal.annotations.GuardedBy;
+import androidx.test.filters.SmallTest;
+
 import com.android.server.LocalServices;
 
 import org.junit.After;
+import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import androidx.test.filters.SmallTest;
-
 /**
  * Tests for the {@link DragDropController} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.DragDropControllerTests
+ *  atest FrameworksServicesTests:DragDropControllerTests
  */
 @SmallTest
 @Presubmit
@@ -67,7 +68,6 @@
     private IBinder mToken;
 
     static class TestDragDropController extends DragDropController {
-        @GuardedBy("sWm.mWindowMap")
         private Runnable mCloseCallback;
 
         TestDragDropController(WindowManagerService service, Looper looper) {
@@ -107,31 +107,34 @@
         return window;
     }
 
-    @Override
-    @Before
-    public void setUp() throws Exception {
+    @BeforeClass
+    public static void setUpOnce() {
         final UserManagerInternal userManager = mock(UserManagerInternal.class);
         LocalServices.addService(UserManagerInternal.class, userManager);
+    }
 
-        super.setUp();
+    @AfterClass
+    public static void tearDownOnce() {
+        LocalServices.removeServiceForTest(UserManagerInternal.class);
+    }
 
-        mTarget = new TestDragDropController(sWm, sWm.mH.getLooper());
+    @Before
+    public void setUp() throws Exception {
+        mTarget = new TestDragDropController(mWm, mWm.mH.getLooper());
         mDisplayContent = spy(mDisplayContent);
         mWindow = createDropTargetWindow("Drag test window", 0);
         doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0);
-        when(sWm.mInputManager.transferTouchFocus(any(), any())).thenReturn(true);
+        when(mWm.mInputManager.transferTouchFocus(any(), any())).thenReturn(true);
 
-        synchronized (sWm.mGlobalLock) {
-            sWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
+        synchronized (mWm.mGlobalLock) {
+            mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
         }
     }
 
-    @Override
     @After
     public void tearDown() throws Exception {
-        LocalServices.removeServiceForTest(UserManagerInternal.class);
         final CountDownLatch latch;
-        synchronized (sWm.mGlobalLock) {
+        synchronized (mWm.mGlobalLock) {
             if (!mTarget.dragDropActiveLocked()) {
                 return;
             }
@@ -142,8 +145,6 @@
             mTarget.setOnClosedCallbackLocked(latch::countDown);
         }
         assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-
-        super.tearDown();
     }
 
     @Test
@@ -174,7 +175,7 @@
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
 
-            assertTrue(sWm.mInputManager.transferTouchFocus(null, null));
+            assertTrue(mWm.mInputManager.transferTouchFocus(null, null));
             mToken = mTarget.performDrag(
                     new SurfaceSession(), 0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0,
                     data);
diff --git a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
index 7222a99..1fae317 100644
--- a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.server.wm;
 
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -16,17 +32,18 @@
 import android.view.IPinnedStackListener;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+/**
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:PinnedStackControllerTest
+ */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class PinnedStackControllerTest extends WindowTestsBase {
 
     @Mock private IPinnedStackListener mIPinnedStackListener;
@@ -34,15 +51,15 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
         MockitoAnnotations.initMocks(this);
+
         when(mIPinnedStackListener.asBinder()).thenReturn(mIPinnedStackListenerStub);
     }
 
     @Test
     public void setShelfHeight_shelfVisibilityChangedTriggered() throws RemoteException {
-        sWm.mSupportsPictureInPicture = true;
-        sWm.registerPinnedStackListener(DEFAULT_DISPLAY, mIPinnedStackListener);
+        mWm.mSupportsPictureInPicture = true;
+        mWm.registerPinnedStackListener(DEFAULT_DISPLAY, mIPinnedStackListener);
 
         verify(mIPinnedStackListener).onImeVisibilityChanged(false, 0);
         verify(mIPinnedStackListener).onShelfVisibilityChanged(false, 0);
@@ -55,7 +72,7 @@
 
         final int SHELF_HEIGHT = 300;
 
-        sWm.setShelfHeight(true, SHELF_HEIGHT);
+        mWm.setShelfHeight(true, SHELF_HEIGHT);
         verify(mIPinnedStackListener).onShelfVisibilityChanged(true, SHELF_HEIGHT);
         verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false),
                 eq(true), anyInt());
diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 088e229..fe5fc06 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -24,9 +24,9 @@
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
 
-import static org.junit.Assert.fail;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.verify;
@@ -42,22 +42,20 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 /**
- * atest FrameworksServicesTests:com.android.server.wm.RecentsAnimationControllerTest
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:RecentsAnimationControllerTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class RecentsAnimationControllerTest extends WindowTestsBase {
 
     @Mock SurfaceControl mMockLeash;
@@ -69,10 +67,10 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
         MockitoAnnotations.initMocks(this);
+
         when(mMockRunner.asBinder()).thenReturn(new Binder());
-        mController = new RecentsAnimationController(sWm, mMockRunner, mAnimationCallbacks,
+        mController = new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks,
                 DEFAULT_DISPLAY);
     }
 
@@ -96,7 +94,7 @@
     }
 
     @Test
-    public void testCancelAfterRemove_expectIgnored() throws Exception {
+    public void testCancelAfterRemove_expectIgnored() {
         final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         AnimationAdapter adapter = mController.addAnimation(appWindow.getTask(),
@@ -114,10 +112,10 @@
         }
     }
 
-    @Test
     @FlakyTest(bugId = 117117823)
-    public void testIncludedApps_expectTargetAndVisible() throws Exception {
-        sWm.setRecentsAnimationController(mController);
+    @Test
+    public void testIncludedApps_expectTargetAndVisible() {
+        mWm.setRecentsAnimationController(mController);
         final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
         final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index ae92984..fa53795 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -39,7 +39,6 @@
 import android.view.SurfaceControl.Transaction;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.testutils.OffsettableClock;
 import com.android.server.testutils.TestHandler;
@@ -47,17 +46,16 @@
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 /**
- * atest FrameworksServicesTests:com.android.server.wm.RemoteAnimationControllerTest
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:RemoteAnimationControllerTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class RemoteAnimationControllerTest extends WindowTestsBase {
 
     @Mock SurfaceControl mMockLeash;
@@ -71,15 +69,13 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
         MockitoAnnotations.initMocks(this);
+
         when(mMockRunner.asBinder()).thenReturn(new Binder());
         mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50);
         mAdapter.setCallingPid(123);
-        sWm.mH.runWithScissors(() -> {
-            mHandler = new TestHandler(null, mClock);
-        }, 0);
-        mController = new RemoteAnimationController(sWm, mAdapter, mHandler);
+        mWm.mH.runWithScissors(() -> mHandler = new TestHandler(null, mClock), 0);
+        mController = new RemoteAnimationController(mWm, mAdapter, mHandler);
     }
 
     @Test
@@ -91,7 +87,7 @@
                     new Point(50, 100), new Rect(50, 100, 150, 150));
             adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
             mController.goodToGo();
-            sWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
@@ -146,7 +142,7 @@
 
     @Test
     public void testTimeout_scaled() throws Exception {
-        sWm.setAnimationScale(2, 5.0f);
+        mWm.setAnimationScale(2, 5.0f);
         try{
             final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
             final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
@@ -165,19 +161,19 @@
             verify(mMockRunner).onAnimationCancelled();
             verify(mFinishedCallback).onAnimationFinished(eq(adapter));
         } finally {
-            sWm.setAnimationScale(2, 1.0f);
+            mWm.setAnimationScale(2, 1.0f);
         }
 
     }
 
     @Test
-    public void testZeroAnimations() throws Exception {
+    public void testZeroAnimations() {
         mController.goodToGo();
         verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
     }
 
     @Test
-    public void testNotReallyStarted() throws Exception {
+    public void testNotReallyStarted() {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         mController.createAnimationAdapter(win.mAppToken,
                 new Point(50, 100), new Rect(50, 100, 150, 150));
@@ -195,7 +191,7 @@
                 new Point(50, 100), new Rect(50, 100, 150, 150));
         adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
         mController.goodToGo();
-        sWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
         final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                 ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
         final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
@@ -206,7 +202,7 @@
     }
 
     @Test
-    public void testRemovedBeforeStarted() throws Exception {
+    public void testRemovedBeforeStarted() {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
                 new Point(50, 100), new Rect(50, 100, 150, 150));
diff --git a/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
index 001bed9..62a617d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -1,30 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.server.wm;
 
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
 
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link RootWindowContainer} class.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.RootWindowContainerTests
+ *  atest FrameworksServicesTests:RootWindowContainerTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class RootWindowContainerTests extends WindowTestsBase {
     @Test
-    public void testSetDisplayOverrideConfigurationIfNeeded() throws Exception {
-        synchronized (sWm.mGlobalLock) {
+    public void testSetDisplayOverrideConfigurationIfNeeded() {
+        synchronized (mWm.mGlobalLock) {
             // Add first stack we expect to be updated with configuration change.
             final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
             stack.getOverrideConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 5, 5));
@@ -41,12 +54,12 @@
             override.windowConfiguration.setBounds(new Rect(0, 0, 10, 10));
 
             // Set display override.
-            final int[] results = sWm.mRoot.setDisplayOverrideConfigurationIfNeeded(override,
+            final int[] results = mWm.mRoot.setDisplayOverrideConfigurationIfNeeded(override,
                     mDisplayContent.getDisplayId());
 
             // Ensure only first stack is returned.
-            assertTrue(results.length == 1);
-            assertTrue(results[0] == stack.mStackId);
+            assertEquals(1, results.length);
+            assertEquals(stack.mStackId, results[0]);
         }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
index 9f2645c..ce5b13c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -25,10 +25,8 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link StackWindowController}.
@@ -38,10 +36,9 @@
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class StackWindowControllerTests extends WindowTestsBase {
     @Test
-    public void testRemoveContainer() throws Exception {
+    public void testRemoveContainer() {
         final StackWindowController stackController =
                 createStackControllerOnDisplay(mDisplayContent);
         final WindowTestUtils.TestTaskWindowContainerController taskController =
@@ -61,7 +58,7 @@
     }
 
     @Test
-    public void testRemoveContainer_deferRemoval() throws Exception {
+    public void testRemoveContainer_deferRemoval() {
         final StackWindowController stackController =
                 createStackControllerOnDisplay(mDisplayContent);
         final WindowTestUtils.TestTaskWindowContainerController taskController =
@@ -87,7 +84,7 @@
     }
 
     @Test
-    public void testReparent() throws Exception {
+    public void testReparent() {
         // Create first stack on primary display.
         final StackWindowController stack1Controller =
                 createStackControllerOnDisplay(mDisplayContent);
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index 4551c36..584f269 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -27,6 +27,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import static java.util.concurrent.TimeUnit.SECONDS;
+
 import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
 import android.animation.ValueAnimator;
 import android.graphics.Matrix;
@@ -42,30 +44,27 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-import static java.util.concurrent.TimeUnit.SECONDS;
-
 import java.util.concurrent.CountDownLatch;
 
 /**
  * Test class for {@link SurfaceAnimationRunner}.
  *
- * atest FrameworksServicesTests:com.android.server.wm.SurfaceAnimationRunnerTest
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:SurfaceAnimationRunnerTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class SurfaceAnimationRunnerTest extends WindowTestsBase {
 
     @Mock SurfaceControl mMockSurface;
@@ -79,7 +78,8 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
+        MockitoAnnotations.initMocks(this);
+
         mFinishCallbackLatch = new CountDownLatch(1);
         mSurfaceAnimationRunner = new SurfaceAnimationRunner(null /* callbackProvider */, null,
                 mMockTransaction, mMockPowerManager);
@@ -112,7 +112,7 @@
     }
 
     @Test
-    public void testCancel_notStarted() throws Exception {
+    public void testCancel_notStarted() {
         mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
                 mMockTransaction, mMockPowerManager);
         mSurfaceAnimationRunner
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 7b5e8de..6833dc5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -35,14 +35,13 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.SurfaceAnimator.Animatable;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -50,11 +49,11 @@
 /**
  * Test class for {@link SurfaceAnimatorTest}.
  *
- * atest FrameworksServicesTests:com.android.server.wm.SurfaceAnimatorTest
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:SurfaceAnimatorTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class SurfaceAnimatorTest extends WindowTestsBase {
 
     @Mock AnimationAdapter mSpec;
@@ -68,15 +67,24 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
         MockitoAnnotations.initMocks(this);
-        mAnimatable = new MyAnimatable();
-        mAnimatable2 = new MyAnimatable();
-        mDeferFinishAnimatable = new DeferFinishAnimatable();
+
+        mAnimatable = new MyAnimatable(mWm, mSession, mTransaction);
+        mAnimatable2 = new MyAnimatable(mWm, mSession, mTransaction);
+        mDeferFinishAnimatable = new DeferFinishAnimatable(mWm, mSession, mTransaction);
+    }
+
+    @After
+    public void tearDown() {
+        mAnimatable = null;
+        mAnimatable2 = null;
+        mDeferFinishAnimatable = null;
+        mSession.kill();
+        mSession = null;
     }
 
     @Test
-    public void testRunAnimation() throws Exception {
+    public void testRunAnimation() {
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
                 OnAnimationFinishedCallback.class);
@@ -92,7 +100,7 @@
     }
 
     @Test
-    public void testOverrideAnimation() throws Exception {
+    public void testOverrideAnimation() {
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         final SurfaceControl firstLeash = mAnimatable.mLeash;
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */);
@@ -117,7 +125,7 @@
     }
 
     @Test
-    public void testCancelAnimation() throws Exception {
+    public void testCancelAnimation() {
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         assertAnimating(mAnimatable);
         mAnimatable.mSurfaceAnimator.cancelAnimation();
@@ -128,7 +136,7 @@
     }
 
     @Test
-    public void testDelayingAnimationStart() throws Exception {
+    public void testDelayingAnimationStart() {
         mAnimatable.mSurfaceAnimator.startDelayingAnimationStart();
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         verifyZeroInteractions(mSpec);
@@ -139,7 +147,7 @@
     }
 
     @Test
-    public void testDelayingAnimationStartAndCancelled() throws Exception {
+    public void testDelayingAnimationStartAndCancelled() {
         mAnimatable.mSurfaceAnimator.startDelayingAnimationStart();
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         mAnimatable.mSurfaceAnimator.cancelAnimation();
@@ -150,7 +158,7 @@
     }
 
     @Test
-    public void testTransferAnimation() throws Exception {
+    public void testTransferAnimation() {
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
 
         final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
@@ -171,7 +179,7 @@
 
     @Test
     @FlakyTest(detail = "Promote once confirmed non-flaky")
-    public void testDeferFinish() throws Exception {
+    public void testDeferFinish() {
 
         // Start animation
         mDeferFinishAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec,
@@ -186,7 +194,7 @@
         assertAnimating(mDeferFinishAnimatable);
 
         // Now end defer finishing.
-        mDeferFinishAnimatable.endDeferFinishCallback.run();
+        mDeferFinishAnimatable.mEndDeferFinishCallback.run();
         assertNotAnimating(mAnimatable2);
         assertTrue(mDeferFinishAnimatable.mFinishedCallbackCalled);
         verify(mTransaction).destroy(eq(mDeferFinishAnimatable.mLeash));
@@ -202,26 +210,30 @@
         assertNull(animatable.mSurfaceAnimator.getAnimation());
     }
 
-    private class MyAnimatable implements Animatable {
+    private static class MyAnimatable implements Animatable {
 
+        private final SurfaceSession mSession;
+        private final Transaction mTransaction;
         final SurfaceControl mParent;
         final SurfaceControl mSurface;
         final SurfaceAnimator mSurfaceAnimator;
         SurfaceControl mLeash;
         boolean mFinishedCallbackCalled;
 
-        MyAnimatable() {
-            mParent = sWm.makeSurfaceBuilder(mSession)
+        MyAnimatable(WindowManagerService wm, SurfaceSession session, Transaction transaction) {
+            mSession = session;
+            mTransaction = transaction;
+            mParent = wm.makeSurfaceBuilder(mSession)
                     .setName("test surface parent")
                     .setSize(3000, 3000)
                     .build();
-            mSurface = sWm.makeSurfaceBuilder(mSession)
+            mSurface = wm.makeSurfaceBuilder(mSession)
                     .setName("test surface")
                     .setSize(1, 1)
                     .build();
             mFinishedCallbackCalled = false;
             mLeash = null;
-            mSurfaceAnimator = new SurfaceAnimator(this, mFinishedCallback, sWm);
+            mSurfaceAnimator = new SurfaceAnimator(this, mFinishedCallback, wm);
         }
 
         @Override
@@ -281,13 +293,18 @@
         private final Runnable mFinishedCallback = () -> mFinishedCallbackCalled = true;
     }
 
-    private class DeferFinishAnimatable extends MyAnimatable {
+    private static class DeferFinishAnimatable extends MyAnimatable {
 
-        Runnable endDeferFinishCallback;
+        Runnable mEndDeferFinishCallback;
+
+        DeferFinishAnimatable(WindowManagerService wm, SurfaceSession session,
+                Transaction transaction) {
+            super(wm, session, transaction);
+        }
 
         @Override
         public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
-            this.endDeferFinishCallback = endDeferFinishCallback;
+            mEndDeferFinishCallback = endDeferFinishCallback;
             return true;
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
index 17b7fc1..785b955 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -35,24 +35,22 @@
 import android.view.Display;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
 /**
  * Tests for the {@link TaskPositioner} class.
  *
- * runtest frameworks-services -c com.android.server.wm.TaskPositionerTests
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:TaskPositionerTests
  */
 @SmallTest
-@RunWith(AndroidJUnit4.class)
 public class TaskPositionerTests extends WindowTestsBase {
 
-    private final boolean DEBUGGING = false;
-    private final String TAG = "TaskPositionerTest";
+    private static final boolean DEBUGGING = false;
+    private static final String TAG = "TaskPositionerTest";
 
     private final static int MOUSE_DELTA_X = 5;
     private final static int MOUSE_DELTA_Y = 5;
@@ -65,8 +63,6 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
-
         TaskPositioner.setFactory(null);
 
         final Display display = mDisplayContent.getDisplay();
@@ -77,7 +73,7 @@
         mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, dm);
         mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
 
-        mPositioner = new TaskPositioner(sWm, Mockito.mock(IActivityTaskManager.class));
+        mPositioner = new TaskPositioner(mWm, Mockito.mock(IActivityTaskManager.class));
         mPositioner.register(mDisplayContent);
 
         mWindow = Mockito.spy(createWindow(null, TYPE_BASE_APPLICATION, "window"));
@@ -94,7 +90,7 @@
     }
 
     @Test
-    public void testOverrideFactory() throws Exception {
+    public void testOverrideFactory() {
         final boolean[] created = new boolean[1];
         created[0] = false;
         TaskPositioner.setFactory(new TaskPositioner.Factory() {
@@ -105,7 +101,7 @@
             }
         });
 
-        assertNull(TaskPositioner.create(sWm));
+        assertNull(TaskPositioner.create(mWm));
         assertTrue(created[0]);
     }
 
@@ -114,7 +110,7 @@
      * as does some basic tests (e.g. dragging in Y only will keep X stable).
      */
     @Test
-    public void testBasicFreeWindowResizing() throws Exception {
+    public void testBasicFreeWindowResizing() {
         final Rect r = new Rect(100, 220, 700, 520);
         final int midY = (r.top + r.bottom) / 2;
         mDimBounds.set(r);
@@ -175,7 +171,7 @@
      * This tests that by dragging any edge, the fixed / opposite edge(s) remains anchored.
      */
     @Test
-    public void testFreeWindowResizingTestAllEdges() throws Exception {
+    public void testFreeWindowResizingTestAllEdges() {
         final Rect r = new Rect(100, 220, 700, 520);
         final int midX = (r.left + r.right) / 2;
         final int midY = (r.top + r.bottom) / 2;
@@ -260,7 +256,7 @@
      * right things upon resizing when dragged from the top left corner.
      */
     @Test
-    public void testLandscapePreservedWindowResizingDragTopLeft() throws Exception {
+    public void testLandscapePreservedWindowResizingDragTopLeft() {
         final Rect r = new Rect(100, 220, 700, 520);
         mDimBounds.set(r);
 
@@ -298,7 +294,7 @@
      * right things upon resizing when dragged from the left corner.
      */
     @Test
-    public void testLandscapePreservedWindowResizingDragLeft() throws Exception {
+    public void testLandscapePreservedWindowResizingDragLeft() {
         final Rect r = new Rect(100, 220, 700, 520);
         final int midY = (r.top + r.bottom) / 2;
         mDimBounds.set(r);
@@ -339,7 +335,7 @@
      * right things upon resizing when dragged from the top corner.
      */
     @Test
-    public void testLandscapePreservedWindowResizingDragTop() throws Exception {
+    public void testLandscapePreservedWindowResizingDragTop() {
         final Rect r = new Rect(100, 220, 700, 520);
         final int midX = (r.left + r.right) / 2;
         mDimBounds.set(r);
@@ -376,7 +372,7 @@
      * right things upon resizing when dragged from the top left corner.
      */
     @Test
-    public void testPortraitPreservedWindowResizingDragTopLeft() throws Exception {
+    public void testPortraitPreservedWindowResizingDragTopLeft() {
         final Rect r = new Rect(330, 100, 630, 600);
         mDimBounds.set(r);
 
@@ -409,7 +405,7 @@
      * right things upon resizing when dragged from the left corner.
      */
     @Test
-    public void testPortraitPreservedWindowResizingDragLeft() throws Exception {
+    public void testPortraitPreservedWindowResizingDragLeft() {
         final Rect r = new Rect(330, 100, 630, 600);
         final int midY = (r.top + r.bottom) / 2;
         mDimBounds.set(r);
@@ -452,7 +448,7 @@
      * right things upon resizing when dragged from the top corner.
      */
     @Test
-    public void testPortraitPreservedWindowResizingDragTop() throws Exception {
+    public void testPortraitPreservedWindowResizingDragTop() {
         final Rect r = new Rect(330, 100, 630, 600);
         final int midX = (r.left + r.right) / 2;
         mDimBounds.set(r);
@@ -485,7 +481,7 @@
                 mPositioner.getWindowDragBounds());
     }
 
-    private void assertBoundsEquals(Rect expected, Rect actual) {
+    private static void assertBoundsEquals(Rect expected, Rect actual) {
         if (DEBUGGING) {
             if (!expected.equals(actual)) {
                 Log.e(TAG, "rect(" + actual.toString() + ") != isRect(" + actual.toString()
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
index d1480c5..00b4629 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -32,70 +32,66 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link TaskPositioningController} class.
  *
- * atest com.android.server.wm.TaskPositioningControllerTests
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:TaskPositioningControllerTests
  */
-@SmallTest
 @FlakyTest(bugId = 117924387)
-@RunWith(AndroidJUnit4.class)
+@SmallTest
 @Presubmit
 public class TaskPositioningControllerTests extends WindowTestsBase {
     private static final int TIMEOUT_MS = 1000;
+
     private TaskPositioningController mTarget;
     private WindowState mWindow;
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
+        assertNotNull(mWm.mTaskPositioningController);
+        mTarget = mWm.mTaskPositioningController;
 
-        assertNotNull(sWm.mTaskPositioningController);
-        mTarget = sWm.mTaskPositioningController;
-
-        when(sWm.mInputManager.transferTouchFocus(
+        when(mWm.mInputManager.transferTouchFocus(
                 any(InputChannel.class),
                 any(InputChannel.class))).thenReturn(true);
 
         mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
         mWindow.mInputChannel = new InputChannel();
-        synchronized (sWm.mGlobalLock) {
-            sWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
+        synchronized (mWm.mGlobalLock) {
+            mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
         }
     }
 
     @Test
-    public void testStartAndFinishPositioning() throws Exception {
-        synchronized (sWm.mGlobalLock) {
+    public void testStartAndFinishPositioning() {
+        synchronized (mWm.mGlobalLock) {
             assertFalse(mTarget.isPositioningLocked());
             assertNull(mTarget.getDragWindowHandleLocked());
         }
 
         assertTrue(mTarget.startMovingTask(mWindow.mClient, 0, 0));
 
-        synchronized (sWm.mGlobalLock) {
+        synchronized (mWm.mGlobalLock) {
             assertTrue(mTarget.isPositioningLocked());
             assertNotNull(mTarget.getDragWindowHandleLocked());
         }
 
         mTarget.finishTaskPositioning();
         // Wait until the looper processes finishTaskPositioning.
-        assertTrue(sWm.mH.runWithScissors(() -> {}, TIMEOUT_MS));
+        assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS));
 
         assertFalse(mTarget.isPositioningLocked());
         assertNull(mTarget.getDragWindowHandleLocked());
     }
 
     @Test
-    public void testHandleTapOutsideTask() throws Exception {
-        synchronized (sWm.mGlobalLock) {
-
+    public void testHandleTapOutsideTask() {
+        synchronized (mWm.mGlobalLock) {
             assertFalse(mTarget.isPositioningLocked());
             assertNull(mTarget.getDragWindowHandleLocked());
         }
@@ -106,16 +102,16 @@
 
         mTarget.handleTapOutsideTask(content, 0, 0);
         // Wait until the looper processes finishTaskPositioning.
-        assertTrue(sWm.mH.runWithScissors(() -> {}, TIMEOUT_MS));
+        assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS));
 
-        synchronized (sWm.mGlobalLock) {
+        synchronized (mWm.mGlobalLock) {
             assertTrue(mTarget.isPositioningLocked());
             assertNotNull(mTarget.getDragWindowHandleLocked());
         }
 
         mTarget.finishTaskPositioning();
         // Wait until the looper processes finishTaskPositioning.
-        assertTrue(sWm.mH.runWithScissors(() -> {}, TIMEOUT_MS));
+        assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS));
 
         assertFalse(mTarget.isPositioningLocked());
         assertNull(mTarget.getDragWindowHandleLocked());
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
index c9d8004..1c6afd54 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -24,32 +24,32 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link TaskSnapshotCache}.
  *
- * runtest frameworks-services -c com.android.server.wm.TaskSnapshotCacheTest
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:TaskSnapshotCacheTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
 
     private TaskSnapshotCache mCache;
 
+    @Override
     @Before
-    public void setUp() throws Exception {
+    public void setUp() {
         super.setUp();
-        mCache = new TaskSnapshotCache(sWm, mLoader);
+
+        mCache = new TaskSnapshotCache(mWm, mLoader);
     }
 
     @Test
-    public void testAppRemoved() throws Exception {
+    public void testAppRemoved() {
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
         mCache.putSnapshot(window.getTask(), createSnapshot());
         assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
@@ -60,7 +60,7 @@
     }
 
     @Test
-    public void testAppDied() throws Exception {
+    public void testAppDied() {
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
         mCache.putSnapshot(window.getTask(), createSnapshot());
         assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
@@ -71,7 +71,7 @@
     }
 
     @Test
-    public void testTaskRemoved() throws Exception {
+    public void testTaskRemoved() {
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
         mCache.putSnapshot(window.getTask(), createSnapshot());
         assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */,
@@ -82,32 +82,32 @@
     }
 
     @Test
-    public void testReduced_notCached() throws Exception {
+    public void testReduced_notCached() {
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
-        mPersister.persistSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId, createSnapshot());
+        mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot());
         mPersister.waitForQueueEmpty();
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId,
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
                 false /* restoreFromDisk */, false /* reducedResolution */));
 
         // Load it from disk
-        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId,
+        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
                 true /* restoreFromDisk */, true /* reducedResolution */));
 
         // Make sure it's not in the cache now.
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId,
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
                 false /* restoreFromDisk */, false /* reducedResolution */));
     }
 
     @Test
-    public void testRestoreFromDisk() throws Exception {
+    public void testRestoreFromDisk() {
         final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
-        mPersister.persistSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId, createSnapshot());
+        mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot());
         mPersister.waitForQueueEmpty();
-        assertNull(mCache.getSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId,
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
                 false /* restoreFromDisk */, false /* reducedResolution */));
 
         // Load it from disk
-        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, sWm.mCurrentUserId,
+        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
                 true /* restoreFromDisk */, false /* reducedResolution */));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index efce063..d2c0765 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -20,7 +20,8 @@
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
 import static android.view.WindowManager.TRANSIT_UNSET;
 
-import static com.android.server.wm.TaskSnapshotController.*;
+import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_APP_THEME;
+import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_REAL;
 
 import static junit.framework.Assert.assertEquals;
 
@@ -28,25 +29,23 @@
 import android.util.ArraySet;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.google.android.collect.Sets;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link TaskSnapshotController}.
  *
- * runtest frameworks-services -c com.android.server.wm.TaskSnapshotControllerTest
+ * Build/Install/Run:
+ *  *  atest FrameworksServicesTests:TaskSnapshotControllerTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class TaskSnapshotControllerTest extends WindowTestsBase {
 
     @Test
-    public void testGetClosingApps_closing() throws Exception {
+    public void testGetClosingApps_closing() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
         closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
@@ -54,13 +53,13 @@
         final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mAppToken);
         final ArraySet<Task> closingTasks = new ArraySet<>();
-        sWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
+        mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
         assertEquals(1, closingTasks.size());
         assertEquals(closingWindow.mAppToken.getTask(), closingTasks.valueAt(0));
     }
 
     @Test
-    public void testGetClosingApps_notClosing() throws Exception {
+    public void testGetClosingApps_notClosing() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
         final WindowState openingWindow = createAppWindow(closingWindow.getTask(),
@@ -72,12 +71,12 @@
         final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mAppToken);
         final ArraySet<Task> closingTasks = new ArraySet<>();
-        sWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
+        mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
         assertEquals(0, closingTasks.size());
     }
 
     @Test
-    public void testGetClosingApps_skipClosingAppsSnapshotTasks() throws Exception {
+    public void testGetClosingApps_skipClosingAppsSnapshotTasks() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
         closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
@@ -85,29 +84,29 @@
         final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mAppToken);
         final ArraySet<Task> closingTasks = new ArraySet<>();
-        sWm.mTaskSnapshotController.addSkipClosingAppSnapshotTasks(
+        mWm.mTaskSnapshotController.addSkipClosingAppSnapshotTasks(
                 Sets.newArraySet(closingWindow.mAppToken.getTask()));
-        sWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
+        mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
         assertEquals(0, closingTasks.size());
     }
 
     @Test
-    public void testGetSnapshotMode() throws Exception {
+    public void testGetSnapshotMode() {
         final WindowState disabledWindow = createWindow(null,
                 FIRST_APPLICATION_WINDOW, mDisplayContent, "disabledWindow");
         disabledWindow.mAppToken.setDisablePreviewScreenshots(true);
         assertEquals(SNAPSHOT_MODE_APP_THEME,
-                sWm.mTaskSnapshotController.getSnapshotMode(disabledWindow.getTask()));
+                mWm.mTaskSnapshotController.getSnapshotMode(disabledWindow.getTask()));
 
         final WindowState normalWindow = createWindow(null,
                 FIRST_APPLICATION_WINDOW, mDisplayContent, "normalWindow");
         assertEquals(SNAPSHOT_MODE_REAL,
-                sWm.mTaskSnapshotController.getSnapshotMode(normalWindow.getTask()));
+                mWm.mTaskSnapshotController.getSnapshotMode(normalWindow.getTask()));
 
         final WindowState secureWindow = createWindow(null,
                 FIRST_APPLICATION_WINDOW, mDisplayContent, "secureWindow");
         secureWindow.mAttrs.flags |= FLAG_SECURE;
         assertEquals(SNAPSHOT_MODE_APP_THEME,
-                sWm.mTaskSnapshotController.getSnapshotMode(secureWindow.getTask()));
+                mWm.mTaskSnapshotController.getSnapshotMode(secureWindow.getTask()));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index 600b2e5..b0eafee 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -34,23 +34,22 @@
 import android.view.View;
 
 import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.io.File;
+import java.util.function.Predicate;
 
 /**
  * Test class for {@link TaskSnapshotPersister} and {@link TaskSnapshotLoader}
  *
- * atest FrameworksServicesTests:TaskSnapshotPersisterLoaderTest
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:TaskSnapshotPersisterLoaderTest
  */
 @MediumTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBase {
 
     private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40);
@@ -59,9 +58,9 @@
     public void testPersistAndLoadSnapshot() {
         mPersister.persistSnapshot(1 , mTestUserId, createSnapshot());
         mPersister.waitForQueueEmpty();
-        final File[] files = new File[] { new File(sFilesDir.getPath() + "/snapshots/1.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/1.jpg"),
-                new File(sFilesDir.getPath() + "/snapshots/1_reduced.jpg")};
+        final File[] files = new File[] { new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
+                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")};
         assertTrueForFiles(files, File::exists, " must exist");
         final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* reduced */);
         assertNotNull(snapshot);
@@ -70,9 +69,10 @@
         assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation());
     }
 
-    private void assertTrueForFiles(File[] files, Predicate<File> predicate, String message) {
+    private static void assertTrueForFiles(File[] files, Predicate<File> predicate,
+            String message) {
         for (File file : files) {
-            assertTrue(file.getName() + message, predicate.apply(file));
+            assertTrue(file.getName() + message, predicate.test(file));
         }
     }
 
@@ -81,9 +81,9 @@
         mPersister.persistSnapshot(1, mTestUserId, createSnapshot());
         mPersister.onTaskRemovedFromRecents(1, mTestUserId);
         mPersister.waitForQueueEmpty();
-        assertFalse(new File(sFilesDir.getPath() + "/snapshots/1.proto").exists());
-        assertFalse(new File(sFilesDir.getPath() + "/snapshots/1.jpg").exists());
-        assertFalse(new File(sFilesDir.getPath() + "/snapshots/1_reduced.jpg").exists());
+        assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1.proto").exists());
+        assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1.jpg").exists());
+        assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg").exists());
     }
 
     /**
@@ -120,12 +120,12 @@
 
         // Make sure 1,2 were purged but removeObsoleteFiles wasn't.
         final File[] existsFiles = new File[] {
-                new File(sFilesDir.getPath() + "/snapshots/3.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/4.proto")};
+                new File(FILES_DIR.getPath() + "/snapshots/3.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/4.proto")};
         final File[] nonExistsFiles = new File[] {
-                new File(sFilesDir.getPath() + "/snapshots/100.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/1.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/1.proto")};
+                new File(FILES_DIR.getPath() + "/snapshots/100.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/1.proto")};
         assertTrueForFiles(existsFiles, File::exists, " must exist");
         assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
     }
@@ -149,10 +149,10 @@
         assertTrue(a.isReducedResolution());
         mPersister.persistSnapshot(1 , mTestUserId, a);
         mPersister.waitForQueueEmpty();
-        final File[] files = new File[] { new File(sFilesDir.getPath() + "/snapshots/1.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/1_reduced.jpg")};
+        final File[] files = new File[] { new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")};
         final File[] nonExistsFiles = new File[] {
-                new File(sFilesDir.getPath() + "/snapshots/1.jpg"),
+                new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
         };
         assertTrueForFiles(files, File::exists, " must exist");
         assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
@@ -195,8 +195,8 @@
         TaskSnapshot b = new TaskSnapshotBuilder()
                 .setWindowingMode(WINDOWING_MODE_PINNED)
                 .build();
-        assertTrue(a.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
-        assertTrue(b.getWindowingMode() == WINDOWING_MODE_PINNED);
+        assertEquals(WINDOWING_MODE_FULLSCREEN, a.getWindowingMode());
+        assertEquals(WINDOWING_MODE_PINNED, b.getWindowingMode());
         mPersister.persistSnapshot(1, mTestUserId, a);
         mPersister.persistSnapshot(2, mTestUserId, b);
         mPersister.waitForQueueEmpty();
@@ -204,8 +204,8 @@
         final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, false /* reduced */);
         assertNotNull(snapshotA);
         assertNotNull(snapshotB);
-        assertTrue(snapshotA.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
-        assertTrue(snapshotB.getWindowingMode() == WINDOWING_MODE_PINNED);
+        assertEquals(WINDOWING_MODE_FULLSCREEN, snapshotA.getWindowingMode());
+        assertEquals(WINDOWING_MODE_PINNED, snapshotB.getWindowingMode());
     }
 
     @Test
@@ -239,8 +239,8 @@
         TaskSnapshot b = new TaskSnapshotBuilder()
                 .setSystemUiVisibility(lightBarFlags)
                 .build();
-        assertTrue(a.getSystemUiVisibility() == 0);
-        assertTrue(b.getSystemUiVisibility() == lightBarFlags);
+        assertEquals(0, a.getSystemUiVisibility());
+        assertEquals(lightBarFlags, b.getSystemUiVisibility());
         mPersister.persistSnapshot(1, mTestUserId, a);
         mPersister.persistSnapshot(2, mTestUserId, b);
         mPersister.waitForQueueEmpty();
@@ -248,8 +248,8 @@
         final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, false /* reduced */);
         assertNotNull(snapshotA);
         assertNotNull(snapshotB);
-        assertTrue(snapshotA.getSystemUiVisibility() == 0);
-        assertTrue(snapshotB.getSystemUiVisibility() == lightBarFlags);
+        assertEquals(0, snapshotA.getSystemUiVisibility());
+        assertEquals(lightBarFlags, snapshotB.getSystemUiVisibility());
     }
 
     @Test
@@ -261,13 +261,13 @@
         mPersister.removeObsoleteFiles(taskIds, new int[] { mTestUserId });
         mPersister.waitForQueueEmpty();
         final File[] existsFiles = new File[] {
-                new File(sFilesDir.getPath() + "/snapshots/1.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/1.jpg"),
-                new File(sFilesDir.getPath() + "/snapshots/1_reduced.jpg") };
+                new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
+                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg") };
         final File[] nonExistsFiles = new File[] {
-                new File(sFilesDir.getPath() + "/snapshots/2.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/2.jpg"),
-                new File(sFilesDir.getPath() + "/snapshots/2_reduced.jpg")};
+                new File(FILES_DIR.getPath() + "/snapshots/2.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/2.jpg"),
+                new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")};
         assertTrueForFiles(existsFiles, File::exists, " must exist");
         assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
     }
@@ -281,24 +281,12 @@
         mPersister.persistSnapshot(2, mTestUserId, createSnapshot());
         mPersister.waitForQueueEmpty();
         final File[] existsFiles = new File[] {
-                new File(sFilesDir.getPath() + "/snapshots/1.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/1.jpg"),
-                new File(sFilesDir.getPath() + "/snapshots/1_reduced.jpg"),
-                new File(sFilesDir.getPath() + "/snapshots/2.proto"),
-                new File(sFilesDir.getPath() + "/snapshots/2.jpg"),
-                new File(sFilesDir.getPath() + "/snapshots/2_reduced.jpg")};
+                new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
+                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"),
+                new File(FILES_DIR.getPath() + "/snapshots/2.proto"),
+                new File(FILES_DIR.getPath() + "/snapshots/2.jpg"),
+                new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")};
         assertTrueForFiles(existsFiles, File::exists, " must exist");
     }
-
-    /**
-     * Private predicate definition.
-     *
-     * This is needed because com.android.internal.util.Predicate is deprecated
-     * and can only be used with classes fron android.test.runner. This cannot
-     * use java.util.function.Predicate because that is not present on all API
-     * versions that this test must run on.
-     */
-    private interface Predicate<T> {
-        boolean apply(T t);
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index 33b137e..0f9b825 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -21,6 +21,8 @@
 import static android.graphics.GraphicBuffer.USAGE_HW_TEXTURE;
 import static android.graphics.GraphicBuffer.USAGE_SW_READ_RARELY;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import android.app.ActivityManager.TaskSnapshot;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -31,51 +33,37 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.BeforeClass;
 
 import java.io.File;
 
-import androidx.test.InstrumentationRegistry;
-
 /**
  * Base class for tests that use a {@link TaskSnapshotPersister}.
  */
 class TaskSnapshotPersisterTestBase extends WindowTestsBase {
 
     private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40);
+    static final File FILES_DIR = getInstrumentation().getTargetContext().getFilesDir();
 
     TaskSnapshotPersister mPersister;
     TaskSnapshotLoader mLoader;
     int mTestUserId;
-    static File sFilesDir;
 
-    @BeforeClass
-    public static void setUpUser() {
-        sFilesDir = InstrumentationRegistry.getContext().getFilesDir();
-    }
-
-    @Override
     @Before
-    public void setUp() throws Exception {
-        super.setUp();
-
-        final UserManager um = UserManager.get(InstrumentationRegistry.getContext());
+    public void setUp() {
+        final UserManager um = UserManager.get(getInstrumentation().getTargetContext());
         mTestUserId = um.getUserHandle();
-        mPersister = new TaskSnapshotPersister(userId -> sFilesDir);
+        mPersister = new TaskSnapshotPersister(userId -> FILES_DIR);
         mLoader = new TaskSnapshotLoader(mPersister);
         mPersister.start();
     }
 
-    @Override
     @After
-    public void tearDown() throws Exception {
+    public void tearDown() {
         cleanDirectory();
-
-        super.tearDown();
     }
 
     private void cleanDirectory() {
-        final File[] files = new File(sFilesDir, "snapshots").listFiles();
+        final File[] files = new File(FILES_DIR, "snapshots").listFiles();
         if (files == null) {
             return;
         }
@@ -99,7 +87,7 @@
     /**
      * Builds a TaskSnapshot.
      */
-    class TaskSnapshotBuilder {
+    static class TaskSnapshotBuilder {
 
         private float mScale = 1f;
         private boolean mIsRealSnapshot = true;
@@ -107,32 +95,32 @@
         private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
         private int mSystemUiVisibility = 0;
 
-        public TaskSnapshotBuilder setScale(float scale) {
+        TaskSnapshotBuilder setScale(float scale) {
             mScale = scale;
             return this;
         }
 
-        public TaskSnapshotBuilder setIsRealSnapshot(boolean isRealSnapshot) {
+        TaskSnapshotBuilder setIsRealSnapshot(boolean isRealSnapshot) {
             mIsRealSnapshot = isRealSnapshot;
             return this;
         }
 
-        public TaskSnapshotBuilder setIsTranslucent(boolean isTranslucent) {
+        TaskSnapshotBuilder setIsTranslucent(boolean isTranslucent) {
             mIsTranslucent = isTranslucent;
             return this;
         }
 
-        public TaskSnapshotBuilder setWindowingMode(int windowingMode) {
+        TaskSnapshotBuilder setWindowingMode(int windowingMode) {
             mWindowingMode = windowingMode;
             return this;
         }
 
-        public TaskSnapshotBuilder setSystemUiVisibility(int systemUiVisibility) {
+        TaskSnapshotBuilder setSystemUiVisibility(int systemUiVisibility) {
             mSystemUiVisibility = systemUiVisibility;
             return this;
         }
 
-        public TaskSnapshot build() {
+        TaskSnapshot build() {
             final GraphicBuffer buffer = GraphicBuffer.create(100, 100, PixelFormat.RGBA_8888,
                     USAGE_HW_TEXTURE | USAGE_SW_READ_RARELY | USAGE_SW_READ_RARELY);
             Canvas c = buffer.lockCanvas();
@@ -142,6 +130,5 @@
                     mScale < 1f /* reducedResolution */, mScale, mIsRealSnapshot, mWindowingMode,
                     mSystemUiVisibility, mIsTranslucent);
         }
-
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index 91074b9..7c16191 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -39,21 +39,19 @@
 import android.view.Surface;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.TaskSnapshotSurface.Window;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link TaskSnapshotSurface}.
  *
- * runtest frameworks-services -c com.android.server.wm.TaskSnapshotSurfaceTest
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:TaskSnapshotSurfaceTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class TaskSnapshotSurfaceTest extends WindowTestsBase {
 
     private TaskSnapshotSurface mSurface;
@@ -65,7 +63,7 @@
         final TaskSnapshot snapshot = new TaskSnapshot(buffer,
                 ORIENTATION_PORTRAIT, contentInsets, false, 1.0f, true /* isRealSnapshot */,
                 WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */);
-        mSurface = new TaskSnapshotSurface(sWm, new Window(), new Surface(), snapshot, "Test",
+        mSurface = new TaskSnapshotSurface(mWm, new Window(), new Surface(), snapshot, "Test",
                 Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds,
                 ORIENTATION_PORTRAIT);
     }
@@ -76,7 +74,7 @@
     }
 
     @Test
-    public void fillEmptyBackground_fillHorizontally() throws Exception {
+    public void fillEmptyBackground_fillHorizontally() {
         setupSurface(200, 100);
         final Canvas mockCanvas = mock(Canvas.class);
         when(mockCanvas.getWidth()).thenReturn(200);
@@ -86,7 +84,7 @@
     }
 
     @Test
-    public void fillEmptyBackground_fillVertically() throws Exception {
+    public void fillEmptyBackground_fillVertically() {
         setupSurface(100, 200);
         final Canvas mockCanvas = mock(Canvas.class);
         when(mockCanvas.getWidth()).thenReturn(100);
@@ -96,7 +94,7 @@
     }
 
     @Test
-    public void fillEmptyBackground_fillBoth() throws Exception {
+    public void fillEmptyBackground_fillBoth() {
         setupSurface(200, 200);
         final Canvas mockCanvas = mock(Canvas.class);
         when(mockCanvas.getWidth()).thenReturn(200);
@@ -107,7 +105,7 @@
     }
 
     @Test
-    public void fillEmptyBackground_dontFill_sameSize() throws Exception {
+    public void fillEmptyBackground_dontFill_sameSize() {
         setupSurface(100, 100);
         final Canvas mockCanvas = mock(Canvas.class);
         when(mockCanvas.getWidth()).thenReturn(100);
@@ -117,7 +115,7 @@
     }
 
     @Test
-    public void fillEmptyBackground_dontFill_bitmapLarger() throws Exception {
+    public void fillEmptyBackground_dontFill_bitmapLarger() {
         setupSurface(100, 100);
         final Canvas mockCanvas = mock(Canvas.class);
         when(mockCanvas.getWidth()).thenReturn(100);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
index 0e9a63c..f01e9f0 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -27,17 +27,17 @@
 
 import android.platform.test.annotations.Presubmit;
 
+import androidx.test.filters.SmallTest;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
-import androidx.test.filters.SmallTest;
-
 /**
  * Tests for the {@link DisplayContent.TaskStackContainers} container in {@link DisplayContent}.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.TaskStackContainersTests
+ *  atest FrameworksServicesTests:TaskStackContainersTests
  */
 @SmallTest
 @Presubmit
@@ -45,11 +45,8 @@
 
     private TaskStack mPinnedStack;
 
-    @Override
     @Before
     public void setUp() throws Exception {
-        super.setUp();
-
         mPinnedStack = createStackControllerOnStackOnDisplay(
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
         // Stack should contain visible app window to be considered visible.
@@ -61,12 +58,9 @@
         assertTrue(mPinnedStack.isVisible());
     }
 
-    @Override
     @After
     public void tearDown() throws Exception {
         mPinnedStack.removeImmediately();
-
-        super.tearDown();
     }
 
     @Test
@@ -121,14 +115,14 @@
         final Task task = createTaskInStack(stack, 0 /* userId */);
 
         // Add another display at top.
-        sWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
+        mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
                 false /* includingParents */);
 
         // Move the task of {@code mDisplayContent} to top.
         stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
-        final int indexOfDisplayWithPinnedStack = sWm.mRoot.mChildren.indexOf(mDisplayContent);
+        final int indexOfDisplayWithPinnedStack = mWm.mRoot.mChildren.indexOf(mDisplayContent);
 
         assertEquals("The testing DisplayContent should be moved to top with task",
-                sWm.mRoot.getChildCount() - 1, indexOfDisplayWithPinnedStack);
+                mWm.mRoot.getChildCount() - 1, indexOfDisplayWithPinnedStack);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
index cb5c1cd..7ac3318 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -25,24 +25,21 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link TaskStack} class.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.TaskStackTests
+ *  atest FrameworksServicesTests:TaskStackTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class TaskStackTests extends WindowTestsBase {
 
     @Test
-    public void testStackPositionChildAt() throws Exception {
+    public void testStackPositionChildAt() {
         final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task1 = createTaskInStack(stack, 0 /* userId */);
         final Task task2 = createTaskInStack(stack, 1 /* userId */);
@@ -59,7 +56,7 @@
     }
 
     @Test
-    public void testClosingAppDifferentStackOrientation() throws Exception {
+    public void testClosingAppDifferentStackOrientation() {
         final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task1 = createTaskInStack(stack, 0 /* userId */);
         WindowTestUtils.TestAppWindowToken appWindowToken1 =
@@ -79,7 +76,7 @@
     }
 
     @Test
-    public void testMoveTaskToBackDifferentStackOrientation() throws Exception {
+    public void testMoveTaskToBackDifferentStackOrientation() {
         final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task1 = createTaskInStack(stack, 0 /* userId */);
         WindowTestUtils.TestAppWindowToken appWindowToken1 =
@@ -99,7 +96,7 @@
     }
 
     @Test
-    public void testStackRemoveImmediately() throws Exception {
+    public void testStackRemoveImmediately() {
         final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task = createTaskInStack(stack, 0 /* userId */);
         assertEquals(stack, task.mStack);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
index edd7664..1af79e4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -24,24 +24,21 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link TaskWindowContainerController}.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.TaskWindowContainerControllerTests
+ *  atest FrameworksServicesTests:TaskWindowContainerControllerTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class TaskWindowContainerControllerTests extends WindowTestsBase {
 
     @Test
-    public void testRemoveContainer() throws Exception {
+    public void testRemoveContainer() {
         final WindowTestUtils.TestTaskWindowContainerController taskController =
                 new WindowTestUtils.TestTaskWindowContainerController(this);
         final WindowTestUtils.TestAppWindowContainerController appController =
@@ -54,7 +51,7 @@
     }
 
     @Test
-    public void testRemoveContainer_deferRemoval() throws Exception {
+    public void testRemoveContainer_deferRemoval() {
         final WindowTestUtils.TestTaskWindowContainerController taskController =
                 new WindowTestUtils.TestTaskWindowContainerController(this);
         final WindowTestUtils.TestAppWindowContainerController appController =
@@ -79,7 +76,7 @@
     }
 
     @Test
-    public void testReparent() throws Exception {
+    public void testReparent() {
         final StackWindowController stackController1 =
                 createStackControllerOnDisplay(mDisplayContent);
         final WindowTestUtils.TestTaskWindowContainerController taskController =
@@ -116,7 +113,7 @@
     }
 
     @Test
-    public void testReparent_BetweenDisplays() throws Exception {
+    public void testReparent_BetweenDisplays() {
         // Create first stack on primary display.
         final StackWindowController stack1Controller =
                 createStackControllerOnDisplay(mDisplayContent);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
index 353aa7d..e8d0a06 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
@@ -11,13 +11,11 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
 
-import com.android.internal.os.IResultReceiver;
-
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
@@ -27,87 +25,71 @@
 import android.view.DragEvent;
 import android.view.IWindow;
 
+import com.android.internal.os.IResultReceiver;
+
 public class TestIWindow extends IWindow.Stub {
     @Override
     public void executeCommand(String command, String parameters,
-            ParcelFileDescriptor descriptor)
-            throws RemoteException {
-
+            ParcelFileDescriptor descriptor) throws RemoteException {
     }
 
     @Override
     public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
             Rect stableInsets, Rect outsets, boolean reportDraw, MergedConfiguration mergedConfig,
             Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId,
-            DisplayCutout.ParcelableWrapper displayCutout)
-            throws RemoteException {
-
+            DisplayCutout.ParcelableWrapper displayCutout) throws RemoteException {
     }
 
     @Override
     public void moved(int newX, int newY) throws RemoteException {
-
     }
 
     @Override
     public void dispatchAppVisibility(boolean visible) throws RemoteException {
-
     }
 
     @Override
     public void dispatchGetNewSurface() throws RemoteException {
-
     }
 
     @Override
-    public void windowFocusChanged(boolean hasFocus, boolean inTouchMode)
-            throws RemoteException {
-
+    public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) throws RemoteException {
     }
 
     @Override
     public void closeSystemDialogs(String reason) throws RemoteException {
-
     }
 
     @Override
-    public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
-            boolean sync)
+    public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync)
             throws RemoteException {
-
     }
 
     @Override
     public void dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras,
             boolean sync) throws RemoteException {
-
     }
 
     @Override
     public void dispatchDragEvent(DragEvent event) throws RemoteException {
-
     }
 
     @Override
     public void updatePointerIcon(float x, float y) throws RemoteException {
-
     }
 
     @Override
     public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, int localValue,
             int localChanges) throws RemoteException {
-
     }
 
     @Override
     public void dispatchWindowShown() throws RemoteException {
-
     }
 
     @Override
     public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId)
             throws RemoteException {
-
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 19abd9e..0165e7d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -46,8 +46,6 @@
 import java.util.function.Supplier;
 
 class TestWindowManagerPolicy implements WindowManagerPolicy {
-    private static final String TAG = "TestWindowManagerPolicy";
-
     private final Supplier<WindowManagerService> mWmSupplier;
 
     int rotationToReport = 0;
@@ -55,21 +53,18 @@
 
     private Runnable mRunnableWhenAddingSplashScreen;
 
-    public TestWindowManagerPolicy(Supplier<WindowManagerService> wmSupplier) {
-
+    TestWindowManagerPolicy(Supplier<WindowManagerService> wmSupplier) {
         mWmSupplier = wmSupplier;
     }
 
     @Override
     public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver)
             throws RemoteException {
-
     }
 
     @Override
     public void init(Context context, IWindowManager windowManager,
             WindowManagerFuncs windowManagerFuncs) {
-
     }
 
     public void setDefaultDisplay(DisplayContentInfo displayContentInfo) {
@@ -93,7 +88,6 @@
     @Override
     public void adjustConfigurationLw(Configuration config, int keyboardPresence,
             int navigationPresence) {
-
     }
 
     @Override
@@ -179,7 +173,6 @@
 
     @Override
     public void removeWindowLw(WindowState win) {
-
     }
 
     @Override
@@ -189,7 +182,6 @@
 
     @Override
     public void selectRotationAnimationLw(int[] anim) {
-
     }
 
     @Override
@@ -220,14 +212,12 @@
     }
 
     @Override
-    public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event,
-            int policyFlags) {
+    public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
         return 0;
     }
 
     @Override
-    public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event,
-            int policyFlags) {
+    public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
         return null;
     }
 
@@ -238,12 +228,11 @@
 
     @Override
     public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
-
     }
 
     @Override
-    public void applyPostLayoutPolicyLw(WindowState win,
-            WindowManager.LayoutParams attrs, WindowState attached, WindowState imeTarget) {
+    public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
+            WindowState attached, WindowState imeTarget) {
     }
 
     @Override
@@ -257,49 +246,40 @@
     }
 
     @Override
-    public int focusChangedLw(WindowState lastFocus,
-            WindowState newFocus) {
+    public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
         return 0;
     }
 
     @Override
     public void startedWakingUp() {
-
     }
 
     @Override
     public void finishedWakingUp() {
-
     }
 
     @Override
     public void startedGoingToSleep(int why) {
-
     }
 
     @Override
     public void finishedGoingToSleep(int why) {
-
     }
 
     @Override
     public void screenTurningOn(ScreenOnListener screenOnListener) {
-
     }
 
     @Override
     public void screenTurnedOn() {
-
     }
 
     @Override
     public void screenTurningOff(ScreenOffListener screenOffListener) {
-
     }
 
     @Override
     public void screenTurnedOff() {
-
     }
 
     @Override
@@ -314,22 +294,18 @@
 
     @Override
     public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
-
     }
 
     @Override
     public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) {
-
     }
 
     @Override
     public void enableKeyguard(boolean enabled) {
-
     }
 
     @Override
     public void exitKeyguardSecurely(OnKeyguardExitResult callback) {
-
     }
 
     @Override
@@ -382,53 +358,44 @@
     }
 
     public void setSafeMode(boolean safeMode) {
-
     }
 
     @Override
     public void systemReady() {
-
     }
 
     @Override
     public void systemBooted() {
-
     }
 
     @Override
     public void showBootMessage(CharSequence msg, boolean always) {
-
     }
 
     @Override
     public void hideBootMessages() {
-
     }
 
     @Override
     public void userActivity() {
-
     }
 
     @Override
     public void enableScreenAfterBoot() {
-
     }
 
     @Override
-    public boolean performHapticFeedbackLw(WindowState win, int effectId,
-            boolean always, String reason) {
+    public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always,
+            String reason) {
         return false;
     }
 
     @Override
     public void keepScreenOnStartedLw() {
-
     }
 
     @Override
     public void keepScreenOnStoppedLw() {
-
     }
 
     @Override
@@ -443,37 +410,30 @@
 
     @Override
     public void lockNow(Bundle options) {
-
     }
 
     @Override
     public void showRecentApps() {
-
     }
 
     @Override
     public void showGlobalActions() {
-
     }
 
     @Override
     public void setCurrentUserLw(int newUserId) {
-
     }
 
     @Override
     public void setSwitchingUser(boolean switching) {
-
     }
 
     @Override
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
-
     }
 
     @Override
     public void dump(String prefix, PrintWriter writer, String[] args) {
-
     }
 
     @Override
@@ -483,13 +443,11 @@
 
     @Override
     public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
-
     }
 
     @Override
     public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
             DisplayCutout cutout, Rect outInsets) {
-
     }
 
     @Override
@@ -506,7 +464,6 @@
     @Override
     public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight,
             DisplayCutout cutout, Rect outInsets) {
-
     }
 
     @Override
@@ -517,7 +474,6 @@
 
     @Override
     public void onConfigurationChanged(DisplayContentInfo displayContentInfo) {
-
     }
 
     @Override
@@ -528,12 +484,10 @@
 
     @Override
     public void setPipVisibilityLw(boolean visible) {
-
     }
 
     @Override
     public void setRecentsVisibilityLw(boolean visible) {
-
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index 54456fb..9e22c0a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -11,52 +11,50 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
 
 import static junit.framework.Assert.assertTrue;
 
+import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link AppTransition}.
  *
- * runtest frameworks-services -c com.android.server.wm.UnknownAppVisibilityControllerTest
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:UnknownAppVisibilityControllerTest
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class UnknownAppVisibilityControllerTest extends WindowTestsBase {
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
         mDisplayContent.mUnknownAppVisibilityController.clear();
     }
 
     @Test
-    public void testFlow() throws Exception {
+    public void testFlow() {
         final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
         mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token);
         mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(token);
         mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token);
 
         // Make sure our handler processed the message.
-        Thread.sleep(100);
+        SystemClock.sleep(100);
         assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
     }
 
     @Test
-    public void testMultiple() throws Exception {
+    public void testMultiple() {
         final AppWindowToken token1 = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
         final AppWindowToken token2 = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
         mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token1);
@@ -67,12 +65,12 @@
         mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token2);
 
         // Make sure our handler processed the message.
-        Thread.sleep(100);
+        SystemClock.sleep(100);
         assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
     }
 
     @Test
-    public void testClear() throws Exception {
+    public void testClear() {
         final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
         mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token);
         mDisplayContent.mUnknownAppVisibilityController.clear();;
@@ -80,7 +78,7 @@
     }
 
     @Test
-    public void testAppRemoved() throws Exception {
+    public void testAppRemoved() {
         final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
         mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token);
         mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(token);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
index e3280ca..25e73e3 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.server.wm;
 
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
@@ -13,33 +29,30 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link WallpaperController} class.
  *
  * Build/Install/Run:
- *  atest com.android.server.wm.WallpaperControllerTests
+ *  atest FrameworksServicesTests:WallpaperControllerTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class WallpaperControllerTests extends WindowTestsBase {
     @Test
     public void testWallpaperScreenshot() {
         WindowSurfaceController windowSurfaceController = mock(WindowSurfaceController.class);
 
-        synchronized (sWm.mGlobalLock) {
+        synchronized (mWm.mGlobalLock) {
             // No wallpaper
             final DisplayContent dc = createNewDisplay();
-            Bitmap wallpaperBitmap = sWm.mRoot.mWallpaperController.screenshotWallpaperLocked();
+            Bitmap wallpaperBitmap = mWm.mRoot.mWallpaperController.screenshotWallpaperLocked();
             assertNull(wallpaperBitmap);
 
             // No wallpaper WSA Surface
-            WindowToken wallpaperWindowToken = new WallpaperWindowToken(sWm, mock(IBinder.class),
+            WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
                     true, dc, true /* ownerCanManageAppTokens */);
             WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER,
                     wallpaperWindowToken, "wallpaperWindow");
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
index e7cc285..4369c96 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -36,26 +36,24 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Before;
 import org.junit.Test;
 
 /**
  * Test class to for {@link android.app.WindowConfiguration}.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.WindowConfigurationTests
+ *  atest FrameworksServicesTests:WindowConfigurationTests
  */
-@SmallTest
 @FlakyTest(bugId = 74078662)
+@SmallTest
 @Presubmit
-@org.junit.runner.RunWith(AndroidJUnit4.class)
 public class WindowConfigurationTests extends WindowTestsBase {
     private Rect mParentBounds;
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
         mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/);
     }
 
@@ -95,7 +93,7 @@
 
     /** Tests {@link android.app.WindowConfiguration#compareTo(WindowConfiguration)}. */
     @Test
-    public void testConfigurationCompareTo() throws Exception {
+    public void testConfigurationCompareTo() {
         final Configuration blankConfig = new Configuration();
         final WindowConfiguration blankWinConfig = new WindowConfiguration();
 
@@ -135,7 +133,7 @@
     }
 
     @Test
-    public void testSetActivityType() throws Exception {
+    public void testSetActivityType() {
         final WindowConfiguration config = new WindowConfiguration();
         config.setActivityType(ACTIVITY_TYPE_HOME);
         assertEquals(ACTIVITY_TYPE_HOME, config.getActivityType());
@@ -147,12 +145,12 @@
 
     /** Ensures the configuration app bounds at the root level match the app dimensions. */
     @Test
-    public void testAppBounds_RootConfigurationBounds() throws Exception {
+    public void testAppBounds_RootConfigurationBounds() {
         final DisplayInfo info = mDisplayContent.getDisplayInfo();
         info.appWidth = 1024;
         info.appHeight = 768;
 
-        final Rect appBounds = sWm.computeNewConfiguration(
+        final Rect appBounds = mWm.computeNewConfiguration(
                 mDisplayContent.getDisplayId()).windowConfiguration.getAppBounds();
         // The bounds should always be positioned in the top left.
         assertEquals(appBounds.left, 0);
@@ -165,7 +163,7 @@
 
     /** Ensures that bounds are clipped to their parent. */
     @Test
-    public void testAppBounds_BoundsClipping() throws Exception {
+    public void testAppBounds_BoundsClipping() {
         final Rect shiftedBounds = new Rect(mParentBounds);
         shiftedBounds.offset(10, 10);
         final Rect expectedBounds = new Rect(mParentBounds);
@@ -176,7 +174,7 @@
 
     /** Ensures that empty bounds are not propagated to the configuration. */
     @Test
-    public void testAppBounds_EmptyBounds() throws Exception {
+    public void testAppBounds_EmptyBounds() {
         final Rect emptyBounds = new Rect();
         testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds,
                 null /*ExpectedBounds*/);
@@ -184,7 +182,7 @@
 
     /** Ensures that bounds on freeform stacks are not clipped. */
     @Test
-    public void testAppBounds_FreeFormBounds() throws Exception {
+    public void testAppBounds_FreeFormBounds() {
         final Rect freeFormBounds = new Rect(mParentBounds);
         freeFormBounds.offset(10, 10);
         testStackBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds,
@@ -193,7 +191,7 @@
 
     /** Ensures that fully contained bounds are not clipped. */
     @Test
-    public void testAppBounds_ContainedBounds() throws Exception {
+    public void testAppBounds_ContainedBounds() {
         final Rect insetBounds = new Rect(mParentBounds);
         insetBounds.inset(5, 5, 5, 5);
         testStackBoundsConfiguration(
@@ -202,7 +200,7 @@
 
     /** Ensures that full screen free form bounds are clipped */
     @Test
-    public void testAppBounds_FullScreenFreeFormBounds() throws Exception {
+    public void testAppBounds_FullScreenFreeFormBounds() {
         final Rect fullScreenBounds = new Rect(0, 0, mDisplayInfo.logicalWidth,
                 mDisplayInfo.logicalHeight);
         testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, fullScreenBounds,
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
index 6b1feb8..7592f1c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -28,7 +28,6 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 
@@ -36,18 +35,17 @@
  * Test class for {@link WindowContainerController}.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.WindowContainerControllerTests
+ *  atest FrameworksServicesTests:WindowContainerControllerTests
  */
+@FlakyTest(bugId = 74078662)
 @SmallTest
 @Presubmit
-@FlakyTest(bugId = 74078662)
-@org.junit.runner.RunWith(AndroidJUnit4.class)
 public class WindowContainerControllerTests extends WindowTestsBase {
 
     @Test
-    public void testCreation() throws Exception {
-        final WindowContainerController controller = new WindowContainerController(null, sWm);
-        final WindowContainer container = new WindowContainer(sWm);
+    public void testCreation() {
+        final WindowContainerController controller = new WindowContainerController<>(null, mWm);
+        final WindowContainer container = new WindowContainer(mWm);
 
         container.setController(controller);
         assertEquals(controller, container.getController());
@@ -55,9 +53,9 @@
     }
 
     @Test
-    public void testSetContainer() throws Exception {
-        final WindowContainerController controller = new WindowContainerController(null, sWm);
-        final WindowContainer container = new WindowContainer(sWm);
+    public void testSetContainer() {
+        final WindowContainerController controller = new WindowContainerController<>(null, mWm);
+        final WindowContainer container = new WindowContainer(mWm);
 
         controller.setContainer(container);
         assertEquals(controller.mContainer, container);
@@ -65,7 +63,7 @@
         // Assert we can't change the container to another one once set
         boolean gotException = false;
         try {
-            controller.setContainer(new WindowContainer(sWm));
+            controller.setContainer(new WindowContainer(mWm));
         } catch (IllegalArgumentException e) {
             gotException = true;
         }
@@ -77,9 +75,9 @@
     }
 
     @Test
-    public void testRemoveContainer() throws Exception {
-        final WindowContainerController controller = new WindowContainerController(null, sWm);
-        final WindowContainer container = new WindowContainer(sWm);
+    public void testRemoveContainer() {
+        final WindowContainerController controller = new WindowContainerController<>(null, mWm);
+        final WindowContainer container = new WindowContainer(mWm);
 
         controller.setContainer(container);
         assertEquals(controller.mContainer, container);
@@ -89,9 +87,9 @@
     }
 
     @Test
-    public void testOnOverrideConfigurationChanged() throws Exception {
-        final WindowContainerController controller = new WindowContainerController(null, sWm);
-        final WindowContainer container = new WindowContainer(sWm);
+    public void testOnOverrideConfigurationChanged() {
+        final WindowContainerController controller = new WindowContainerController<>(null, mWm);
+        final WindowContainer container = new WindowContainer(mWm);
 
         controller.setContainer(container);
         assertEquals(controller.mContainer, container);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index dc763b9..e59afd6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -46,10 +46,8 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.util.Comparator;
 
@@ -57,24 +55,23 @@
  * Test class for {@link WindowContainer}.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.WindowContainerTests
+ *  atest FrameworksServicesTests:WindowContainerTests
  */
 @SmallTest
 @Presubmit
 @FlakyTest(bugId = 74078662)
-@RunWith(AndroidJUnit4.class)
 public class WindowContainerTests extends WindowTestsBase {
 
     @Test
-    public void testCreation() throws Exception {
-        final TestWindowContainer w = new TestWindowContainerBuilder().setLayer(0).build();
+    public void testCreation() {
+        final TestWindowContainer w = new TestWindowContainerBuilder(mWm).setLayer(0).build();
         assertNull("window must have no parent", w.getParentWindow());
         assertEquals("window must have no children", 0, w.getChildrenCount());
     }
 
     @Test
-    public void testAdd() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testAdd() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer layer1 = root.addChildWindow(builder.setLayer(1));
@@ -113,23 +110,24 @@
     }
 
     @Test
-    public void testAddChildSetsSurfacePosition() throws Exception {
-        MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer();
+    public void testAddChildSetsSurfacePosition() {
+        try (MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer(mWm)) {
 
-        final SurfaceControl.Transaction transaction = mock(SurfaceControl.Transaction.class);
-        sWm.mTransactionFactory = () -> transaction;
+            final SurfaceControl.Transaction transaction = mock(SurfaceControl.Transaction.class);
+            mWm.mTransactionFactory = () -> transaction;
 
-        WindowContainer child = new WindowContainer(sWm);
-        child.setBounds(1, 1, 10, 10);
+            WindowContainer child = new WindowContainer(mWm);
+            child.setBounds(1, 1, 10, 10);
 
-        verify(transaction, never()).setPosition(any(), anyFloat(), anyFloat());
-        top.addChild(child, 0);
-        verify(transaction, times(1)).setPosition(any(), eq(1.f), eq(1.f));
+            verify(transaction, never()).setPosition(any(), anyFloat(), anyFloat());
+            top.addChild(child, 0);
+            verify(transaction, times(1)).setPosition(any(), eq(1.f), eq(1.f));
+        }
     }
 
     @Test
-    public void testAdd_AlreadyHasParent() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testAdd_AlreadyHasParent() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow();
@@ -153,8 +151,8 @@
     }
 
     @Test
-    public void testHasChild() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testHasChild() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow();
@@ -183,8 +181,8 @@
     }
 
     @Test
-    public void testRemoveImmediately() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testRemoveImmediately() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow();
@@ -218,9 +216,9 @@
     }
 
     @Test
-    public void testRemoveImmediately_WithController() throws Exception {
-        final WindowContainer container = new WindowContainer(sWm);
-        final WindowContainerController controller = new WindowContainerController(null, sWm);
+    public void testRemoveImmediately_WithController() {
+        final WindowContainer container = new WindowContainer(mWm);
+        final WindowContainerController controller = new WindowContainerController<>(null, mWm);
 
         container.setController(controller);
         assertEquals(controller, container.getController());
@@ -232,9 +230,9 @@
     }
 
     @Test
-    public void testSetController() throws Exception {
-        final WindowContainerController controller = new WindowContainerController(null, sWm);
-        final WindowContainer container = new WindowContainer(sWm);
+    public void testSetController() {
+        final WindowContainerController controller = new WindowContainerController<>(null, mWm);
+        final WindowContainer container = new WindowContainer(mWm);
 
         container.setController(controller);
         assertEquals(controller, container.getController());
@@ -243,7 +241,7 @@
         // Assert we can't change the controller to another one once set
         boolean gotException = false;
         try {
-            container.setController(new WindowContainerController(null, sWm));
+            container.setController(new WindowContainerController<>(null, mWm));
         } catch (IllegalArgumentException e) {
             gotException = true;
         }
@@ -256,8 +254,8 @@
     }
 
     @Test
-    public void testAddChildByIndex() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testAddChildByIndex() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child = root.addChildWindow();
@@ -283,8 +281,8 @@
     }
 
     @Test
-    public void testPositionChildAt() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testPositionChildAt() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow();
@@ -307,8 +305,8 @@
     }
 
     @Test
-    public void testPositionChildAtIncludeParents() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testPositionChildAtIncludeParents() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow();
@@ -349,12 +347,11 @@
     }
 
     @Test
-    public void testPositionChildAtInvalid() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testPositionChildAtInvalid() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow();
-        final TestWindowContainer child2 = root.addChildWindow();
 
         boolean gotException = false;
         try {
@@ -376,8 +373,8 @@
     }
 
     @Test
-    public void testIsAnimating() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testIsAnimating() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow(builder.setIsAnimating(true));
@@ -402,8 +399,8 @@
     }
 
     @Test
-    public void testIsVisible() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testIsVisible() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow(builder.setIsVisible(true));
@@ -422,7 +419,7 @@
 
     @Test
     public void testOverrideConfigurationAncestorNotification() {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer grandparent = builder.setLayer(0).build();
 
         final TestWindowContainer parent = grandparent.addChildWindow();
@@ -433,8 +430,8 @@
     }
 
     @Test
-    public void testRemoveChild() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testRemoveChild() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
         final TestWindowContainer child1 = root.addChildWindow();
         final TestWindowContainer child2 = root.addChildWindow();
@@ -460,7 +457,7 @@
     }
 
     @Test
-    public void testGetOrientation_childSpecified() throws Exception {
+    public void testGetOrientation_childSpecified() {
         testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_LANDSCAPE,
             SCREEN_ORIENTATION_LANDSCAPE);
         testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_UNSET,
@@ -469,7 +466,7 @@
 
     private void testGetOrientation_childSpecifiedConfig(boolean childVisible, int childOrientation,
         int expectedOrientation) {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
         root.setFillsParent(true);
 
@@ -486,16 +483,16 @@
     }
 
     @Test
-    public void testGetOrientation_Unset() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testGetOrientation_Unset() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
         // Unspecified well because we didn't specify anything...
         assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, root.getOrientation());
     }
 
     @Test
-    public void testGetOrientation_InvisibleParentUnsetVisibleChildren() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testGetOrientation_InvisibleParentUnsetVisibleChildren() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
 
         builder.setIsVisible(false).setLayer(-1);
@@ -516,12 +513,11 @@
         visibleUnset.setOrientation(SCREEN_ORIENTATION_UNSET);
         assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnset.getOrientation());
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, root.getOrientation());
-
     }
 
     @Test
-    public void testGetOrientation_setBehind() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testGetOrientation_setBehind() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
 
         builder.setIsVisible(true).setLayer(-1);
@@ -541,8 +537,8 @@
     }
 
     @Test
-    public void testGetOrientation_fillsParent() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testGetOrientation_fillsParent() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build();
 
         builder.setIsVisible(true).setLayer(-1);
@@ -577,8 +573,8 @@
     }
 
     @Test
-    public void testCompareTo() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testCompareTo() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
 
         final TestWindowContainer child1 = root.addChildWindow();
@@ -588,8 +584,6 @@
         final TestWindowContainer child2 = root.addChildWindow();
         final TestWindowContainer child21 = child2.addChildWindow();
         final TestWindowContainer child22 = child2.addChildWindow();
-        final TestWindowContainer child23 = child2.addChildWindow();
-        final TestWindowContainer child221 = child22.addChildWindow();
         final TestWindowContainer child222 = child22.addChildWindow();
         final TestWindowContainer child223 = child22.addChildWindow();
         final TestWindowContainer child2221 = child222.addChildWindow();
@@ -620,8 +614,8 @@
     }
 
     @Test
-    public void testPrefixOrderIndex() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testPrefixOrderIndex() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.build();
 
         final TestWindowContainer child1 = root.addChildWindow();
@@ -654,8 +648,8 @@
     }
 
     @Test
-    public void testPrefixOrder_addEntireSubtree() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testPrefixOrder_addEntireSubtree() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.build();
         final TestWindowContainer subtree = builder.build();
         final TestWindowContainer subtree2 = builder.build();
@@ -677,8 +671,8 @@
     }
 
     @Test
-    public void testPrefixOrder_remove() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testPrefixOrder_remove() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.build();
 
         final TestWindowContainer child1 = root.addChildWindow();
@@ -705,8 +699,8 @@
      * is invoked with overridden bounds.
      */
     @Test
-    public void testOnParentResizePropagation() throws Exception {
-        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+    public void testOnParentResizePropagation() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.build();
 
         final TestWindowContainer child = root.addChildWindow();
@@ -731,7 +725,7 @@
     }
 
     /* Used so we can gain access to some protected members of the {@link WindowContainer} class */
-    private class TestWindowContainer extends WindowContainer<TestWindowContainer> {
+    private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         private final int mLayer;
         private boolean mIsAnimating;
         private boolean mIsVisible;
@@ -745,7 +739,7 @@
          * Compares 2 window layers and returns -1 if the first is lesser than the second in terms
          * of z-order and 1 otherwise.
          */
-        private final Comparator<TestWindowContainer> mWindowSubLayerComparator = (w1, w2) -> {
+        private static final Comparator<TestWindowContainer> SUBLAYER_COMPARATOR = (w1, w2) -> {
             final int layer1 = w1.mLayer;
             final int layer2 = w2.mLayer;
             if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0 )) {
@@ -753,12 +747,14 @@
                 // the negative one should go below others; the positive one should go above others.
                 return -1;
             }
+            if (layer1 == layer2) return 0;
             return 1;
         };
 
-        TestWindowContainer(int layer, boolean isAnimating, boolean isVisible,
-            Integer orientation) {
-            super(sWm);
+        TestWindowContainer(WindowManagerService wm, int layer, boolean isAnimating,
+                boolean isVisible, Integer orientation) {
+            super(wm);
+
             mLayer = layer;
             mIsAnimating = isAnimating;
             mIsVisible = isVisible;
@@ -775,18 +771,18 @@
         }
 
         TestWindowContainer addChildWindow(TestWindowContainer child) {
-            addChild(child, mWindowSubLayerComparator);
+            addChild(child, SUBLAYER_COMPARATOR);
             return child;
         }
 
         TestWindowContainer addChildWindow(TestWindowContainerBuilder childBuilder) {
             TestWindowContainer child = childBuilder.build();
-            addChild(child, mWindowSubLayerComparator);
+            addChild(child, SUBLAYER_COMPARATOR);
             return child;
         }
 
         TestWindowContainer addChildWindow() {
-            return addChildWindow(new TestWindowContainerBuilder().setLayer(1));
+            return addChildWindow(new TestWindowContainerBuilder(mService).setLayer(1));
         }
 
         @Override
@@ -830,14 +826,19 @@
         }
     }
 
-    private class TestWindowContainerBuilder {
+    private static class TestWindowContainerBuilder {
+        private final WindowManagerService mWm;
         private int mLayer;
         private boolean mIsAnimating;
         private boolean mIsVisible;
         private Integer mOrientation;
 
-        public TestWindowContainerBuilder() {
-            reset();
+        TestWindowContainerBuilder(WindowManagerService wm) {
+            mWm = wm;
+            mLayer = 0;
+            mIsAnimating = false;
+            mIsVisible = false;
+            mOrientation = null;
         }
 
         TestWindowContainerBuilder setLayer(int layer) {
@@ -860,27 +861,20 @@
             return this;
         }
 
-        TestWindowContainerBuilder reset() {
-            mLayer = 0;
-            mIsAnimating = false;
-            mIsVisible = false;
-            mOrientation = null;
-            return this;
-        }
-
         TestWindowContainer build() {
-            return new TestWindowContainer(mLayer, mIsAnimating, mIsVisible, mOrientation);
+            return new TestWindowContainer(mWm, mLayer, mIsAnimating, mIsVisible, mOrientation);
         }
     }
 
-    private class MockSurfaceBuildingContainer extends WindowContainer<WindowContainer> {
-        final SurfaceSession mSession = new SurfaceSession();
+    private static class MockSurfaceBuildingContainer extends WindowContainer<WindowContainer>
+            implements AutoCloseable {
+        private final SurfaceSession mSession = new SurfaceSession();
 
-        MockSurfaceBuildingContainer() {
-            super(sWm);
+        MockSurfaceBuildingContainer(WindowManagerService wm) {
+            super(wm);
         }
 
-        class MockSurfaceBuilder extends SurfaceControl.Builder {
+        static class MockSurfaceBuilder extends SurfaceControl.Builder {
             MockSurfaceBuilder(SurfaceSession ss) {
                 super(ss);
             }
@@ -895,5 +889,10 @@
         SurfaceControl.Builder makeChildSurface(WindowContainer child) {
             return new MockSurfaceBuilder(mSession);
         }
+
+        @Override
+        public void close() {
+            mSession.kill();
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
index ffc8622..2b8b934 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -28,23 +28,23 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.util.function.Consumer;
 
 /**
  * Tests for {@link WindowContainer#forAllWindows} and various implementations.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:WindowContainerTraversalTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class WindowContainerTraversalTests extends WindowTestsBase {
 
     @Test
-    public void testDockedDividerPosition() throws Exception {
+    public void testDockedDividerPosition() {
         final WindowState splitScreenWindow = createWindowOnStack(null,
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
                 mDisplayContent, "splitScreenWindow");
@@ -52,7 +52,7 @@
                 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
                 TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow");
 
-        sWm.mInputMethodTarget = splitScreenWindow;
+        mWm.mInputMethodTarget = splitScreenWindow;
 
         Consumer<WindowState> c = mock(Consumer.class);
         mDisplayContent.forAllWindows(c, false);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 7cd1314..b0c8d8b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -11,14 +11,14 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
 
 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
 import static android.view.DisplayCutout.fromBoundingRect;
-import static android.view.WindowManager.LayoutParams.FILL_PARENT;
+import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import static org.junit.Assert.assertEquals;
@@ -33,33 +33,32 @@
 import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link WindowState#computeFrameLw} method and other window frame machinery.
  *
- * Build/Install/Run: bit FrameworksServicesTests:com.android.server.wm.WindowFrameTests
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:WindowFrameTests
  */
 @SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class WindowFrameTests extends WindowTestsBase {
 
     private WindowToken mWindowToken;
     private final IWindow mIWindow = new TestIWindow();
     private final Rect mEmptyRect = new Rect();
 
-    class WindowStateWithTask extends WindowState {
+    static class WindowStateWithTask extends WindowState {
         final Task mTask;
         boolean mDockedResizingForTest = false;
-        WindowStateWithTask(WindowManager.LayoutParams attrs, Task t) {
-            super(sWm, null, mIWindow, mWindowToken, null, 0, 0, attrs, 0, 0,
+        WindowStateWithTask(WindowManagerService wm, IWindow iWindow, WindowToken windowToken,
+                WindowManager.LayoutParams attrs, Task t) {
+            super(wm, null, iWindow, windowToken, null, 0, 0, attrs, 0, 0,
                     false /* ownerCanAddInternalSystemWindow */);
             mTask = t;
         }
@@ -73,14 +72,15 @@
         boolean isDockedResizing() {
             return mDockedResizingForTest;
         }
-    };
+    }
 
-    class TaskWithBounds extends Task {
+    private static class TaskWithBounds extends Task {
         final Rect mBounds;
         final Rect mInsetBounds = new Rect();
         boolean mFullscreenForTest = true;
-        TaskWithBounds(Rect bounds) {
-            super(0, mStubStack, 0, sWm, 0, false, new TaskDescription(), null);
+
+        TaskWithBounds(TaskStack stack, WindowManagerService wm, Rect bounds) {
+            super(0, stack, 0, wm, 0, false, new TaskDescription(), null);
             mBounds = bounds;
             setBounds(bounds);
         }
@@ -113,14 +113,12 @@
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
-
         // Just any non zero value.
-        sWm.mSystemDecorLayer = 10000;
+        mWm.mSystemDecorLayer = 10000;
 
         mWindowToken = WindowTestUtils.createTestAppWindowToken(
-                sWm.getDefaultDisplayContentLocked());
-        mStubStack = new TaskStack(sWm, 0, null);
+                mWm.getDefaultDisplayContentLocked());
+        mStubStack = new TaskStack(mWm, 0, null);
     }
 
     // Do not use this function directly in the tests below. Instead, use more explicit function
@@ -170,9 +168,10 @@
     }
 
     @Test
-    public void testLayoutInFullscreenTaskInsets() throws Exception {
-        Task task = new TaskWithBounds(null); // fullscreen task doesn't use bounds for computeFrame
-        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+    public void testLayoutInFullscreenTaskInsets() {
+        // fullscreen task doesn't use bounds for computeFrame
+        final Task task = new TaskWithBounds(mStubStack, mWm, null);
+        WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final int bottomContentInset = 100;
@@ -227,9 +226,10 @@
     }
 
     @Test
-    public void testLayoutInFullscreenTaskNoInsets() throws Exception {
-        Task task = new TaskWithBounds(null); // fullscreen task doesn't use bounds for computeFrame
-        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+    public void testLayoutInFullscreenTaskNoInsets() {
+        // fullscreen task doesn't use bounds for computeFrame
+        final Task task = new TaskWithBounds(mStubStack, mWm, null);
+        WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         // With no insets or system decor all the frames incoming from PhoneWindowManager
@@ -307,7 +307,7 @@
 
     @Test
     public void testLayoutNonfullscreenTask() {
-        final DisplayInfo displayInfo = sWm.getDefaultDisplayContentLocked().getDisplayInfo();
+        final DisplayInfo displayInfo = mWm.getDefaultDisplayContentLocked().getDisplayInfo();
         final int logicalWidth = displayInfo.logicalWidth;
         final int logicalHeight = displayInfo.logicalHeight;
 
@@ -316,9 +316,9 @@
         final int taskRight = logicalWidth / 4 * 3;
         final int taskBottom = logicalHeight / 4 * 3;
         final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom);
-        TaskWithBounds task = new TaskWithBounds(taskBounds);
+        final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm, taskBounds);
         task.mFullscreenForTest = false;
-        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+        WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
@@ -367,7 +367,7 @@
     @Test
     public void testCalculatePolicyCrop() {
         final WindowStateWithTask w = createWindow(
-                new TaskWithBounds(null), FILL_PARENT, FILL_PARENT);
+                new TaskWithBounds(mStubStack, mWm, null), MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo();
@@ -423,7 +423,7 @@
     @Test
     public void testLayoutLetterboxedWindow() {
         // First verify task behavior in multi-window mode.
-        final DisplayInfo displayInfo = sWm.getDefaultDisplayContentLocked().getDisplayInfo();
+        final DisplayInfo displayInfo = mWm.getDefaultDisplayContentLocked().getDisplayInfo();
         final int logicalWidth = displayInfo.logicalWidth;
         final int logicalHeight = displayInfo.logicalHeight;
 
@@ -432,10 +432,10 @@
         final int taskRight = logicalWidth / 4 * 3;
         final int taskBottom = logicalHeight / 4 * 3;
         final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom);
-        TaskWithBounds task = new TaskWithBounds(taskBounds);
+        final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm, taskBounds);
         task.mInsetBounds.set(taskLeft, taskTop, taskRight, taskBottom);
         task.mFullscreenForTest = false;
-        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+        WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
@@ -467,8 +467,8 @@
     @Test
     public void testDisplayCutout() {
         // Regular fullscreen task and window
-        Task task = new TaskWithBounds(null);
-        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+        final Task task = new TaskWithBounds(mStubStack, mWm, null);
+        WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final Rect pf = new Rect(0, 0, 1000, 2000);
@@ -491,10 +491,11 @@
     @Test
     public void testDisplayCutout_tempInsetBounds() {
         // Regular fullscreen task and window
-        TaskWithBounds task = new TaskWithBounds(new Rect(0, -500, 1000, 1500));
+        final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm,
+                new Rect(0, -500, 1000, 1500));
         task.mFullscreenForTest = false;
         task.mInsetBounds.set(0, 0, 1000, 2000);
-        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+        WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final Rect pf = new Rect(0, -500, 1000, 1500);
@@ -519,7 +520,6 @@
         attrs.width = width;
         attrs.height = height;
 
-        return new WindowStateWithTask(attrs, task);
+        return new WindowStateWithTask(mWm, mIWindow, mWindowToken, attrs, task);
     }
-
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
index 012c4be..4e75ec9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -19,6 +19,8 @@
 import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -51,8 +53,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import androidx.test.InstrumentationRegistry;
-
 /**
  * A test rule that sets up a fresh WindowManagerService instance before each test and makes sure
  * to properly tear it down after.
@@ -89,7 +89,7 @@
             }
 
             private void setUp() {
-                final Context context = InstrumentationRegistry.getTargetContext();
+                final Context context = getInstrumentation().getTargetContext();
 
                 removeServices();
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
index 570a853..343d359 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
@@ -22,13 +22,14 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
-@RunWith(AndroidJUnit4.class)
+/**
+ * Build/InstallRun:
+ *  atest FrameworksServicesTests:WindowManagerServiceRuleTest
+ */
 @Presubmit
 @SmallTest
 public class WindowManagerServiceRuleTest {
@@ -40,4 +41,4 @@
     public void testWindowManagerSetUp() {
         assertThat(mRule.getWindowManagerService(), notNullValue());
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 3637baf..118ce89 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -58,32 +58,28 @@
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
 import com.android.server.wm.utils.WmDisplayCutout;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
-import java.util.Arrays;
 import java.util.LinkedList;
 
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
 /**
  * Tests for the {@link WindowState} class.
  *
- * atest FrameworksServicesTests:com.android.server.wm.WindowStateTests
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:WindowStateTests
  */
-@SmallTest
 @FlakyTest(bugId = 74078662)
-// TODO(b/116597907): Re-enable this test in postsubmit after the bug is fixed.
-// @Presubmit
-@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
 public class WindowStateTests extends WindowTestsBase {
 
     @Test
-    public void testIsParentWindowHidden() throws Exception {
+    public void testIsParentWindowHidden() {
         final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
         final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
         final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
@@ -98,11 +94,10 @@
         assertFalse(parentWindow.isParentWindowHidden());
         assertFalse(child1.isParentWindowHidden());
         assertFalse(child2.isParentWindowHidden());
-
     }
 
     @Test
-    public void testIsChildWindow() throws Exception {
+    public void testIsChildWindow() {
         final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
         final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
         final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
@@ -115,7 +110,7 @@
     }
 
     @Test
-    public void testHasChild() throws Exception {
+    public void testHasChild() {
         final WindowState win1 = createWindow(null, TYPE_APPLICATION, "win1");
         final WindowState win11 = createWindow(win1, FIRST_SUB_WINDOW, "win11");
         final WindowState win12 = createWindow(win1, FIRST_SUB_WINDOW, "win12");
@@ -136,7 +131,7 @@
     }
 
     @Test
-    public void testGetParentWindow() throws Exception {
+    public void testGetParentWindow() {
         final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
         final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
         final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
@@ -157,7 +152,7 @@
     }
 
     @Test
-    public void testGetTopParentWindow() throws Exception {
+    public void testGetTopParentWindow() {
         final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
         final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1");
         final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2");
@@ -183,7 +178,7 @@
     }
 
     @Test
-    public void testCanBeImeTarget() throws Exception {
+    public void testCanBeImeTarget() {
         final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
         final WindowState imeWindow = createWindow(null, TYPE_INPUT_METHOD, "imeWindow");
 
@@ -219,7 +214,7 @@
     }
 
     @Test
-    public void testGetWindow() throws Exception {
+    public void testGetWindow() {
         final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
         final WindowState mediaChild = createWindow(root, TYPE_APPLICATION_MEDIA, "mediaChild");
         final WindowState mediaOverlayChild = createWindow(root,
@@ -231,7 +226,7 @@
         final WindowState aboveSubPanelChild = createWindow(root,
                 TYPE_APPLICATION_ABOVE_SUB_PANEL, "aboveSubPanelChild");
 
-        final LinkedList<WindowState> windows = new LinkedList();
+        final LinkedList<WindowState> windows = new LinkedList<>();
 
         root.getWindow(w -> {
             windows.addLast(w);
@@ -249,7 +244,7 @@
     }
 
     @Test
-    public void testPrepareWindowToDisplayDuringRelayout() throws Exception {
+    public void testPrepareWindowToDisplayDuringRelayout() {
         testPrepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
         testPrepareWindowToDisplayDuringRelayout(true /*wasVisible*/);
 
@@ -262,14 +257,14 @@
         final WindowState second = createWindow(null, TYPE_APPLICATION, appWindowToken, "second");
         second.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
 
-        reset(mPowerManagerWrapper);
+        reset(sPowerManagerWrapper);
         first.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(mPowerManagerWrapper, never()).wakeUp(anyLong(), anyString());
+        verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyString());
         assertTrue(appWindowToken.canTurnScreenOn());
 
-        reset(mPowerManagerWrapper);
+        reset(sPowerManagerWrapper);
         second.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(mPowerManagerWrapper).wakeUp(anyLong(), anyString());
+        verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString());
         assertFalse(appWindowToken.canTurnScreenOn());
 
         // Call prepareWindowToDisplayDuringRelayout for two window that have FLAG_TURN_SCREEN_ON
@@ -278,14 +273,14 @@
         first.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
         second.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
 
-        reset(mPowerManagerWrapper);
+        reset(sPowerManagerWrapper);
         first.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(mPowerManagerWrapper).wakeUp(anyLong(), anyString());
+        verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString());
         assertFalse(appWindowToken.canTurnScreenOn());
 
-        reset(mPowerManagerWrapper);
+        reset(sPowerManagerWrapper);
         second.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(mPowerManagerWrapper, never()).wakeUp(anyLong(), anyString());
+        verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyString());
         assertFalse(appWindowToken.canTurnScreenOn());
 
         // Call prepareWindowToDisplayDuringRelayout for a windows that are not children of an
@@ -299,17 +294,17 @@
         firstWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
         secondWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
 
-        reset(mPowerManagerWrapper);
+        reset(sPowerManagerWrapper);
         firstWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(mPowerManagerWrapper).wakeUp(anyLong(), anyString());
+        verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString());
 
-        reset(mPowerManagerWrapper);
+        reset(sPowerManagerWrapper);
         secondWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(mPowerManagerWrapper).wakeUp(anyLong(), anyString());
+        verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString());
     }
 
     @Test
-    public void testCanAffectSystemUiFlags() throws Exception {
+    public void testCanAffectSystemUiFlags() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         app.mToken.setHidden(false);
         assertTrue(app.canAffectSystemUiFlags());
@@ -318,11 +313,10 @@
         app.mToken.setHidden(false);
         app.mAttrs.alpha = 0.0f;
         assertFalse(app.canAffectSystemUiFlags());
-
     }
 
     @Test
-    public void testCanAffectSystemUiFlags_disallow() throws Exception {
+    public void testCanAffectSystemUiFlags_disallow() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         app.mToken.setHidden(false);
         assertTrue(app.canAffectSystemUiFlags());
@@ -331,7 +325,7 @@
     }
 
     @Test
-    public void testIsSelfOrAncestorWindowAnimating() throws Exception {
+    public void testIsSelfOrAncestorWindowAnimating() {
         final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
         final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1");
         final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2");
@@ -344,7 +338,7 @@
     }
 
     @Test
-    public void testLayoutSeqResetOnReparent() throws Exception {
+    public void testLayoutSeqResetOnReparent() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         app.mLayoutSeq = 1;
         mDisplayContent.mLayoutSeq = 1;
@@ -355,7 +349,7 @@
     }
 
     @Test
-    public void testDisplayIdUpdatedOnReparent() throws Exception {
+    public void testDisplayIdUpdatedOnReparent() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         // fake a different display
         app.mInputWindowHandle.displayId = mDisplayContent.getDisplayId() + 1;
@@ -418,11 +412,11 @@
     }
 
     private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) {
-        reset(mPowerManagerWrapper);
+        reset(sPowerManagerWrapper);
         final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
         root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
 
         root.prepareWindowToDisplayDuringRelayout(wasVisible /*wasVisible*/);
-        verify(mPowerManagerWrapper).wakeUp(anyLong(), anyString());
+        verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index e155be4..9e12f02 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -11,11 +11,26 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
 
+import static android.app.AppOpsManager.OP_NONE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -26,24 +41,9 @@
 import android.view.IApplicationToken;
 import android.view.IWindow;
 import android.view.Surface;
-import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowManager;
 
-import static android.app.AppOpsManager.OP_NONE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyFloat;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
 import org.mockito.invocation.InvocationOnMock;
 
 /**
@@ -277,35 +277,33 @@
      */
     public static class TestTaskWindowContainerController extends TaskWindowContainerController {
 
+        static final TaskWindowContainerListener NOP_LISTENER = new TaskWindowContainerListener() {
+            @Override
+            public void registerConfigurationChangeListener(
+                    ConfigurationContainerListener listener) {
+            }
+
+            @Override
+            public void unregisterConfigurationChangeListener(
+                    ConfigurationContainerListener listener) {
+            }
+
+            @Override
+            public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) {
+            }
+
+            @Override
+            public void requestResize(Rect bounds, int resizeMode) {
+            }
+        };
+
         TestTaskWindowContainerController(WindowTestsBase testsBase) {
             this(testsBase.createStackControllerOnDisplay(testsBase.mDisplayContent));
         }
 
         TestTaskWindowContainerController(StackWindowController stackController) {
-            super(sNextTaskId++, new TaskWindowContainerListener() {
-                        @Override
-                        public void registerConfigurationChangeListener(
-                                ConfigurationContainerListener listener) {
-
-                        }
-
-                        @Override
-                        public void unregisterConfigurationChangeListener(
-                                ConfigurationContainerListener listener) {
-
-                        }
-
-                        @Override
-                        public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) {
-
-                        }
-
-                        @Override
-                        public void requestResize(Rect bounds, int resizeMode) {
-
-                        }
-                    }, stackController, 0 /* userId */, null /* bounds */, RESIZE_MODE_UNRESIZEABLE,
-                    false /* supportsPictureInPicture */, true /* toTop*/,
+            super(sNextTaskId++, NOP_LISTENER, stackController, 0 /* userId */, null /* bounds */,
+                    RESIZE_MODE_UNRESIZEABLE, false /* supportsPictureInPicture */, true /* toTop*/,
                     true /* showForAllUsers */, new ActivityManager.TaskDescription(),
                     stackController.mService);
         }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 73bb1c9..945cbb9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -35,6 +35,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static org.mockito.Mockito.mock;
 
 import android.content.Context;
@@ -52,13 +54,12 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Rule;
 
 import java.util.HashSet;
 import java.util.LinkedList;
 
-import androidx.test.InstrumentationRegistry;
-
 /**
  * Common base class for window manager unit test classes.
  *
@@ -66,7 +67,8 @@
  */
 class WindowTestsBase {
     private static final String TAG = WindowTestsBase.class.getSimpleName();
-    WindowManagerService sWm = null;  // TODO(roosa): rename to mWm in follow-up CL
+
+    WindowManagerService mWm;
     private final IWindow mIWindow = new TestIWindow();
     private Session mMockSession;
     // The default display is removed in {@link #setUp} and then we iterate over all displays to
@@ -97,32 +99,36 @@
     @Rule
     public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule();
 
-    static WindowState.PowerManagerWrapper mPowerManagerWrapper;  // TODO(roosa): make non-static.
+    static WindowState.PowerManagerWrapper sPowerManagerWrapper;  // TODO(roosa): make non-static.
+
+    @BeforeClass
+    public static void setUpOnceBase() {
+        AttributeCache.init(getInstrumentation().getTargetContext());
+        sPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
+    }
 
     @Before
-    public void setUp() throws Exception {
+    public void setUpBase() {
         // If @Before throws an exception, the error isn't logged. This will make sure any failures
         // in the set up are clear. This can be removed when b/37850063 is fixed.
         try {
             mMockSession = mock(Session.class);
-            mPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
 
-            final Context context = InstrumentationRegistry.getTargetContext();
-            AttributeCache.init(context);
+            final Context context = getInstrumentation().getTargetContext();
 
-            sWm = mWmRule.getWindowManagerService();
+            mWm = mWmRule.getWindowManagerService();
             beforeCreateDisplay();
 
-            mWallpaperController = new WallpaperController(sWm);
+            mWallpaperController = new WallpaperController(mWm);
 
             context.getDisplay().getDisplayInfo(mDisplayInfo);
             mDisplayContent = createNewDisplay();
-            sWm.mDisplayEnabled = true;
-            sWm.mDisplayReady = true;
+            mWm.mDisplayEnabled = true;
+            mWm.mDisplayReady = true;
 
             // Set-up some common windows.
-            mCommonWindows = new HashSet();
-            synchronized (sWm.mGlobalLock) {
+            mCommonWindows = new HashSet<>();
+            synchronized (mWm.mGlobalLock) {
                 mWallpaperWindow = createCommonWindow(null, TYPE_WALLPAPER, "wallpaperWindow");
                 mImeWindow = createCommonWindow(null, TYPE_INPUT_METHOD, "mImeWindow");
                 mDisplayContent.mInputMethodWindow = mImeWindow;
@@ -154,7 +160,7 @@
     }
 
     @After
-    public void tearDown() throws Exception {
+    public void tearDownBase() {
         // If @After throws an exception, the error isn't logged. This will make sure any failures
         // in the tear down are clear. This can be removed when b/37850063 is fixed.
         try {
@@ -164,8 +170,8 @@
 
             final LinkedList<WindowState> nonCommonWindows = new LinkedList<>();
 
-            synchronized (sWm.mGlobalLock) {
-                sWm.mRoot.forAllWindows(w -> {
+            synchronized (mWm.mGlobalLock) {
+                mWm.mRoot.forAllWindows(w -> {
                     if (!mCommonWindows.contains(w)) {
                         nonCommonWindows.addLast(w);
                     }
@@ -175,18 +181,18 @@
                     nonCommonWindows.pollLast().removeImmediately();
                 }
 
-                for (int i = sWm.mRoot.mChildren.size() - 1; i >= 0; --i) {
-                    final DisplayContent displayContent = sWm.mRoot.mChildren.get(i);
+                for (int i = mWm.mRoot.mChildren.size() - 1; i >= 0; --i) {
+                    final DisplayContent displayContent = mWm.mRoot.mChildren.get(i);
                     if (!displayContent.isDefaultDisplay) {
                         displayContent.removeImmediately();
                     }
                 }
                 // Remove app transition & window freeze timeout callbacks to prevent unnecessary
                 // actions after test.
-                sWm.getDefaultDisplayContentLocked().mAppTransition
+                mWm.getDefaultDisplayContentLocked().mAppTransition
                         .removeAppTransitionTimeoutCallbacks();
-                sWm.mH.removeMessages(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT);
-                sWm.mInputMethodTarget = null;
+                mWm.mH.removeMessages(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT);
+                mWm.mInputMethodTarget = null;
             }
 
             // Wait until everything is really cleaned up.
@@ -198,7 +204,7 @@
     }
 
     private WindowState createCommonWindow(WindowState parent, int type, String name) {
-        synchronized (sWm.mGlobalLock) {
+        synchronized (mWm.mGlobalLock) {
             final WindowState win = createWindow(parent, type, name);
             mCommonWindows.add(win);
             // Prevent common windows from been IMe targets
@@ -216,7 +222,7 @@
 
     private WindowToken createWindowToken(
             DisplayContent dc, int windowingMode, int activityType, int type) {
-        synchronized (sWm.mGlobalLock) {
+        synchronized (mWm.mGlobalLock) {
             if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) {
                 return WindowTestUtils.createTestWindowToken(type, dc);
             }
@@ -241,7 +247,7 @@
     }
 
     WindowState createWindow(WindowState parent, int type, String name) {
-        synchronized (sWm.mGlobalLock) {
+        synchronized (mWm.mGlobalLock) {
             return (parent == null)
                     ? createWindow(parent, type, mDisplayContent, name)
                     : createWindow(parent, type, parent.mToken, name);
@@ -250,14 +256,14 @@
 
     WindowState createWindowOnStack(WindowState parent, int windowingMode, int activityType,
             int type, DisplayContent dc, String name) {
-        synchronized (sWm.mGlobalLock) {
+        synchronized (mWm.mGlobalLock) {
             final WindowToken token = createWindowToken(dc, windowingMode, activityType, type);
             return createWindow(parent, type, token, name);
         }
     }
 
     WindowState createAppWindow(Task task, int type, String name) {
-        synchronized (sWm.mGlobalLock) {
+        synchronized (mWm.mGlobalLock) {
             final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
             task.addChild(token, 0);
             return createWindow(null, type, token, name);
@@ -265,7 +271,7 @@
     }
 
     WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) {
-        synchronized (sWm.mGlobalLock) {
+        synchronized (mWm.mGlobalLock) {
             final WindowToken token = createWindowToken(
                     dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
             return createWindow(parent, type, token, name);
@@ -274,7 +280,7 @@
 
     WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name,
             boolean ownerCanAddInternalSystemWindow) {
-        synchronized (sWm.mGlobalLock) {
+        synchronized (mWm.mGlobalLock) {
             final WindowToken token = createWindowToken(
                     dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
             return createWindow(parent, type, token, name, 0 /* ownerId */,
@@ -283,7 +289,7 @@
     }
 
     WindowState createWindow(WindowState parent, int type, WindowToken token, String name) {
-        synchronized (sWm.mGlobalLock) {
+        synchronized (mWm.mGlobalLock) {
             return createWindow(parent, type, token, name, 0 /* ownerId */,
                     false /* ownerCanAddInternalSystemWindow */);
         }
@@ -292,7 +298,7 @@
     WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
             int ownerId, boolean ownerCanAddInternalSystemWindow) {
         return createWindow(parent, type, token, name, ownerId, ownerCanAddInternalSystemWindow,
-                sWm, mMockSession, mIWindow);
+                mWm, mMockSession, mIWindow);
     }
 
     static WindowState createWindow(WindowState parent, int type, WindowToken token,
@@ -305,7 +311,7 @@
             final WindowState w = new WindowState(service, session, iWindow, token, parent,
                     OP_NONE,
                     0, attrs, VISIBLE, ownerId, ownerCanAddInternalSystemWindow,
-                    mPowerManagerWrapper);
+                    sPowerManagerWrapper);
             // TODO: Probably better to make this call in the WindowState ctor to avoid errors with
             // adding it to the token...
             token.addWindow(w);
@@ -315,13 +321,13 @@
 
     /** Creates a {@link TaskStack} and adds it to the specified {@link DisplayContent}. */
     TaskStack createTaskStackOnDisplay(DisplayContent dc) {
-        synchronized (sWm.mGlobalLock) {
+        synchronized (mWm.mGlobalLock) {
             return createStackControllerOnDisplay(dc).mContainer;
         }
     }
 
     StackWindowController createStackControllerOnDisplay(DisplayContent dc) {
-        synchronized (sWm.mGlobalLock) {
+        synchronized (mWm.mGlobalLock) {
             return createStackControllerOnStackOnDisplay(
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc);
         }
@@ -329,13 +335,13 @@
 
     StackWindowController createStackControllerOnStackOnDisplay(
             int windowingMode, int activityType, DisplayContent dc) {
-        synchronized (sWm.mGlobalLock) {
+        synchronized (mWm.mGlobalLock) {
             final Configuration overrideConfig = new Configuration();
             overrideConfig.windowConfiguration.setWindowingMode(windowingMode);
             overrideConfig.windowConfiguration.setActivityType(activityType);
             final int stackId = ++sNextStackId;
             final StackWindowController controller = new StackWindowController(stackId, null,
-                    dc.getDisplayId(), true /* onTop */, new Rect(), sWm);
+                    dc.getDisplayId(), true /* onTop */, new Rect(), mWm);
             controller.onOverrideConfigurationChanged(overrideConfig);
             return controller;
         }
@@ -343,7 +349,7 @@
 
     /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
     Task createTaskInStack(TaskStack stack, int userId) {
-        return WindowTestUtils.createTaskInStack(sWm, stack, userId);
+        return WindowTestUtils.createTaskInStack(mWm, stack, userId);
     }
 
     /** Creates a {@link DisplayContent} and adds it to the system. */
@@ -351,8 +357,8 @@
         final int displayId = sNextDisplayId++;
         final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                 mDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
-        synchronized (sWm.mGlobalLock) {
-            return new DisplayContent(display, sWm, mWallpaperController,
+        synchronized (mWm.mGlobalLock) {
+            return new DisplayContent(display, mWm, mWallpaperController,
                     mock(DisplayWindowController.class));
         }
     }
@@ -375,20 +381,19 @@
         final int displayId = sNextDisplayId++;
         final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                 displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
-        final DisplayWindowController dcw = new DisplayWindowController(display, sWm);
-        synchronized (sWm.mGlobalLock) {
+        final DisplayWindowController dcw = new DisplayWindowController(display, mWm);
+        synchronized (mWm.mGlobalLock) {
             // Display creation is driven by DisplayWindowController via ActivityStackSupervisor.
             // We skip those steps here.
-            return sWm.mRoot.createDisplayContent(display, dcw);
+            return mWm.mRoot.createDisplayContent(display, dcw);
         }
     }
 
     /** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */
     WindowTestUtils.TestWindowState createWindowState(WindowManager.LayoutParams attrs,
             WindowToken token) {
-        synchronized (sWm.mGlobalLock) {
-            return new WindowTestUtils.TestWindowState(sWm, mMockSession, mIWindow, attrs, token);
+        synchronized (mWm.mGlobalLock) {
+            return new WindowTestUtils.TestWindowState(mWm, mMockSession, mIWindow, attrs, token);
         }
     }
-
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
index 3732486..3048f1a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -30,25 +30,22 @@
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link WindowToken} class.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.WindowTokenTests
+ *  atest FrameworksServicesTests:WindowTokenTests
  */
-@SmallTest
 @FlakyTest(bugId = 74078662)
+@SmallTest
 @Presubmit
-@RunWith(AndroidJUnit4.class)
 public class WindowTokenTests extends WindowTestsBase {
 
     @Test
-    public void testAddWindow() throws Exception {
+    public void testAddWindow() {
         final WindowTestUtils.TestWindowToken token =
                 WindowTestUtils.createTestWindowToken(0, mDisplayContent);
 
@@ -78,7 +75,7 @@
     }
 
     @Test
-    public void testChildRemoval() throws Exception {
+    public void testChildRemoval() {
         final DisplayContent dc = mDisplayContent;
         final WindowTestUtils.TestWindowToken token = WindowTestUtils.createTestWindowToken(0, dc);
 
@@ -102,7 +99,7 @@
      * Tokens should only be removed from the system when all their windows are gone.
      */
     @Test
-    public void testTokenRemovalProcess() throws Exception {
+    public void testTokenRemovalProcess() {
         final WindowTestUtils.TestWindowToken token = WindowTestUtils.createTestWindowToken(
                 TYPE_TOAST, mDisplayContent, true /* persistOnEmpty */);
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
index 3a8c4ae..32e4e02 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -38,27 +38,30 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
 import org.junit.After;
 import org.junit.Test;
 
 import java.util.HashMap;
 import java.util.LinkedList;
 
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
 /**
- * Tests for the {@link WindowLayersController} class.
+ * Tests for the {@link DisplayContent#assignChildLayers(SurfaceControl.Transaction)} method.
  *
  * Build/Install/Run:
- *  atest FrameworksServicesTests:com.android.server.wm.ZOrderingTests
+ *  atest FrameworksServicesTests:ZOrderingTests
  */
-@SmallTest
 @FlakyTest(bugId = 74078662)
+@SmallTest
 @Presubmit
 public class ZOrderingTests extends WindowTestsBase {
 
-    private class LayerRecordingTransaction extends SurfaceControl.Transaction {
+    private static class LayerRecordingTransaction extends SurfaceControl.Transaction {
+        // We have WM use our Hierarchy recording subclass of SurfaceControl.Builder
+        // such that we can keep track of the parents of Surfaces as they are constructed.
+        private final HashMap<SurfaceControl, SurfaceControl> mParentFor = new HashMap<>();
         HashMap<SurfaceControl, Integer> mLayersForControl = new HashMap<>();
         HashMap<SurfaceControl, SurfaceControl> mRelativeLayersForControl = new HashMap<>();
 
@@ -85,17 +88,28 @@
         private SurfaceControl getRelativeLayer(SurfaceControl sc) {
             return mRelativeLayersForControl.get(sc);
         }
+
+        void addParentFor(SurfaceControl child, SurfaceControl parent) {
+            mParentFor.put(child, parent);
+        }
+
+        SurfaceControl getParentFor(SurfaceControl child) {
+            return mParentFor.get(child);
+        }
+
+        @Override
+        public void close() {
+
+        }
     }
 
-    // We have WM use our Hierarchy recording subclass of SurfaceControl.Builder
-    // such that we can keep track of the parents of Surfaces as they are constructed.
-    private HashMap<SurfaceControl, SurfaceControl> mParentFor = new HashMap<>();
+    private static class HierarchyRecorder extends SurfaceControl.Builder {
+        private LayerRecordingTransaction mTransaction;
+        private SurfaceControl mPendingParent;
 
-    private class HierarchyRecorder extends SurfaceControl.Builder {
-        SurfaceControl mPendingParent;
-
-        HierarchyRecorder(SurfaceSession s) {
+        HierarchyRecorder(SurfaceSession s, LayerRecordingTransaction transaction) {
             super(s);
+            mTransaction = transaction;
         }
 
         @Override
@@ -106,16 +120,26 @@
 
         @Override
         public SurfaceControl build() {
-            SurfaceControl sc = super.build();
-            mParentFor.put(sc, mPendingParent);
+            final SurfaceControl sc = super.build();
+            mTransaction.addParentFor(sc, mPendingParent);
+            mTransaction = null;
             mPendingParent = null;
             return sc;
         }
     }
 
-    private class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
+    private static class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
+        private LayerRecordingTransaction mTransaction;
+
+        HierarchyRecordingBuilderFactory(LayerRecordingTransaction transaction) {
+            mTransaction = transaction;
+        }
+
+        @Override
         public SurfaceControl.Builder make(SurfaceSession s) {
-            return new HierarchyRecorder(s);
+            final LayerRecordingTransaction transaction = mTransaction;
+            mTransaction = null;
+            return new HierarchyRecorder(s, transaction);
         }
     }
 
@@ -127,18 +151,17 @@
         // which is after construction of the DisplayContent, meaning the HierarchyRecorder
         // would miss construction of the top-level layers.
         mTransaction = new LayerRecordingTransaction();
-        sWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory();
-        sWm.mTransactionFactory = () -> mTransaction;
+        mWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory(mTransaction);
+        mWm.mTransactionFactory = () -> mTransaction;
     }
 
     @After
-    public void after() {
+    public void tearDown() {
         mTransaction.close();
-        mParentFor.keySet().forEach(SurfaceControl::destroy);
-        mParentFor.clear();
     }
 
-    LinkedList<SurfaceControl> getAncestors(LayerRecordingTransaction t, SurfaceControl sc) {
+    private static LinkedList<SurfaceControl> getAncestors(LayerRecordingTransaction t,
+            SurfaceControl sc) {
         LinkedList<SurfaceControl> p = new LinkedList<>();
         SurfaceControl current = sc;
         do {
@@ -148,23 +171,22 @@
             if (rs != null) {
                 current = rs;
             } else {
-                current = mParentFor.get(current);
+                current = t.getParentFor(current);
             }
         } while (current != null);
         return p;
     }
 
 
-    void assertZOrderGreaterThan(LayerRecordingTransaction t, SurfaceControl left,
+    private static void assertZOrderGreaterThan(LayerRecordingTransaction t, SurfaceControl left,
             SurfaceControl right) {
         final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left);
         final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right);
 
-        SurfaceControl commonAncestor = null;
         SurfaceControl leftTop = leftParentChain.peekLast();
         SurfaceControl rightTop = rightParentChain.peekLast();
         while (leftTop != null && rightTop != null && leftTop == rightTop) {
-            commonAncestor = leftParentChain.removeLast();
+            leftParentChain.removeLast();
             rightParentChain.removeLast();
             leftTop = leftParentChain.peekLast();
             rightTop = rightParentChain.peekLast();
@@ -189,7 +211,7 @@
 
     @Test
     public void testAssignWindowLayers_ForImeWithNoTarget() {
-        sWm.mInputMethodTarget = null;
+        mWm.mInputMethodTarget = null;
         mDisplayContent.assignChildLayers(mTransaction);
 
         // The Ime has an higher base layer than app windows and lower base layer than system
@@ -207,7 +229,7 @@
     @Test
     public void testAssignWindowLayers_ForImeWithAppTarget() {
         final WindowState imeAppTarget = createWindow("imeAppTarget");
-        sWm.mInputMethodTarget = imeAppTarget;
+        mWm.mInputMethodTarget = imeAppTarget;
 
         mDisplayContent.assignChildLayers(mTransaction);
 
@@ -233,7 +255,7 @@
                 TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
                 "imeAppTargetChildBelowWindow");
 
-        sWm.mInputMethodTarget = imeAppTarget;
+        mWm.mInputMethodTarget = imeAppTarget;
         mDisplayContent.assignChildLayers(mTransaction);
 
         // Ime should be above all app windows except for child windows that are z-ordered above it
@@ -255,7 +277,7 @@
         final WindowState imeAppTarget = createWindow("imeAppTarget");
         final WindowState appAboveImeTarget = createWindow("appAboveImeTarget");
 
-        sWm.mInputMethodTarget = imeAppTarget;
+        mWm.mInputMethodTarget = imeAppTarget;
         mDisplayContent.assignChildLayers(mTransaction);
 
         // Ime should be above all app windows except for non-fullscreen app window above it and
@@ -278,7 +300,7 @@
                 mDisplayContent, "imeSystemOverlayTarget",
                 true /* ownerCanAddInternalSystemWindow */);
 
-        sWm.mInputMethodTarget = imeSystemOverlayTarget;
+        mWm.mInputMethodTarget = imeSystemOverlayTarget;
         mDisplayContent.assignChildLayers(mTransaction);
 
         // The IME target base layer is higher than all window except for the nav bar window, so the
@@ -301,7 +323,7 @@
 
     @Test
     public void testAssignWindowLayers_ForStatusBarImeTarget() {
-        sWm.mInputMethodTarget = mStatusBarWindow;
+        mWm.mInputMethodTarget = mStatusBarWindow;
         mDisplayContent.assignChildLayers(mTransaction);
 
         assertWindowHigher(mImeWindow, mChildAppWindowAbove);
@@ -322,8 +344,8 @@
         final WindowState dockedStackWindow = createWindowOnStack(null,
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
                 mDisplayContent, "dockedStackWindow");
-        final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
+        final WindowState assistantStackWindow = createWindowOnStack(null,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
                 mDisplayContent, "assistantStackWindow");
         final WindowState homeActivityWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_HOME, TYPE_BASE_APPLICATION,
@@ -368,7 +390,8 @@
         final WindowState anyWindow = createWindow("anyWindow");
         final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent,
                 "TypeApplicationMediaChild");
-        final WindowState mediaOverlayChild = createWindow(anyWindow, TYPE_APPLICATION_MEDIA_OVERLAY,
+        final WindowState mediaOverlayChild = createWindow(anyWindow,
+                TYPE_APPLICATION_MEDIA_OVERLAY,
                 mDisplayContent, "TypeApplicationMediaOverlayChild");
 
         mDisplayContent.assignChildLayers(mTransaction);
@@ -388,8 +411,8 @@
         final WindowState splitScreenSecondaryWindow = createWindowOnStack(null,
                 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
                 TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow");
-        final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
+        final WindowState assistantStackWindow = createWindowOnStack(null,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
                 mDisplayContent, "assistantStackWindow");
 
         mDisplayContent.assignChildLayers(mTransaction);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index b19cc86..38d8e39 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -21,13 +21,14 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 
-import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
 import static junit.framework.TestCase.assertTrue;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doNothing;
@@ -45,13 +46,14 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
-import android.media.VolumePolicy;
 import android.media.AudioSystem;
+import android.media.VolumePolicy;
 import android.net.Uri;
 import android.provider.Settings;
 import android.provider.Settings.Global;
@@ -67,9 +69,9 @@
 
 import com.android.internal.R;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.server.notification.ManagedServices.UserProfiles;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.server.UiServiceTestCase;
+import com.android.server.notification.ManagedServices.UserProfiles;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -87,12 +89,16 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
+import java.util.Objects;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class ZenModeHelperTest extends UiServiceTestCase {
 
+    private static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
+    private static final String SCHEDULE_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
+
     ConditionProviders mConditionProviders;
     @Mock NotificationManager mNotificationManager;
     private Resources mResources;
@@ -108,7 +114,6 @@
         mTestableLooper = TestableLooper.get(this);
         mContext = spy(getContext());
         mContentResolver = mContext.getContentResolver();
-
         mResources = spy(mContext.getResources());
         try {
             when(mResources.getXml(R.xml.default_zen_mode_config)).thenReturn(
@@ -132,12 +137,13 @@
                 + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
                 + "visualScreenOff=\"true\" alarms=\"true\" "
                 + "media=\"true\" system=\"false\" />\n"
-                + "<automatic ruleId=\"EVENTS_DEFAULT_RULE\" enabled=\"false\" snoozing=\"false\""
+                + "<automatic ruleId=\"" + EVENTS_DEFAULT_RULE_ID
+                + "\" enabled=\"false\" snoozing=\"false\""
                 + " name=\"Event\" zen=\"1\""
                 + " component=\"android/com.android.server.notification.EventConditionProvider\""
                 + " conditionId=\"condition://android/event?userId=-10000&amp;calendar=&amp;"
                 + "reply=1\"/>\n"
-                + "<automatic ruleId=\"EVERY_NIGHT_DEFAULT_RULE\" enabled=\"false\""
+                + "<automatic ruleId=\"" + SCHEDULE_DEFAULT_RULE_ID + "\" enabled=\"false\""
                 + " snoozing=\"false\" name=\"Sleeping\" zen=\"1\""
                 + " component=\"android/com.android.server.notification.ScheduleConditionProvider\""
                 + " conditionId=\"condition://android/schedule?days=1.2.3.4.5.6.7 &amp;start=22.0"
@@ -770,8 +776,8 @@
         mZenModeHelperSpy.readXml(parser, false);
 
         assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
-                | SUPPRESSED_EFFECT_LIGHTS
-                | SUPPRESSED_EFFECT_PEEK,
+                        | SUPPRESSED_EFFECT_LIGHTS
+                        | SUPPRESSED_EFFECT_PEEK,
                 mZenModeHelperSpy.mConfig.suppressedVisualEffects);
 
         xml = "<zen version=\"6\" user=\"0\">\n"
@@ -1007,6 +1013,90 @@
         mZenModeHelperSpy.updateDefaultZenRules(); // shouldn't throw null pointer
     }
 
+    @Test
+    public void testDoNotUpdateModifiedDefaultAutoRule() {
+        // mDefaultConfig is set to default config in setup by getDefaultConfigParser
+        when(mContext.checkCallingPermission(anyString()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+        // shouldn't update rule that's been modified
+        ZenModeConfig.ZenRule updatedDefaultRule = new ZenModeConfig.ZenRule();
+        updatedDefaultRule.modified = true;
+        updatedDefaultRule.enabled = false;
+        updatedDefaultRule.creationTime = 0;
+        updatedDefaultRule.id = SCHEDULE_DEFAULT_RULE_ID;
+        updatedDefaultRule.name = "Schedule Default Rule";
+        updatedDefaultRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        updatedDefaultRule.conditionId = ZenModeConfig.toScheduleConditionId(new ScheduleInfo());
+        updatedDefaultRule.component = new ComponentName("android", "ScheduleConditionProvider");
+
+        ArrayMap<String, ZenModeConfig.ZenRule> autoRules = new ArrayMap<>();
+        autoRules.put(SCHEDULE_DEFAULT_RULE_ID, updatedDefaultRule);
+        mZenModeHelperSpy.mConfig.automaticRules = autoRules;
+
+        mZenModeHelperSpy.updateDefaultZenRules();
+        assertEquals(updatedDefaultRule,
+                mZenModeHelperSpy.mConfig.automaticRules.get(SCHEDULE_DEFAULT_RULE_ID));
+    }
+
+    @Test
+    public void testDoNotUpdateEnabledDefaultAutoRule() {
+        // mDefaultConfig is set to default config in setup by getDefaultConfigParser
+        when(mContext.checkCallingPermission(anyString()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+        // shouldn't update the rule that's enabled
+        ZenModeConfig.ZenRule updatedDefaultRule = new ZenModeConfig.ZenRule();
+        updatedDefaultRule.enabled = true;
+        updatedDefaultRule.modified = false;
+        updatedDefaultRule.creationTime = 0;
+        updatedDefaultRule.id = SCHEDULE_DEFAULT_RULE_ID;
+        updatedDefaultRule.name = "Schedule Default Rule";
+        updatedDefaultRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        updatedDefaultRule.conditionId = ZenModeConfig.toScheduleConditionId(new ScheduleInfo());
+        updatedDefaultRule.component = new ComponentName("android", "ScheduleConditionProvider");
+
+        ArrayMap<String, ZenModeConfig.ZenRule> autoRules = new ArrayMap<>();
+        autoRules.put(SCHEDULE_DEFAULT_RULE_ID, updatedDefaultRule);
+        mZenModeHelperSpy.mConfig.automaticRules = autoRules;
+
+        mZenModeHelperSpy.updateDefaultZenRules();
+        assertEquals(updatedDefaultRule,
+                mZenModeHelperSpy.mConfig.automaticRules.get(SCHEDULE_DEFAULT_RULE_ID));
+    }
+
+    @Test
+    public void testUpdateDefaultAutoRule() {
+        // mDefaultConfig is set to default config in setup by getDefaultConfigParser
+        final String defaultRuleName = "rule name test";
+        when(mContext.checkCallingPermission(anyString()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+        // will update rule that is not enabled and modified
+        ZenModeConfig.ZenRule customDefaultRule = new ZenModeConfig.ZenRule();
+        customDefaultRule.enabled = false;
+        customDefaultRule.modified = false;
+        customDefaultRule.creationTime = 0;
+        customDefaultRule.id = SCHEDULE_DEFAULT_RULE_ID;
+        customDefaultRule.name = "Schedule Default Rule";
+        customDefaultRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customDefaultRule.conditionId = ZenModeConfig.toScheduleConditionId(new ScheduleInfo());
+        customDefaultRule.component = new ComponentName("android", "ScheduleConditionProvider");
+
+        ArrayMap<String, ZenModeConfig.ZenRule> autoRules = new ArrayMap<>();
+        autoRules.put(SCHEDULE_DEFAULT_RULE_ID, customDefaultRule);
+        mZenModeHelperSpy.mConfig.automaticRules = autoRules;
+
+        mZenModeHelperSpy.updateDefaultZenRules();
+        ZenModeConfig.ZenRule ruleAfterUpdating =
+                mZenModeHelperSpy.mConfig.automaticRules.get(SCHEDULE_DEFAULT_RULE_ID);
+        assertEquals(customDefaultRule.enabled, ruleAfterUpdating.enabled);
+        assertEquals(customDefaultRule.modified, ruleAfterUpdating.modified);
+        assertEquals(customDefaultRule.id, ruleAfterUpdating.id);
+        assertEquals(customDefaultRule.conditionId, ruleAfterUpdating.conditionId);
+        assertFalse(Objects.equals(defaultRuleName, ruleAfterUpdating.name)); // update name
+    }
+
     private void setupZenConfig() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConfig.allowAlarms = false;
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
similarity index 81%
rename from services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
index 01b7c4f..0445ea0 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTracingTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -29,21 +31,21 @@
 
 import android.content.Context;
 import android.platform.test.annotations.Presubmit;
+import android.testing.DexmakerShareClassLoaderRule;
 import android.util.proto.ProtoOutputStream;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.util.Preconditions;
-import com.android.server.wm.WindowManagerTraceProto;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
-import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -55,41 +57,44 @@
  * Test class for {@link WindowTracing}.
  *
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.WindowTracingTest
+ *  atest FrameworksServicesTests:WindowTracingTest
  */
-@SmallTest
 @FlakyTest(bugId = 74078662)
-// TODO(b/116597907): Re-enable this test in postsubmit after the bug is fixed.
-// @Presubmit
-@RunWith(AndroidJUnit4.class)
-public class WindowTracingTest extends WindowTestsBase {
+@SmallTest
+@Presubmit
+public class WindowTracingTest {
 
-    private static final byte[] MAGIC_HEADER = new byte[] {
-        0x9, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45,
+    private static final byte[] MAGIC_HEADER = new byte[]{
+            0x9, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45,
     };
 
-    private Context mTestContext;
-    private WindowTracing mWindowTracing;
+    @Rule
+    public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+            new DexmakerShareClassLoaderRule();
+
+    @Mock
     private WindowManagerService mWmMock;
+    private WindowTracing mWindowTracing;
     private File mFile;
 
-    @Override
     @Before
     public void setUp() throws Exception {
-        super.setUp();
+        MockitoAnnotations.initMocks(this);
 
-        mWmMock = mock(WindowManagerService.class);
-
-        mTestContext = InstrumentationRegistry.getContext();
-
-        mFile = mTestContext.getFileStreamPath("tracing_test.dat");
+        final Context testContext = getInstrumentation().getContext();
+        mFile = testContext.getFileStreamPath("tracing_test.dat");
         mFile.delete();
 
         mWindowTracing = new WindowTracing(mFile);
     }
 
+    @After
+    public void tearDown() throws Exception {
+        mFile.delete();
+    }
+
     @Test
-    public void isEnabled_returnsFalseByDefault() throws Exception {
+    public void isEnabled_returnsFalseByDefault() {
         assertFalse(mWindowTracing.isEnabled());
     }
 
@@ -107,7 +112,7 @@
     }
 
     @Test
-    public void trace_discared_whenNotTracing() throws Exception {
+    public void trace_discared_whenNotTracing() {
         mWindowTracing.traceStateLocked("where", mWmMock);
         verifyZeroInteractions(mWmMock);
     }
@@ -132,12 +137,12 @@
         }
     }
 
-    @Test
     @Ignore("Figure out why this test is crashing when setting up mWmMock.")
+    @Test
     public void tracing_endsUpInFile() throws Exception {
         mWindowTracing.startTrace(mock(PrintWriter.class));
 
-        doAnswer((inv) -> {
+        doAnswer(inv -> {
             inv.<ProtoOutputStream>getArgument(0).write(
                     WindowManagerTraceProto.WHERE, "TEST_WM_PROTO");
             return null;
@@ -157,22 +162,14 @@
         }
     }
 
-    @Override
-    @After
-    public void tearDown() throws Exception {
-        super.tearDown();
-
-        mFile.delete();
-    }
-
     /** Return true if {@code needle} appears anywhere in {@code haystack[0..length]} */
-    boolean containsBytes(byte[] haystack, int haystackLenght, byte[] needle) {
-        Preconditions.checkArgument(haystackLenght > 0);
+    private static boolean containsBytes(byte[] haystack, int haystackLength, byte[] needle) {
+        Preconditions.checkArgument(haystackLength > 0);
         Preconditions.checkArgument(needle.length > 0);
 
-        outer: for (int i = 0; i <= haystackLenght - needle.length; i++) {
+        outer: for (int i = 0; i <= haystackLength - needle.length; i++) {
             for (int j = 0; j < needle.length; j++) {
-                if (haystack[i+j] != needle[j]) {
+                if (haystack[i + j] != needle[j]) {
                     continue outer;
                 }
             }
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 52ac32d..d7024cf 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
@@ -1271,6 +1272,31 @@
              */
             public static final String EXTRA_IS_INITIAL_CREATE =
                     "android.provider.extra.IS_INITIAL_CREATE";
+
+            /**
+             * Broadcast intent action indicating that the telephony provider SMS MMS database is
+             * corrupted. A boolean is specified in {@link #EXTRA_IS_CORRUPTED} to indicate if the
+             * database is corrupted. Requires the
+             * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE permission.
+             *
+             * @hide
+             */
+            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+            @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+            public static final String ACTION_SMS_MMS_DB_LOST =
+                    "android.provider.action.SMS_MMS_DB_LOST";
+
+            /**
+             * Boolean flag passed as an extra with {@link #ACTION_SMS_MMS_DB_LOST} to indicate
+             * whether the DB got corrupted or not.
+             *
+             * @see #ACTION_SMS_MMS_DB_LOST
+             *
+             * @hide
+             */
+            public static final String EXTRA_IS_CORRUPTED =
+                    "android.provider.extra.IS_CORRUPTED";
+
             /**
              * Read the PDUs out of an {@link #SMS_RECEIVED_ACTION} or a
              * {@link #DATA_SMS_RECEIVED_ACTION} intent.
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 05c1fd5..d6856b3 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -31,27 +31,8 @@
     private static final String LOG_TAG = "CellSignalStrengthLte";
     private static final boolean DBG = false;
 
-    /**
-     * Indicates the unknown or undetectable RSSI value in ASU.
-     *
-     * Reference: TS 27.007 8.5 - Signal quality +CSQ
-     */
-    private static final int SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN = 99;
-    /**
-     * Indicates the maximum valid RSSI value in ASU.
-     *
-     * Reference: TS 27.007 8.5 - Signal quality +CSQ
-     */
-    private static final int SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MAX_VALUE = 31;
-    /**
-     * Indicates the minimum valid RSSI value in ASU.
-     *
-     * Reference: TS 27.007 8.5 - Signal quality +CSQ
-     */
-    private static final int SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE = 0;
-
     @UnsupportedAppUsage
-    private int mRssi;
+    private int mSignalStrength;
     @UnsupportedAppUsage
     private int mRsrp;
     @UnsupportedAppUsage
@@ -70,9 +51,9 @@
     }
 
     /** @hide */
-    public CellSignalStrengthLte(int rssi, int rsrp, int rsrq, int rssnr, int cqi,
+    public CellSignalStrengthLte(int signalStrength, int rsrp, int rsrq, int rssnr, int cqi,
             int timingAdvance) {
-        mRssi = convertRssiAsuToDBm(rssi);
+        mSignalStrength = signalStrength;
         mRsrp = rsrp;
         mRsrq = rsrq;
         mRssnr = rssnr;
@@ -87,7 +68,7 @@
 
     /** @hide */
     protected void copyFrom(CellSignalStrengthLte s) {
-        mRssi = s.mRssi;
+        mSignalStrength = s.mSignalStrength;
         mRsrp = s.mRsrp;
         mRsrq = s.mRsrq;
         mRssnr = s.mRssnr;
@@ -104,7 +85,7 @@
     /** @hide */
     @Override
     public void setDefaultValues() {
-        mRssi = CellInfo.UNAVAILABLE;
+        mSignalStrength = CellInfo.UNAVAILABLE;
         mRsrp = CellInfo.UNAVAILABLE;
         mRsrq = CellInfo.UNAVAILABLE;
         mRssnr = CellInfo.UNAVAILABLE;
@@ -161,19 +142,6 @@
     }
 
     /**
-     * Get Received Signal Strength Indication (RSSI) in dBm
-     *
-     * The value range is [-113, -51] inclusively or {@link CellInfo#UNAVAILABLE} if unavailable.
-     *
-     * Reference: TS 27.007 8.5 Signal quality +CSQ
-     *
-     * @return the RSSI if available or {@link CellInfo#UNAVAILABLE} if unavailable.
-     */
-    public int getRssi() {
-        return mRssi;
-    }
-
-    /**
      * Get reference signal signal-to-noise ratio
      *
      * @return the RSSNR if available or
@@ -242,7 +210,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mRssi, mRsrp, mRsrq, mRssnr, mCqi, mTimingAdvance);
+        return Objects.hash(mSignalStrength, mRsrp, mRsrq, mRssnr, mCqi, mTimingAdvance);
     }
 
     @Override
@@ -259,7 +227,7 @@
             return false;
         }
 
-        return mRssi == s.mRssi
+        return mSignalStrength == s.mSignalStrength
                 && mRsrp == s.mRsrp
                 && mRsrq == s.mRsrq
                 && mRssnr == s.mRssnr
@@ -273,7 +241,7 @@
     @Override
     public String toString() {
         return "CellSignalStrengthLte:"
-                + " rssi(dBm)=" + mRssi
+                + " ss=" + mSignalStrength
                 + " rsrp=" + mRsrp
                 + " rsrq=" + mRsrq
                 + " rssnr=" + mRssnr
@@ -285,7 +253,7 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         if (DBG) log("writeToParcel(Parcel, int): " + toString());
-        dest.writeInt(mRssi);
+        dest.writeInt(mSignalStrength);
         // Need to multiply rsrp and rsrq by -1
         // to ensure consistency when reading values written here
         // unless the values are invalid
@@ -301,7 +269,7 @@
      * where the token is already been processed.
      */
     private CellSignalStrengthLte(Parcel in) {
-        mRssi = convertRssiAsuToDBm(in.readInt());
+        mSignalStrength = in.readInt();
         // rsrp and rsrq are written into the parcel as positive values.
         // Need to convert into negative values unless the values are invalid
         mRsrp = in.readInt();
@@ -341,17 +309,4 @@
     private static void log(String s) {
         Rlog.w(LOG_TAG, s);
     }
-
-    private static int convertRssiAsuToDBm(int rssiAsu) {
-        if (rssiAsu != SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN
-                && (rssiAsu < SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MIN_VALUE
-                || rssiAsu > SIGNAL_STRENGTH_LTE_RSSI_VALID_ASU_MAX_VALUE)) {
-            Rlog.e(LOG_TAG, "convertRssiAsuToDBm: invalid RSSI in ASU=" + rssiAsu);
-            return CellInfo.UNAVAILABLE;
-        }
-        if (rssiAsu == SIGNAL_STRENGTH_LTE_RSSI_ASU_UNKNOWN) {
-            return CellInfo.UNAVAILABLE;
-        }
-        return -113 + (2 * rssiAsu);
-    }
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e5c4ccd..8a77f14 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1238,38 +1238,6 @@
      */
     public static final String EXTRA_RECOVERY_ACTION = "recoveryAction";
 
-     /**
-     * Broadcast intent action indicating that the telephony provider DB got lost.
-     *
-     * <p>
-     * The {@link #EXTRA_IS_CORRUPTED} extra indicates whether the database is lost
-     * due to corruption or not
-     *
-     * <p class="note">
-     * Requires the MODIFY_PHONE_STATE permission.
-     *
-     * <p class="note">
-     * This is a protected intent that can only be sent by the system.
-     *
-     * @see #EXTRA_IS_CORRUPTED
-     *
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public static final String ACTION_MMSSMS_DATABASE_LOST =
-            "android.intent.action.MMSSMS_DATABASE_LOST";
-
-    /**
-     * A boolean extra used with {@link #ACTION_MMSSMS_DATABASE_LOST} to indicate
-     * whether the database is lost due to corruption or not.
-     *
-     * @see #ACTION_MMSSMS_DATABASE_LOST
-     *
-     * @hide
-     */
-    public static final String EXTRA_IS_CORRUPTED = "isCorrupted";
-
     //
     //
     // Device Info
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 045291b..529548f 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -594,17 +594,17 @@
             /** SSID of the network */
             public String ssid;
             /** Bitmask of the FLAG_XXX */
-            public byte flags;
+            public byte flags = 0;
             /** Bitmask of the ATUH_XXX */
-            public byte authBitField;
+            public byte authBitField = 0;
+            /** frequencies on which the particular network needs to be scanned for */
+            public int[] frequencies = {};
 
             /**
              * default constructor for PnoNetwork
              */
             public PnoNetwork(String ssid) {
                 this.ssid = ssid;
-                flags = 0;
-                authBitField = 0;
             }
         }
 
@@ -651,6 +651,7 @@
                     dest.writeString(networkList[i].ssid);
                     dest.writeByte(networkList[i].flags);
                     dest.writeByte(networkList[i].authBitField);
+                    dest.writeIntArray(networkList[i].frequencies);
                 }
             } else {
                 dest.writeInt(0);
@@ -677,6 +678,7 @@
                             PnoNetwork network = new PnoNetwork(ssid);
                             network.flags = in.readByte();
                             network.authBitField = in.readByte();
+                            network.frequencies = in.createIntArray();
                             settings.networkList[i] = network;
                         }
                         return settings;
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index 96d5a51..da42dcf 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -17,16 +17,20 @@
 package android.net.wifi;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.validateMockitoUsage;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.net.wifi.WifiScanner.PnoSettings;
+import android.net.wifi.WifiScanner.PnoSettings.PnoNetwork;
+import android.net.wifi.WifiScanner.ScanSettings;
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.test.TestLooper;
 import android.support.test.filters.SmallTest;
-import android.net.wifi.WifiScanner.ScanSettings;
 
 import com.android.internal.util.test.BidirectionalAsyncChannelServer;
 
@@ -36,6 +40,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Arrays;
+
 
 /**
  * Unit tests for {@link android.net.wifi.WifiScanner}.
@@ -47,6 +53,19 @@
     @Mock
     private IWifiScanner mService;
 
+    private static final boolean TEST_PNOSETTINGS_IS_CONNECTED = false;
+    private static final int TEST_PNOSETTINGS_MIN_5GHZ_RSSI = -60;
+    private static final int TEST_PNOSETTINGS_MIN_2GHZ_RSSI = -70;
+    private static final int TEST_PNOSETTINGS_INITIAL_SCORE_MAX = 50;
+    private static final int TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS = 10;
+    private static final int TEST_PNOSETTINGS_SAME_NETWORK_BONUS = 11;
+    private static final int TEST_PNOSETTINGS_SECURE_BONUS = 12;
+    private static final int TEST_PNOSETTINGS_BAND_5GHZ_BONUS = 13;
+    private static final String TEST_SSID_1 = "TEST1";
+    private static final String TEST_SSID_2 = "TEST2";
+    private static final int[] TEST_FREQUENCIES_1 = {};
+    private static final int[] TEST_FREQUENCIES_2 = {2500, 5124};
+
     private WifiScanner mWifiScanner;
     private TestLooper mLooper;
     private Handler mHandler;
@@ -120,4 +139,68 @@
         return ScanSettings.CREATOR.createFromParcel(parcel);
     }
 
+    /**
+     *  PnoSettings object can be serialized and deserialized, while keeping the
+     *  values unchanged.
+     */
+    @Test
+    public void canSerializeAndDeserializePnoSettings() throws Exception {
+
+        PnoSettings pnoSettings = new PnoSettings();
+
+        PnoNetwork pnoNetwork1 = new PnoNetwork(TEST_SSID_1);
+        PnoNetwork pnoNetwork2 = new PnoNetwork(TEST_SSID_2);
+        pnoNetwork1.frequencies = TEST_FREQUENCIES_1;
+        pnoNetwork2.frequencies = TEST_FREQUENCIES_2;
+
+        pnoSettings.networkList = new PnoNetwork[]{pnoNetwork1, pnoNetwork2};
+        pnoSettings.isConnected = TEST_PNOSETTINGS_IS_CONNECTED;
+        pnoSettings.min5GHzRssi = TEST_PNOSETTINGS_MIN_5GHZ_RSSI;
+        pnoSettings.min24GHzRssi = TEST_PNOSETTINGS_MIN_2GHZ_RSSI;
+        pnoSettings.initialScoreMax = TEST_PNOSETTINGS_INITIAL_SCORE_MAX;
+        pnoSettings.currentConnectionBonus = TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS;
+        pnoSettings.sameNetworkBonus = TEST_PNOSETTINGS_SAME_NETWORK_BONUS;
+        pnoSettings.secureBonus = TEST_PNOSETTINGS_SECURE_BONUS;
+        pnoSettings.band5GHzBonus = TEST_PNOSETTINGS_BAND_5GHZ_BONUS;
+
+        Parcel parcel = Parcel.obtain();
+        pnoSettings.writeToParcel(parcel, 0);
+        // Rewind the pointer to the head of the parcel.
+        parcel.setDataPosition(0);
+        PnoSettings pnoSettingsDeserialized =
+                pnoSettings.CREATOR.createFromParcel(parcel);
+
+        assertNotNull(pnoSettingsDeserialized);
+        assertEquals(TEST_PNOSETTINGS_IS_CONNECTED, pnoSettingsDeserialized.isConnected);
+        assertEquals(TEST_PNOSETTINGS_MIN_5GHZ_RSSI, pnoSettingsDeserialized.min5GHzRssi);
+        assertEquals(TEST_PNOSETTINGS_MIN_2GHZ_RSSI, pnoSettingsDeserialized.min24GHzRssi);
+        assertEquals(TEST_PNOSETTINGS_INITIAL_SCORE_MAX, pnoSettingsDeserialized.initialScoreMax);
+        assertEquals(TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS,
+                pnoSettingsDeserialized.currentConnectionBonus);
+        assertEquals(TEST_PNOSETTINGS_SAME_NETWORK_BONUS,
+                pnoSettingsDeserialized.sameNetworkBonus);
+        assertEquals(TEST_PNOSETTINGS_SECURE_BONUS, pnoSettingsDeserialized.secureBonus);
+        assertEquals(TEST_PNOSETTINGS_BAND_5GHZ_BONUS, pnoSettingsDeserialized.band5GHzBonus);
+
+        // Test parsing of PnoNetwork
+        assertEquals(pnoSettings.networkList.length, pnoSettingsDeserialized.networkList.length);
+        for (int i = 0; i < pnoSettings.networkList.length; i++) {
+            PnoNetwork expected = pnoSettings.networkList[i];
+            PnoNetwork actual = pnoSettingsDeserialized.networkList[i];
+            assertEquals(expected.ssid, actual.ssid);
+            assertEquals(expected.flags, actual.flags);
+            assertEquals(expected.authBitField, actual.authBitField);
+            assertTrue(Arrays.equals(expected.frequencies, actual.frequencies));
+        }
+    }
+
+    /**
+     *  Make sure that frequencies is not null by default.
+     */
+    @Test
+    public void pnoNetworkFrequencyIsNotNull() throws Exception {
+        PnoNetwork pnoNetwork = new PnoNetwork(TEST_SSID_1);
+        assertNotNull(pnoNetwork.frequencies);
+    }
+
 }