Merge "Remove Vibrator Shell Command Time Limit"
diff --git a/Android.bp b/Android.bp
index b715b73..65f232d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -181,6 +181,7 @@
         "core/java/android/hardware/input/IInputManager.aidl",
         "core/java/android/hardware/input/IInputDevicesChangedListener.aidl",
         "core/java/android/hardware/input/ITabletModeChangedListener.aidl",
+        "core/java/android/hardware/iris/IIrisService.aidl",
         "core/java/android/hardware/location/IActivityRecognitionHardware.aidl",
         "core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl",
         "core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl",
@@ -594,6 +595,8 @@
         "telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl",
         "telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl",
         "telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl",
+        "wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl",
+        "wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl",
         "wifi/java/android/net/wifi/ISoftApCallback.aidl",
         "wifi/java/android/net/wifi/ITrafficStateCallback.aidl",
         "wifi/java/android/net/wifi/IWifiManager.aidl",
@@ -692,6 +695,7 @@
     ],
 
     static_libs: [
+        "apex_aidl_interface-java",
         "framework-protos",
         "mediaplayer2-protos",
         "android.hidl.base-V1.0-java",
@@ -1567,6 +1571,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 a2b8eec..dd4c781 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
@@ -28614,7 +28690,7 @@
     enum_constant public static final android.net.wifi.SupplicantState UNINITIALIZED;
   }
 
-  public class WifiConfiguration implements android.os.Parcelable {
+  public deprecated class WifiConfiguration implements android.os.Parcelable {
     ctor public WifiConfiguration();
     method public int describeContents();
     method public android.net.ProxyInfo getHttpProxy();
@@ -28768,7 +28844,8 @@
   }
 
   public class WifiManager {
-    method public int addNetwork(android.net.wifi.WifiConfiguration);
+    method public deprecated int addNetwork(android.net.wifi.WifiConfiguration);
+    method public boolean addNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>, android.app.PendingIntent);
     method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
     method public static int calculateSignalLevel(int, int);
     method public deprecated void cancelWps(android.net.wifi.WifiManager.WpsCallback);
@@ -28776,10 +28853,10 @@
     method public android.net.wifi.WifiManager.MulticastLock createMulticastLock(java.lang.String);
     method public android.net.wifi.WifiManager.WifiLock createWifiLock(int, java.lang.String);
     method public android.net.wifi.WifiManager.WifiLock createWifiLock(java.lang.String);
-    method public boolean disableNetwork(int);
-    method public boolean disconnect();
-    method public boolean enableNetwork(int, boolean);
-    method public java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks();
+    method public deprecated boolean disableNetwork(int);
+    method public deprecated boolean disconnect();
+    method public deprecated boolean enableNetwork(int, boolean);
+    method public deprecated java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks();
     method public android.net.wifi.WifiInfo getConnectionInfo();
     method public android.net.DhcpInfo getDhcpInfo();
     method public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations();
@@ -28794,18 +28871,19 @@
     method public boolean isTdlsSupported();
     method public boolean isWifiEnabled();
     method public deprecated boolean pingSupplicant();
-    method public boolean reassociate();
-    method public boolean reconnect();
-    method public boolean removeNetwork(int);
+    method public deprecated boolean reassociate();
+    method public deprecated boolean reconnect();
+    method public deprecated boolean removeNetwork(int);
+    method public boolean removeNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
     method public void removePasspointConfiguration(java.lang.String);
     method public deprecated boolean saveConfiguration();
     method public void setTdlsEnabled(java.net.InetAddress, boolean);
     method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
-    method public boolean setWifiEnabled(boolean);
+    method public deprecated boolean setWifiEnabled(boolean);
     method public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, android.os.Handler);
     method public deprecated boolean startScan();
     method public deprecated void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
-    method public int updateNetwork(android.net.wifi.WifiConfiguration);
+    method public deprecated int updateNetwork(android.net.wifi.WifiConfiguration);
     field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
     field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
     field public static final deprecated int ERROR_AUTHENTICATING = 1; // 0x1
@@ -28879,6 +28957,29 @@
     method public abstract deprecated void onSucceeded();
   }
 
+  public class WifiNetworkConfigBuilder {
+    ctor public WifiNetworkConfigBuilder();
+    method public android.net.NetworkSpecifier buildNetworkSpecifier();
+    method public android.net.wifi.WifiNetworkSuggestion buildNetworkSuggestion();
+    method public android.net.wifi.WifiNetworkConfigBuilder setBssid(android.net.MacAddress);
+    method public android.net.wifi.WifiNetworkConfigBuilder setBssidPattern(android.net.MacAddress, android.net.MacAddress);
+    method public android.net.wifi.WifiNetworkConfigBuilder setEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
+    method public android.net.wifi.WifiNetworkConfigBuilder setIsAppInteractionRequired();
+    method public android.net.wifi.WifiNetworkConfigBuilder setIsHiddenSsid();
+    method public android.net.wifi.WifiNetworkConfigBuilder setIsMetered();
+    method public android.net.wifi.WifiNetworkConfigBuilder setIsUserInteractionRequired();
+    method public android.net.wifi.WifiNetworkConfigBuilder setPriority(int);
+    method public android.net.wifi.WifiNetworkConfigBuilder setPskPassphrase(java.lang.String);
+    method public android.net.wifi.WifiNetworkConfigBuilder setSsid(java.lang.String);
+    method public android.net.wifi.WifiNetworkConfigBuilder setSsidPattern(android.os.PatternMatcher);
+  }
+
+  public final class WifiNetworkSuggestion 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.net.wifi.WifiNetworkSuggestion> CREATOR;
+  }
+
   public deprecated class WpsInfo implements android.os.Parcelable {
     ctor public deprecated WpsInfo();
     ctor public deprecated WpsInfo(android.net.wifi.WpsInfo);
@@ -34041,6 +34142,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 +37393,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";
@@ -42316,6 +42419,7 @@
     method public java.lang.String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
     method public boolean handleMmi(java.lang.String);
     method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
+    method public boolean isDefaultCallScreeningApp(android.content.ComponentName);
     method public boolean isInCall();
     method public boolean isInManagedCall();
     method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
@@ -42324,12 +42428,14 @@
     method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
     method public void placeCall(android.net.Uri, android.os.Bundle);
     method public void registerPhoneAccount(android.telecom.PhoneAccount);
+    method public void requestChangeDefaultCallScreeningApp(android.content.ComponentName);
     method public void showInCallScreen(boolean);
     method public void silenceRinger();
     method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
     field public static final java.lang.String ACTION_CHANGE_DEFAULT_DIALER = "android.telecom.action.CHANGE_DEFAULT_DIALER";
     field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
     field public static final java.lang.String ACTION_CONFIGURE_PHONE_ACCOUNT = "android.telecom.action.CONFIGURE_PHONE_ACCOUNT";
+    field public static final java.lang.String ACTION_DEFAULT_CALL_SCREENING_APP_CHANGED = "android.telecom.action.DEFAULT_CALL_SCREENING_APP_CHANGED";
     field public static final java.lang.String ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED";
     field public static final deprecated java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
     field public static final java.lang.String ACTION_PHONE_ACCOUNT_REGISTERED = "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
@@ -42346,9 +42452,11 @@
     field public static final java.lang.String EXTRA_CALL_NETWORK_TYPE = "android.telecom.extra.CALL_NETWORK_TYPE";
     field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final java.lang.String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
+    field public static final java.lang.String EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME = "android.telecom.extra.DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME";
     field public static final java.lang.String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS";
     field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
     field public static final java.lang.String EXTRA_INCOMING_VIDEO_STATE = "android.telecom.extra.INCOMING_VIDEO_STATE";
+    field public static final java.lang.String EXTRA_IS_DEFAULT_CALL_SCREENING_APP = "android.telecom.extra.IS_DEFAULT_CALL_SCREENING_APP";
     field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telecom.extra.NOTIFICATION_COUNT";
     field public static final java.lang.String EXTRA_NOTIFICATION_PHONE_NUMBER = "android.telecom.extra.NOTIFICATION_PHONE_NUMBER";
     field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
@@ -42842,7 +42950,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);
@@ -51479,6 +51586,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..d8da475 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 {
@@ -3606,7 +3615,7 @@
     field public byte id;
   }
 
-  public class WifiConfiguration implements android.os.Parcelable {
+  public deprecated class WifiConfiguration implements android.os.Parcelable {
     method public boolean hasNoInternetAccess();
     method public boolean isEphemeral();
     method public boolean isNoInternetAccessExpected();
@@ -3635,8 +3644,10 @@
     method public boolean isPortableHotspotSupported();
     method public boolean isWifiApEnabled();
     method public boolean isWifiScannerSupported();
+    method public void registerNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback, android.os.Handler);
     method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
     method public boolean startScan(android.os.WorkSource);
+    method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback);
     field public static final int CHANGE_REASON_ADDED = 0; // 0x0
     field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2
     field public static final int CHANGE_REASON_REMOVED = 1; // 0x1
@@ -3664,6 +3675,18 @@
     method public abstract void onSuccess();
   }
 
+  public static abstract interface WifiManager.NetworkRequestMatchCallback {
+    method public abstract void onMatch(java.util.List<android.net.wifi.WifiConfiguration>);
+    method public abstract void onUserSelectionCallbackRegistration(android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback);
+    method public abstract void onUserSelectionConnectFailure(android.net.wifi.WifiConfiguration);
+    method public abstract void onUserSelectionConnectSuccess(android.net.wifi.WifiConfiguration);
+  }
+
+  public static abstract interface WifiManager.NetworkRequestUserSelectionCallback {
+    method public abstract void reject();
+    method public abstract void select(android.net.wifi.WifiConfiguration);
+  }
+
   public class WifiNetworkConnectionStatistics implements android.os.Parcelable {
     ctor public WifiNetworkConnectionStatistics(int, int);
     ctor public WifiNetworkConnectionStatistics();
@@ -4861,6 +4884,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 +6951,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/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 12e2560a..065f49e 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -276,22 +276,21 @@
  */
 void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTimeStampNs,
                                      const bool include_current_partial_bucket,
+                                     const bool erase_data,
                                      const DumpReportReason dumpReportReason,
-                                     vector<uint8_t>* outData) {
+                                     ProtoOutputStream* proto) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
 
-    ProtoOutputStream proto;
-
     // Start of ConfigKey.
-    uint64_t configKeyToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
-    proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid());
-    proto.write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)key.GetId());
-    proto.end(configKeyToken);
+    uint64_t configKeyToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
+    proto->write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid());
+    proto->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)key.GetId());
+    proto->end(configKeyToken);
     // End of ConfigKey.
 
     // Then, check stats-data directory to see there's any file containing
     // ConfigMetricsReport from previous shutdowns to concatenate to reports.
-    StorageManager::appendConfigMetricsReport(key, &proto);
+    StorageManager::appendConfigMetricsReport(key, proto);
 
     auto it = mMetricsManagers.find(key);
     if (it != mMetricsManagers.end()) {
@@ -301,14 +300,27 @@
 
         // Start of ConfigMetricsReport (reports).
         uint64_t reportsToken =
-                proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS);
+                proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS);
         onConfigMetricsReportLocked(key, dumpTimeStampNs, include_current_partial_bucket,
-                                    dumpReportReason, &proto);
-        proto.end(reportsToken);
+                                    erase_data, dumpReportReason, proto);
+        proto->end(reportsToken);
         // End of ConfigMetricsReport (reports).
     } else {
         ALOGW("Config source %s does not exist", key.ToString().c_str());
     }
+}
+
+/*
+ * onDumpReport dumps serialized ConfigMetricsReportList into outData.
+ */
+void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTimeStampNs,
+                                     const bool include_current_partial_bucket,
+                                     const bool erase_data,
+                                     const DumpReportReason dumpReportReason,
+                                     vector<uint8_t>* outData) {
+    ProtoOutputStream proto;
+    onDumpReport(key, dumpTimeStampNs, include_current_partial_bucket, erase_data,
+                 dumpReportReason, &proto);
 
     if (outData != nullptr) {
         outData->clear();
@@ -332,6 +344,7 @@
 void StatsLogProcessor::onConfigMetricsReportLocked(const ConfigKey& key,
                                                     const int64_t dumpTimeStampNs,
                                                     const bool include_current_partial_bucket,
+                                                    const bool erase_data,
                                                     const DumpReportReason dumpReportReason,
                                                     ProtoOutputStream* proto) {
     // We already checked whether key exists in mMetricsManagers in
@@ -348,7 +361,7 @@
     // First, fill in ConfigMetricsReport using current data on memory, which
     // starts from filling in StatsLogReport's.
     it->second->onDumpReport(dumpTimeStampNs, include_current_partial_bucket,
-                             &str_set, proto);
+                             erase_data, &str_set, proto);
 
     // Fill in UidMap if there is at least one metric to report.
     // This skips the uid map if it's an empty config.
@@ -479,7 +492,7 @@
     }
     ProtoOutputStream proto;
     onConfigMetricsReportLocked(key, timestampNs, true /* include_current_partial_bucket*/,
-                                dumpReportReason, &proto);
+                                true /* erase_data */, dumpReportReason, &proto);
     string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR,
          (long)getWallClockSec(), key.GetUid(), (long long)key.GetId());
     android::base::unique_fd fd(open(file_name.c_str(),
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 3e8b9b8..ecfd819 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -61,8 +61,11 @@
     size_t GetMetricsSize(const ConfigKey& key) const;
 
     void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs,
-                      const bool include_current_partial_bucket,
+                      const bool include_current_partial_bucket, const bool erase_data,
                       const DumpReportReason dumpReportReason, vector<uint8_t>* outData);
+    void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs,
+                      const bool include_current_partial_bucket, const bool erase_data,
+                      const DumpReportReason dumpReportReason, ProtoOutputStream* proto);
 
     /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */
     void onAnomalyAlarmFired(
@@ -141,6 +144,7 @@
 
     void onConfigMetricsReportLocked(const ConfigKey& key, const int64_t dumpTimeStampNs,
                                      const bool include_current_partial_bucket,
+                                     const bool erase_data,
                                      const DumpReportReason dumpReportReason,
                                      util::ProtoOutputStream* proto);
 
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index ce28777..27685fc 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -46,6 +46,8 @@
 using namespace android;
 
 using android::base::StringPrintf;
+using android::util::FIELD_COUNT_REPEATED;
+using android::util::FIELD_TYPE_MESSAGE;
 
 namespace android {
 namespace os {
@@ -58,6 +60,9 @@
 
 #define STATS_SERVICE_DIR "/data/misc/stats-service"
 
+// for StatsDataDumpProto
+const int FIELD_ID_REPORTS_LIST = 1;
+
 static binder::Status ok() {
     return binder::Status::ok();
 }
@@ -224,31 +229,48 @@
 }
 
 /**
- * Write debugging data about statsd.
+ * Write data from statsd.
+ * Format for statsdStats:  adb shell dumpsys stats --metadata [-v] [--proto]
+ * Format for data report:  adb shell dumpsys stats [anything other than --metadata] [--proto]
+ * Anything ending in --proto will be in proto format.
+ * Anything without --metadata as the first argument will be report information.
+ *     (bugreports call "adb shell dumpsys stats --dump-priority NORMAL -a --proto")
+ * TODO: Come up with a more robust method of enacting <serviceutils/PriorityDumper.h>.
  */
 status_t StatsService::dump(int fd, const Vector<String16>& args) {
     if (!checkCallingPermission(String16(kPermissionDump))) {
         return PERMISSION_DENIED;
     }
-
-    bool verbose = false;
-    bool proto = false;
-    if (args.size() > 0 && !args[0].compare(String16("-v"))) {
-        verbose = true;
+    int lastArg = args.size() - 1;
+    bool asProto = false;
+    if (lastArg >= 0 && !args[lastArg].compare(String16("--proto"))) { // last argument
+        asProto = true;
+        lastArg--;
     }
-    if (args.size() > 0 && !args[args.size()-1].compare(String16("--proto"))) {
-        proto = true;
+    if (args.size() > 0 && !args[0].compare(String16("--metadata"))) { // first argument
+        // Request is to dump statsd stats.
+        bool verbose = false;
+        if (lastArg >= 0 && !args[lastArg].compare(String16("-v"))) {
+            verbose = true;
+            lastArg--;
+        }
+        dumpStatsdStats(fd, verbose, asProto);
+    } else {
+        // Request is to dump statsd report data.
+        if (asProto) {
+            dumpIncidentSection(fd);
+        } else {
+            dprintf(fd, "Non-proto format of stats data dump not available; see proto version.\n");
+        }
     }
 
-    dump_impl(fd, verbose, proto);
-
     return NO_ERROR;
 }
 
 /**
  * Write debugging data about statsd in text or proto format.
  */
-void StatsService::dump_impl(int out, bool verbose, bool proto) {
+void StatsService::dumpStatsdStats(int out, bool verbose, bool proto) {
     if (proto) {
         vector<uint8_t> data;
         StatsdStats::getInstance().dumpStats(&data, false); // does not reset statsdStats.
@@ -262,6 +284,22 @@
 }
 
 /**
+ * Write stats report data in StatsDataDumpProto incident section format.
+ */
+void StatsService::dumpIncidentSection(int out) {
+    ProtoOutputStream proto;
+    for (const ConfigKey& configKey : mConfigManager->GetAllConfigKeys()) {
+        uint64_t reportsListToken =
+                proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS_LIST);
+        mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
+                                 true /* includeCurrentBucket */, false /* erase_data */,
+                                 ADB_DUMP, &proto);
+        proto.end(reportsListToken);
+        proto.flush(out);
+    }
+}
+
+/**
  * Implementation of the adb shell cmd stats command.
  */
 status_t StatsService::command(int in, int out, int err, Vector<String8>& args,
@@ -283,7 +321,7 @@
         }
 
         if (!args[0].compare(String8("dump-report"))) {
-            return cmd_dump_report(out, err, args);
+            return cmd_dump_report(out, args);
         }
 
         if (!args[0].compare(String8("pull-source")) && args.size() > 1) {
@@ -546,7 +584,7 @@
     return UNKNOWN_ERROR;
 }
 
-status_t StatsService::cmd_dump_report(int out, int err, const Vector<String8>& args) {
+status_t StatsService::cmd_dump_report(int out, const Vector<String8>& args) {
     if (mProcessor != nullptr) {
         int argCount = args.size();
         bool good = false;
@@ -589,14 +627,13 @@
         if (good) {
             vector<uint8_t> data;
             mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), getElapsedRealtimeNs(),
-                                     includeCurrentBucket, ADB_DUMP, &data);
+                                     includeCurrentBucket, true /* erase_data */, ADB_DUMP, &data);
             if (proto) {
                 for (size_t i = 0; i < data.size(); i ++) {
                     dprintf(out, "%c", data[i]);
                 }
             } else {
-                dprintf(out, "Dump report for Config [%d,%s]\n", uid, name.c_str());
-                dprintf(out, "See the StatsLogReport in logcat...\n");
+                dprintf(out, "Non-proto stats data dump not currently supported.\n");
             }
             return android::OK;
         } else {
@@ -888,7 +925,7 @@
     VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid());
     ConfigKey configKey(ipc->getCallingUid(), key);
     mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), false /* include_current_bucket*/,
-                             GET_DATA_CALLED, output);
+                             true /* erase_data */, GET_DATA_CALLED, output);
     return Status::ok();
 }
 
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index cbf3429..4a5f05f 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -213,9 +213,14 @@
                                          uint32_t serial);
 
     /**
-     * Text or proto output of dumpsys.
+     * Proto output of statsd report data dumpsys, wrapped in a StatsDataDumpProto.
      */
-    void dump_impl(int outFd, bool verbose, bool proto);
+    void dumpIncidentSection(int outFd);
+
+    /**
+     * Text or proto output of statsdStats dumpsys.
+     */
+    void dumpStatsdStats(int outFd, bool verbose, bool proto);
 
     /**
      * Print usage information for the commands
@@ -240,7 +245,7 @@
     /**
      * Print the event log.
      */
-    status_t cmd_dump_report(int outFd, int err, const Vector<String8>& args);
+    status_t cmd_dump_report(int outFd, const Vector<String8>& args);
 
     /**
      * Print the mapping of uids to package names.
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 6b8c12a..eddc86e 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -82,7 +82,9 @@
 
     // Create the service
     gStatsService = new StatsService(looper);
-    if (defaultServiceManager()->addService(String16("stats"), gStatsService) != 0) {
+    if (defaultServiceManager()->addService(String16("stats"), gStatsService, false,
+                IServiceManager::DUMP_FLAG_PRIORITY_NORMAL | IServiceManager::DUMP_FLAG_PROTO)
+            != 0) {
         ALOGE("Failed to add service as AIDL service");
         return -1;
     }
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index bd94800..5ca8814 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -143,6 +143,7 @@
 
 void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                              const bool include_current_partial_bucket,
+                                             const bool erase_data,
                                              std::set<string> *str_set,
                                              ProtoOutputStream* protoOutput) {
     if (include_current_partial_bucket) {
@@ -230,7 +231,9 @@
 
     protoOutput->end(protoToken);
 
-    mPastBuckets.clear();
+    if (erase_data) {
+        mPastBuckets.clear();
+    }
 }
 
 void CountMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 39d4ae2..1ac44264 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -56,6 +56,7 @@
 
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
+                            const bool erase_data,
                             std::set<string> *str_set,
                             android::util::ProtoOutputStream* protoOutput) override;
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index dd3402d..35deffe 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -453,6 +453,7 @@
 
 void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                                 const bool include_current_partial_bucket,
+                                                const bool erase_data,
                                                 std::set<string> *str_set,
                                                 ProtoOutputStream* protoOutput) {
     if (include_current_partial_bucket) {
@@ -541,7 +542,9 @@
     }
 
     protoOutput->end(protoToken);
-    mPastBuckets.clear();
+    if (erase_data) {
+        mPastBuckets.clear();
+    }
 }
 
 void DurationMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 12addb8..1b830a3 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -63,6 +63,7 @@
 
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
+                            const bool erase_data,
                             std::set<string> *str_set,
                             android::util::ProtoOutputStream* protoOutput) override;
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index afd8ec2..a18e406 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -105,6 +105,7 @@
 
 void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                              const bool include_current_partial_bucket,
+                                             const bool erase_data,
                                              std::set<string> *str_set,
                                              ProtoOutputStream* protoOutput) {
     if (mProto->size() <= 0) {
@@ -120,7 +121,9 @@
     protoOutput->write(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS,
                        reinterpret_cast<char*>(buffer.get()->data()), buffer.get()->size());
 
-    mProto->clear();
+    if (erase_data) {
+        mProto->clear();
+    }
 }
 
 void EventMetricProducer::onConditionChangedLocked(const bool conditionMet,
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 7f7aa37..96adfdd 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -47,6 +47,7 @@
 
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
+                            const bool erase_data,
                             std::set<string> *str_set,
                             android::util::ProtoOutputStream* protoOutput) override;
     void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index f5a16e9..05103a9 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -182,6 +182,7 @@
 
 void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                              const bool include_current_partial_bucket,
+                                             const bool erase_data,
                                              std::set<string> *str_set,
                                              ProtoOutputStream* protoOutput) {
     VLOG("Gauge metric %lld report now...", (long long)mMetricId);
@@ -226,7 +227,6 @@
                            (long long)(NanoToMillis(pair.second)));
         protoOutput->end(wrapperToken);
     }
-    mSkippedBuckets.clear();
 
     for (const auto& pair : mPastBuckets) {
         const MetricDimensionKey& dimensionKey = pair.first;
@@ -304,7 +304,11 @@
     }
     protoOutput->end(protoToken);
 
-    mPastBuckets.clear();
+
+    if (erase_data) {
+        mPastBuckets.clear();
+        mSkippedBuckets.clear();
+    }
 }
 
 void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 99827bb..5866139 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -94,6 +94,7 @@
 private:
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
+                            const bool erase_data,
                             std::set<string> *str_set,
                             android::util::ProtoOutputStream* protoOutput) override;
     void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index b21fd50..127cbbd 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -133,10 +133,12 @@
     // This method clears all the past buckets.
     void onDumpReport(const int64_t dumpTimeNs,
                       const bool include_current_partial_bucket,
+                      const bool erase_data,
                       std::set<string> *str_set,
                       android::util::ProtoOutputStream* protoOutput) {
         std::lock_guard<std::mutex> lock(mMutex);
-        return onDumpReportLocked(dumpTimeNs, include_current_partial_bucket, str_set, protoOutput);
+        return onDumpReportLocked(dumpTimeNs, include_current_partial_bucket, erase_data,
+                str_set, protoOutput);
     }
 
     void clearPastBuckets(const int64_t dumpTimeNs) {
@@ -210,6 +212,7 @@
                                                   const int64_t eventTime) = 0;
     virtual void onDumpReportLocked(const int64_t dumpTimeNs,
                                     const bool include_current_partial_bucket,
+                                    const bool erase_data,
                                     std::set<string> *str_set,
                                     android::util::ProtoOutputStream* protoOutput) = 0;
     virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0;
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index f85ba1f..4244d5b 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -197,6 +197,7 @@
 
 void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs,
                                   const bool include_current_partial_bucket,
+                                  const bool erase_data,
                                   std::set<string> *str_set,
                                   ProtoOutputStream* protoOutput) {
     VLOG("=========================Metric Reports Start==========================");
@@ -206,11 +207,11 @@
             uint64_t token = protoOutput->start(
                     FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS);
             if (mHashStringsInReport) {
-                producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, str_set,
-                                       protoOutput);
+                producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data,
+                                       str_set, protoOutput);
             } else {
-                producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, nullptr,
-                                       protoOutput);
+                producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, erase_data,
+                                       nullptr, protoOutput);
             }
             protoOutput->end(token);
         } else {
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 649222ff..a4672b6 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -107,6 +107,7 @@
 
     virtual void onDumpReport(const int64_t dumpTimeNs,
                               const bool include_current_partial_bucket,
+                              const bool erase_data,
                               std::set<string> *str_set,
                               android::util::ProtoOutputStream* protoOutput);
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 192a54b..6367479 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -170,6 +170,7 @@
 
 void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
                                              const bool include_current_partial_bucket,
+                                             const bool erase_data,
                                              std::set<string> *str_set,
                                              ProtoOutputStream* protoOutput) {
     VLOG("metric %lld dump report now...", (long long)mMetricId);
@@ -211,7 +212,6 @@
                            (long long)(NanoToMillis(pair.second)));
         protoOutput->end(wrapperToken);
     }
-    mSkippedBuckets.clear();
 
     for (const auto& pair : mPastBuckets) {
         const MetricDimensionKey& dimensionKey = pair.first;
@@ -271,7 +271,10 @@
     protoOutput->end(protoToken);
 
     VLOG("metric %lld dump report now...", (long long)mMetricId);
-    mPastBuckets.clear();
+    if (erase_data) {
+        mPastBuckets.clear();
+        mSkippedBuckets.clear();
+    }
 }
 
 void ValueMetricProducer::onConditionChangedLocked(const bool condition,
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index b2f0b6f..8db2d95 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -94,6 +94,7 @@
 private:
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
+                            const bool erase_data,
                             std::set<string> *str_set,
                             android::util::ProtoOutputStream* protoOutput) override;
     void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index b6f635c..8864252 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -168,7 +168,7 @@
 
     // Expect to get no metrics, but snapshot specified above in uidmap.
     vector<uint8_t> bytes;
-    p.onDumpReport(key, 1, false, ADB_DUMP, &bytes);
+    p.onDumpReport(key, 1, false, true, ADB_DUMP, &bytes);
 
     ConfigMetricsReportList output;
     output.ParseFromArray(bytes.data(), bytes.size());
@@ -197,7 +197,7 @@
 
     // Expect to get no metrics, but snapshot specified above in uidmap.
     vector<uint8_t> bytes;
-    p.onDumpReport(key, 1, false, ADB_DUMP, &bytes);
+    p.onDumpReport(key, 1, false, true, ADB_DUMP, &bytes);
 
     ConfigMetricsReportList output;
     output.ParseFromArray(bytes.data(), bytes.size());
@@ -227,7 +227,7 @@
 
     // Expect to get no metrics, but snapshot specified above in uidmap.
     vector<uint8_t> bytes;
-    p.onDumpReport(key, 1, false, ADB_DUMP, &bytes);
+    p.onDumpReport(key, 1, false, true, ADB_DUMP, &bytes);
 
     ConfigMetricsReportList output;
     output.ParseFromArray(bytes.data(), bytes.size());
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index a8fcc81..5c47af7 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -144,8 +144,8 @@
     }
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -290,8 +290,8 @@
     }
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
index 75bd40f..a8914da 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
@@ -212,7 +212,7 @@
                 ConfigMetricsReportList reports;
                 vector<uint8_t> buffer;
                 processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
-                                        ADB_DUMP, &buffer);
+                                        true, ADB_DUMP, &buffer);
                 EXPECT_TRUE(buffer.size() > 0);
                 EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
                 backfillDimensionPath(&reports);
@@ -548,7 +548,7 @@
             ConfigMetricsReportList reports;
             vector<uint8_t> buffer;
             processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
-                                    ADB_DUMP, &buffer);
+                                    true, ADB_DUMP, &buffer);
             EXPECT_TRUE(buffer.size() > 0);
             EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
             backfillDimensionPath(&reports);
@@ -798,7 +798,7 @@
             ConfigMetricsReportList reports;
             vector<uint8_t> buffer;
             processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
-                                    ADB_DUMP, &buffer);
+                                    true, ADB_DUMP, &buffer);
             EXPECT_TRUE(buffer.size() > 0);
             EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
             backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
index c5a8a2e..621b6ed 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
@@ -130,8 +130,8 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -346,8 +346,8 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -530,8 +530,8 @@
 
         ConfigMetricsReportList reports;
         vector<uint8_t> buffer;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
-                                &buffer);
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
+                                ADB_DUMP, &buffer);
         EXPECT_TRUE(buffer.size() > 0);
         EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
         backfillDimensionPath(&reports);
@@ -732,8 +732,8 @@
 
         ConfigMetricsReportList reports;
         vector<uint8_t> buffer;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
-                                &buffer);
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
+                                ADB_DUMP, &buffer);
         EXPECT_TRUE(buffer.size() > 0);
         EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
         backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
index 5bcc9ee..9f8acaf 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
@@ -143,7 +143,7 @@
             ConfigMetricsReportList reports;
             vector<uint8_t> buffer;
             processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
-                                    ADB_DUMP, &buffer);
+                                    true, ADB_DUMP, &buffer);
             EXPECT_TRUE(buffer.size() > 0);
             EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
             backfillDimensionPath(&reports);
@@ -438,7 +438,7 @@
             ConfigMetricsReportList reports;
             vector<uint8_t> buffer;
             processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
-                                    ADB_DUMP, &buffer);
+                                    true, ADB_DUMP, &buffer);
             EXPECT_TRUE(buffer.size() > 0);
             EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
             backfillDimensionPath(&reports);
@@ -658,8 +658,8 @@
 
         ConfigMetricsReportList reports;
         vector<uint8_t> buffer;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
-                                &buffer);
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
+                                ADB_DUMP, &buffer);
         EXPECT_TRUE(buffer.size() > 0);
         EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
         backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index d7b9c11..2d090e0 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -123,8 +123,8 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -246,8 +246,8 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -350,8 +350,8 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
index 5c1ef01..71afedf 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
@@ -149,8 +149,8 @@
         }
         ConfigMetricsReportList reports;
         vector<uint8_t> buffer;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, ADB_DUMP,
-                                &buffer);
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, true,
+                                ADB_DUMP, &buffer);
         EXPECT_TRUE(buffer.size() > 0);
         EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
         backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
index 0f13a4a..29e86f3 100644
--- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
@@ -167,8 +167,8 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index cc8894b..9349c85 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -199,8 +199,8 @@
     }
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -318,8 +318,8 @@
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
 
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 67acd61..3cb553f 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -46,7 +46,7 @@
     IPCThreadState* ipc = IPCThreadState::self();
     ConfigKey configKey(ipc->getCallingUid(), kConfigKey);
     processor->onDumpReport(configKey, timestamp, include_current /* include_current_bucket*/,
-                            ADB_DUMP, &output);
+                            true /* erase_data */, ADB_DUMP, &output);
     ConfigMetricsReportList reports;
     reports.ParseFromArray(output.data(), output.size());
     EXPECT_EQ(1, reports.reports_size());
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index f2e8f58..fed5a3f 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -117,8 +117,8 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -224,8 +224,8 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index b9d0c62..6d1317c 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -127,8 +127,8 @@
     FeedEvents(config, processor);
     vector<uint8_t> buffer;
     ConfigMetricsReportList reports;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -164,8 +164,8 @@
     FeedEvents(config, processor);
     vector<uint8_t> buffer;
     ConfigMetricsReportList reports;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -215,8 +215,8 @@
         processor->OnLogEvent(event.get());
     }
 
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -248,8 +248,8 @@
     FeedEvents(config, processor);
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
 
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
@@ -277,8 +277,8 @@
     FeedEvents(config, processor);
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
@@ -323,8 +323,8 @@
         processor->OnLogEvent(event.get());
     }
 
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, ADB_DUMP,
-                            &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true,
+                            ADB_DUMP, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     backfillDimensionPath(&reports);
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/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 508ea3b..f267169 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -66,6 +66,8 @@
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.IHdmiControlService;
 import android.hardware.input.InputManager;
+import android.hardware.iris.IrisManager;
+import android.hardware.iris.IIrisService;
 import android.hardware.location.ContextHubManager;
 import android.hardware.radio.RadioManager;
 import android.hardware.usb.IUsbManager;
@@ -836,6 +838,18 @@
                     }
                 });
 
+        registerService(Context.IRIS_SERVICE, IrisManager.class,
+                new CachedServiceFetcher<IrisManager>() {
+                    @Override
+                    public IrisManager createService(ContextImpl ctx)
+                        throws ServiceNotFoundException {
+                        final IBinder binder =
+                                ServiceManager.getServiceOrThrow(Context.IRIS_SERVICE);
+                        IIrisService service = IIrisService.Stub.asInterface(binder);
+                        return new IrisManager(ctx.getOuterContext(), service);
+                    }
+                });
+
         registerService(Context.BIOMETRIC_SERVICE, BiometricManager.class,
                 new CachedServiceFetcher<BiometricManager>() {
                     @Override
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/Context.java b/core/java/android/content/Context.java
index e378800..6a7829b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3706,6 +3706,17 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.hardware.iris.IrisManager} for handling management
+     * of iris authentication.
+     *
+     * @hide
+     * @see #getSystemService
+     * @see android.hardware.iris.IrisManager
+     */
+    public static final String IRIS_SERVICE = "iris";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.hardware.biometrics.BiometricManager} for handling management
      * of face authentication.
      *
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1f700f7..67b86c0 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -855,6 +855,14 @@
      */
     public static final int INSTALL_VIRTUAL_PRELOAD = 0x00010000;
 
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that this package
+     * is an APEX package
+     *
+     * @hide
+     */
+    public static final int INSTALL_APEX = 0x00020000;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = {
             DONT_KILL_APP
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index a8bbeab..096301c 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -25,6 +25,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -78,6 +79,7 @@
     private final @Type int mType;
     private final VersionedPackage mDeclaringPackage;
     private final List<VersionedPackage> mDependentPackages;
+    private List<SharedLibraryInfo> mDependencies;
 
     /**
      * Creates a new instance.
@@ -91,7 +93,8 @@
      * @hide
      */
     public SharedLibraryInfo(String path, String packageName, String name, long version, int type,
-            VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages) {
+            VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
+            List<SharedLibraryInfo> dependencies) {
         mPath = path;
         mPackageName = packageName;
         mName = name;
@@ -99,11 +102,13 @@
         mType = type;
         mDeclaringPackage = declaringPackage;
         mDependentPackages = dependentPackages;
+        mDependencies = dependencies;
     }
 
     private SharedLibraryInfo(Parcel parcel) {
         this(parcel.readString(), parcel.readString(), parcel.readString(), parcel.readLong(),
-                parcel.readInt(), parcel.readParcelable(null), parcel.readArrayList(null));
+                parcel.readInt(), parcel.readParcelable(null), parcel.readArrayList(null),
+                parcel.createTypedArrayList(SharedLibraryInfo.CREATOR));
     }
 
     /**
@@ -150,6 +155,47 @@
     }
 
     /**
+     * Add a library dependency to that library. Note that this
+     * should be called under the package manager lock.
+     *
+     * @hide
+     */
+    public void addDependency(@Nullable SharedLibraryInfo info) {
+        if (info == null) {
+            // For convenience of the caller, allow null to be passed.
+            // This can happen when we create the dependencies of builtin
+            // libraries.
+            return;
+        }
+        if (mDependencies == null) {
+            mDependencies = new ArrayList<>();
+        }
+        mDependencies.add(info);
+    }
+
+    /**
+     * Clear all dependencies.
+     *
+     * @hide
+     */
+    public void clearDependencies() {
+        mDependencies = null;
+    }
+
+    /**
+     * Gets the libraries this library directly depends on. Note that
+     * the package manager prevents recursive dependencies when installing
+     * a package.
+     *
+     * @return The dependencies.
+     *
+     * @hide
+     */
+    public @Nullable List<SharedLibraryInfo> getDependencies() {
+        return mDependencies;
+    }
+
+    /**
      * @deprecated Use {@link #getLongVersion()} instead.
      */
     @Deprecated
@@ -232,6 +278,7 @@
         parcel.writeInt(mType);
         parcel.writeParcelable(mDeclaringPackage, flags);
         parcel.writeList(mDependentPackages);
+        parcel.writeTypedList(mDependencies);
     }
 
     private static String typeToString(int type) {
diff --git a/core/java/android/content/pm/SharedLibraryNames.java b/core/java/android/content/pm/SharedLibraryNames.java
index 387d29e8..5afc8a9 100644
--- a/core/java/android/content/pm/SharedLibraryNames.java
+++ b/core/java/android/content/pm/SharedLibraryNames.java
@@ -22,15 +22,15 @@
  */
 public class SharedLibraryNames {
 
-    static final String ANDROID_HIDL_BASE = "android.hidl.base-V1.0-java";
+    public static final String ANDROID_HIDL_BASE = "android.hidl.base-V1.0-java";
 
-    static final String ANDROID_HIDL_MANAGER = "android.hidl.manager-V1.0-java";
+    public static final String ANDROID_HIDL_MANAGER = "android.hidl.manager-V1.0-java";
 
-    static final String ANDROID_TEST_BASE = "android.test.base";
+    public static final String ANDROID_TEST_BASE = "android.test.base";
 
-    static final String ANDROID_TEST_MOCK = "android.test.mock";
+    public static final String ANDROID_TEST_MOCK = "android.test.mock";
 
-    static final String ANDROID_TEST_RUNNER = "android.test.runner";
+    public static final String ANDROID_TEST_RUNNER = "android.test.runner";
 
-    static final String ORG_APACHE_HTTP_LEGACY = "org.apache.http.legacy";
+    public static final String ORG_APACHE_HTTP_LEGACY = "org.apache.http.legacy";
 }
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/iris/IIrisService.aidl b/core/java/android/hardware/iris/IIrisService.aidl
new file mode 100644
index 0000000..8cf3c13
--- /dev/null
+++ b/core/java/android/hardware/iris/IIrisService.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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.hardware.iris;
+
+/**
+ * Communication channel from client to the iris service. These methods are all require the
+ * MANAGE_BIOMETRIC signature permission.
+ * @hide
+ */
+interface IIrisService {
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/iris/IrisManager.java b/core/java/android/hardware/iris/IrisManager.java
new file mode 100644
index 0000000..281ac47
--- /dev/null
+++ b/core/java/android/hardware/iris/IrisManager.java
@@ -0,0 +1,34 @@
+/*
+ * 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.hardware.iris;
+
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * A class that coordinates access to the iris authentication hardware.
+ * @hide
+ */
+@SystemService(Context.IRIS_SERVICE)
+public class IrisManager {
+
+    /**
+     * @hide
+     */
+    public IrisManager(Context context, IIrisService service) {
+    }
+}
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/net/INetdEventCallback.aidl b/core/java/android/net/INetdEventCallback.aidl
index 1e75bf4..4b1a08d 100644
--- a/core/java/android/net/INetdEventCallback.aidl
+++ b/core/java/android/net/INetdEventCallback.aidl
@@ -28,6 +28,11 @@
      * Reports a single DNS lookup function call.
      * This method must not block or perform long-running operations.
      *
+     * @param netId the ID of the network the lookup was performed on.
+     * @param eventType one of the EVENT_* constants in {@link INetdEventListener}.
+     * @param returnCode the return value of the query, may vary based on {@code eventType}. See
+     *        {@code getaddrinfo()}, {@code gethostbyaddr()} and {@code gethostbyname()} section in
+     *        bionic/libc/include/netdb.h.
      * @param hostname the name that was looked up.
      * @param ipAddresses (possibly a subset of) the IP addresses returned.
      *        At most {@link #DNS_REPORTED_IP_ADDRESSES_LIMIT} addresses are logged.
@@ -36,8 +41,8 @@
      * @param timestamp the timestamp at which the query was reported by netd.
      * @param uid the UID of the application that performed the query.
      */
-    void onDnsEvent(String hostname, in String[] ipAddresses, int ipAddressesCount, long timestamp,
-            int uid);
+    void onDnsEvent(int netId, int eventType, int returnCode, String hostname,
+            in String[] ipAddresses, int ipAddressesCount, long timestamp, int uid);
 
     /**
      * Represents a private DNS validation success or failure.
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 98f3567..4cd0001 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -393,4 +393,19 @@
         }
         return out;
     }
+
+    /**
+     * Checks if this MAC Address matches the provided range.
+     *
+     * @param baseAddress MacAddress representing the base address to compare with.
+     * @param mask MacAddress representing the mask to use during comparison.
+     * @return true if this MAC Address matches the given range.
+     *
+     * @hide
+     */
+    public boolean matches(@NonNull MacAddress baseAddress, @NonNull MacAddress mask) {
+        Preconditions.checkNotNull(baseAddress);
+        Preconditions.checkNotNull(mask);
+        return (mAddr & mask.mAddr) == (baseAddress.mAddr & mask.mAddr);
+    }
 }
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/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 2d67d79..769dd1b 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -36,6 +36,7 @@
     public static final String PERSIST_PREFIX = "persist." + FFLAG_OVERRIDE_PREFIX;
     public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
     public static final String EMERGENCY_DIAL_SHORTCUTS = "settings_emergency_dial_shortcuts";
+    public static final String SAFETY_HUB = "settings_safety_hub";
     public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
 
     private static final Map<String, String> DEFAULT_FLAGS;
@@ -51,6 +52,7 @@
         DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
         DEFAULT_FLAGS.put(EMERGENCY_DIAL_SHORTCUTS, "true");
         DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false");
+        DEFAULT_FLAGS.put(SAFETY_HUB, "false");
         DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
     }
 
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/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index b799728..25a5a07 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -69,7 +69,7 @@
     private static final int ASPECT_LOCK_HEIGHT = 2; // Fixed height; width will be minimum of (w,h)
 
     private static final boolean PROFILE_DRAWING = false;
-    private static final float LINE_FADE_ALPHA_MULTIPLIER = 3.5f;
+    private static final float LINE_FADE_ALPHA_MULTIPLIER = 1.5f;
     private final CellState[][] mCellStates;
 
     private final int mDotSize;
diff --git a/core/java/com/android/server/net/BaseNetdEventCallback.java b/core/java/com/android/server/net/BaseNetdEventCallback.java
index fdba2f3..97247aa 100644
--- a/core/java/com/android/server/net/BaseNetdEventCallback.java
+++ b/core/java/com/android/server/net/BaseNetdEventCallback.java
@@ -26,8 +26,8 @@
  */
 public class BaseNetdEventCallback extends INetdEventCallback.Stub {
     @Override
-    public void onDnsEvent(String hostname, String[] ipAddresses,
-            int ipAddressesCount, long timestamp, int uid) {
+    public void onDnsEvent(int netId, int eventType, int returnCode, String hostname,
+            String[] ipAddresses, int ipAddressesCount, long timestamp, int uid) {
         // default no-op
     }
 
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/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 8f28970..0b9e347 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -26,6 +26,7 @@
 import "frameworks/base/core/proto/android/os/pagetypeinfo.proto";
 import "frameworks/base/core/proto/android/os/procrank.proto";
 import "frameworks/base/core/proto/android/os/ps.proto";
+import "frameworks/base/core/proto/android/os/statsdata.proto";
 import "frameworks/base/core/proto/android/os/system_properties.proto";
 import "frameworks/base/core/proto/android/providers/settings.proto";
 import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
@@ -301,6 +302,12 @@
         (section).userdebug_and_eng_only = true
     ];
 
+    optional android.os.StatsDataDumpProto stats_data = 3023 [
+        (section).type = SECTION_DUMPSYS,
+        (section).args = "stats --proto",
+        (section).userdebug_and_eng_only = true
+    ];
+
     // Reserved for OEMs.
     extensions 50000 to 100000;
 }
diff --git a/core/proto/android/os/statsdata.proto b/core/proto/android/os/statsdata.proto
new file mode 100644
index 0000000..25d76b8
--- /dev/null
+++ b/core/proto/android/os/statsdata.proto
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+option java_multiple_files = true;
+
+package android.os;
+
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+// Dump of statsd report data (dumpsys stats --proto).
+message StatsDataDumpProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    // The following is an android.os.statsd.ConfigMetricsReportList, which is defined
+    // in frameworks/base/cmds/statsd/src/stats_log.proto. It should not be imported (even weakly)
+    // in AOSP because it would drag with it atoms.proto, which is enormous and awkward.
+    repeated bytes config_metrics_report_list = 1;
+
+}
\ No newline at end of file
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/README.md b/packages/SystemUI/README.md
index 33c5551..a8ce196 100644
--- a/packages/SystemUI/README.md
+++ b/packages/SystemUI/README.md
@@ -164,7 +164,7 @@
 
 ### [com.android.systemui.biometrics.BiometricDialogImpl](/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java)
 
-Fingerprint UI.
+Biometric UI.
 
 ---
 
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/art-profile b/services/art-profile
index 742ca1c..bdd49de 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -2271,6 +2271,7 @@
 HPLcom/android/server/wm/DisplayContent;->prepareSurfaces()V
 HPLcom/android/server/wm/DisplayContent;->resetAnimationBackgroundAnimator()V
 HPLcom/android/server/wm/DisplayContent;->skipTraverseChild(Lcom/android/server/wm/WindowContainer;)Z
+HPLcom/android/server/wm/DisplayContent;->updateOrientationFromAppTokens(Z)Z
 HPLcom/android/server/wm/DisplayContent;->updateTouchExcludeRegion()V
 HPLcom/android/server/wm/DockedStackDividerController;->isResizing()Z
 HPLcom/android/server/wm/DragDropController;->dragDropActiveLocked()Z
@@ -2451,7 +2452,6 @@
 HPLcom/android/server/wm/WindowManagerService;->resetPriorityAfterLockedSection()V
 HPLcom/android/server/wm/WindowManagerService;->scheduleAnimationLocked()V
 HPLcom/android/server/wm/WindowManagerService;->traceStateLocked(Ljava/lang/String;)V
-HPLcom/android/server/wm/WindowManagerService;->updateOrientationFromAppTokensLocked(IZ)Z
 HPLcom/android/server/wm/WindowManagerService;->windowForClientLocked(Lcom/android/server/wm/Session;Landroid/os/IBinder;Z)Lcom/android/server/wm/WindowState;
 HPLcom/android/server/wm/WindowManagerThreadPriorityBooster;->boost()V
 HPLcom/android/server/wm/WindowManagerThreadPriorityBooster;->reset()V
@@ -18137,6 +18137,7 @@
 PLcom/android/server/wm/DisplayContent;->updateBounds()V
 PLcom/android/server/wm/DisplayContent;->updateDisplayAndOrientation(I)Landroid/view/DisplayInfo;
 PLcom/android/server/wm/DisplayContent;->updateDisplayInfo()V
+PLcom/android/server/wm/DisplayContent;->updateOrientationFromAppTokens()Z
 PLcom/android/server/wm/DisplayContent;->updateRotationUnchecked()Z
 PLcom/android/server/wm/DisplayContent;->updateRotationUnchecked(Z)Z
 PLcom/android/server/wm/DisplayContent;->updateStackBoundsAfterConfigChange(Ljava/util/List;)V
@@ -18906,7 +18907,6 @@
 PLcom/android/server/wm/WindowManagerService;->updateNonSystemOverlayWindowsVisibilityIfNeeded(Lcom/android/server/wm/WindowState;Z)V
 PLcom/android/server/wm/WindowManagerService;->updateOrientationFromAppTokens(Landroid/content/res/Configuration;Landroid/os/IBinder;I)Landroid/content/res/Configuration;
 PLcom/android/server/wm/WindowManagerService;->updateOrientationFromAppTokens(Landroid/content/res/Configuration;Landroid/os/IBinder;IZ)Landroid/content/res/Configuration;
-PLcom/android/server/wm/WindowManagerService;->updateOrientationFromAppTokensLocked(I)Z
 PLcom/android/server/wm/WindowManagerService;->updateOrientationFromAppTokensLocked(Landroid/content/res/Configuration;Landroid/os/IBinder;IZ)Landroid/content/res/Configuration;
 PLcom/android/server/wm/WindowManagerService;->updatePointerIcon(Landroid/view/IWindow;)V
 PLcom/android/server/wm/WindowManagerService;->updateRotation(ZZ)V
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/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 5d4263b..bc3cc3b 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -70,7 +70,7 @@
 /**
  * A service to manage multiple clients that want to access the face HAL API.
  * The service is responsible for maintaining a list of clients and dispatching all
- * face -related events.
+ * face-related events.
  *
  * @hide
  */
diff --git a/services/core/java/com/android/server/biometrics/iris/IrisService.java b/services/core/java/com/android/server/biometrics/iris/IrisService.java
new file mode 100644
index 0000000..37cdc2a
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/iris/IrisService.java
@@ -0,0 +1,131 @@
+/*
+ * 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.biometrics.iris;
+
+import android.content.Context;
+
+import com.android.server.biometrics.BiometricServiceBase;
+import com.android.server.biometrics.BiometricUtils;
+import com.android.server.biometrics.Metrics;
+
+/**
+ * A service to manage multiple clients that want to access the Iris HAL API.
+ * The service is responsible for maintaining a list of clients and dispatching all
+ * iris-related events.
+ *
+ * TODO: The vendor is expected to fill in the service. See
+ * {@link com.android.server.biometrics.fingerprint.FingerprintService}
+ *
+ * @hide
+ */
+public class IrisService extends BiometricServiceBase {
+
+    private static final String TAG = "IrisService";
+
+    /**
+     * Initializes the system service.
+     * <p>
+     * Subclasses must define a single argument constructor that accepts the context
+     * and passes it to super.
+     * </p>
+     *
+     * @param context The system server context.
+     */
+    public IrisService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+    }
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+
+    @Override
+    protected BiometricUtils getBiometricUtils() {
+        return null;
+    }
+
+    @Override
+    protected int getFailedAttemptsLockoutTimed() {
+        return 0;
+    }
+
+    @Override
+    protected int getFailedAttemptsLockoutPermanent() {
+        return 0;
+    }
+
+    @Override
+    protected Metrics getMetrics() {
+        return null;
+    }
+
+    @Override
+    protected boolean hasReachedEnrollmentLimit(int userId) {
+        return false;
+    }
+
+    @Override
+    protected void updateActiveGroup(int userId, String clientPackage) {
+
+    }
+
+    @Override
+    protected String getLockoutResetIntent() {
+        return null;
+    }
+
+    @Override
+    protected String getLockoutBroadcastPermission() {
+        return null;
+    }
+
+    @Override
+    protected long getHalDeviceId() {
+        return 0;
+    }
+
+    @Override
+    protected void handleUserSwitching(int userId) {
+
+    }
+
+    @Override
+    protected boolean hasEnrolledBiometrics(int userId) {
+        return false;
+    }
+
+    @Override
+    protected String getManageBiometricPermission() {
+        return null;
+    }
+
+    @Override
+    protected void checkUseBiometricPermission() {
+
+    }
+
+    @Override
+    protected boolean checkAppOps(int uid, String opPackageName) {
+        return false;
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 4f31e53..422f556 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -208,7 +208,8 @@
 
         for (INetdEventCallback callback : mNetdEventCallbackList) {
             if (callback != null) {
-                callback.onDnsEvent(hostname, ipAddresses, ipAddressesCount, timestamp, uid);
+                callback.onDnsEvent(netId, eventType, returnCode, hostname, ipAddresses,
+                        ipAddressesCount, timestamp, uid);
             }
         }
     }
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/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
index 29b1339..fa90e90 100644
--- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
@@ -142,8 +142,8 @@
 
     private final INetdEventCallback mNetdEventCallback = new BaseNetdEventCallback() {
         @Override
-        public void onDnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount,
-                long timestamp, int uid) {
+        public void onDnsEvent(int netId, int eventType, int returnCode, String hostname,
+                String[] ipAddresses, int ipAddressesCount, long timestamp, int uid) {
             if (!mIsLoggingEnabled) {
                 return;
             }
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/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 0b32d1a..6ccd040 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -58,7 +58,6 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SELinux;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
@@ -646,8 +645,8 @@
         }
 
         try {
-            Os.mkdir(stageDir.getAbsolutePath(), 0755);
-            Os.chmod(stageDir.getAbsolutePath(), 0755);
+            Os.mkdir(stageDir.getAbsolutePath(), 0775);
+            Os.chmod(stageDir.getAbsolutePath(), 0775);
         } catch (ErrnoException e) {
             // This purposefully throws if directory already exists
             throw new IOException("Failed to prepare session dir: " + stageDir, e);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 51225a7..6e45013 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -43,6 +43,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.apex.IApexService;
 import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.Context;
@@ -75,6 +76,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.RevocableFileDescriptor;
+import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
@@ -838,12 +840,15 @@
         resolveStageDirLocked();
 
         mSealed = true;
-
-        // Verify that stage looks sane with respect to existing application.
-        // This currently only ensures packageName, versionCode, and certificate
-        // consistency.
         try {
-            validateInstallLocked(pkgInfo);
+            if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+                validateApexInstallLocked(pkgInfo);
+            } else {
+                // Verify that stage looks sane with respect to existing application.
+                // This currently only ensures packageName, versionCode, and certificate
+                // consistency.
+                validateApkInstallLocked(pkgInfo);
+            }
         } catch (PackageManagerException e) {
             throw e;
         } catch (Throwable e) {
@@ -926,6 +931,31 @@
         Preconditions.checkNotNull(mSigningDetails);
         Preconditions.checkNotNull(mResolvedBaseFile);
 
+        if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+            commitApexLocked();
+        } else {
+            commitApkLocked();
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void commitApexLocked() throws PackageManagerException {
+        try {
+            IApexService apex = IApexService.Stub.asInterface(
+                    ServiceManager.getService("apexservice"));
+            apex.installPackage(mResolvedBaseFile.toString());
+        } catch (Throwable e) {
+            // Convert all exceptions into package manager exceptions as only those are handled
+            // in the code above
+            throw new PackageManagerException(e);
+        } finally {
+            destroyInternal();
+            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "APEX installed", null);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void commitApkLocked() throws PackageManagerException {
         if (needToAskForPermissionsLocked()) {
             // User needs to confirm installation; give installer an intent they can use to involve
             // user.
@@ -1047,6 +1077,57 @@
                 (params.installFlags & PackageManager.DONT_KILL_APP) != 0;
     }
 
+    @GuardedBy("mLock")
+    private void validateApexInstallLocked(@Nullable PackageInfo pkgInfo)
+            throws PackageManagerException {
+        mResolvedStagedFiles.clear();
+        mResolvedInheritedFiles.clear();
+
+        try {
+            resolveStageDirLocked();
+        } catch (IOException e) {
+            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
+                "Failed to resolve stage location", e);
+        }
+
+        final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
+        if (ArrayUtils.isEmpty(addedFiles)) {
+            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
+        }
+
+        if (addedFiles.length > 1) {
+            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                "Only one APEX file at a time might be installed");
+        }
+        File addedFile = addedFiles[0];
+        final ApkLite apk;
+        try {
+            apk = PackageParser.parseApkLite(
+                addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
+        } catch (PackageParserException e) {
+            throw PackageManagerException.from(e);
+        }
+
+        mPackageName = apk.packageName;
+        mVersionCode = apk.getLongVersionCode();
+        mSigningDetails = apk.signingDetails;
+        mResolvedBaseFile = addedFile;
+
+        assertApkConsistentLocked(String.valueOf(addedFile), apk);
+
+        if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
+            try {
+                // STOPSHIP: For APEX we should also implement proper APK Signature verification.
+                mSigningDetails = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
+                    pkgInfo.applicationInfo.sourceDir,
+                    PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
+            } catch (PackageParserException e) {
+                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                    "Couldn't obtain signatures from base APK");
+            }
+        }
+    }
+
     /**
      * Validate install by confirming that all application packages are have
      * consistent package name, version code, and signing certificates.
@@ -1060,7 +1141,7 @@
      * {@link PackageManagerService}.
      */
     @GuardedBy("mLock")
-    private void validateInstallLocked(@Nullable PackageInfo pkgInfo)
+    private void validateApkInstallLocked(@Nullable PackageInfo pkgInfo)
             throws PackageManagerException {
         ApkLite baseApk = null;
         mPackageName = null;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9210d46..0e37bca 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -85,6 +85,11 @@
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.pm.PackageParser.isApkFile;
+import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE;
+import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
 import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
@@ -366,6 +371,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiConsumer;
 import java.util.function.Predicate;
 
 /**
@@ -2090,6 +2096,28 @@
         }
     }
 
+    @GuardedBy("mPackages")
+    private void setupBuiltinSharedLibraryDependenciesLocked() {
+        // Builtin libraries don't have versions.
+        long version = SharedLibraryInfo.VERSION_UNDEFINED;
+
+        SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(ANDROID_HIDL_MANAGER, version);
+        if (libraryInfo != null) {
+            libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_HIDL_BASE, version));
+        }
+
+        libraryInfo = getSharedLibraryInfoLPr(ANDROID_TEST_RUNNER, version);
+        if (libraryInfo != null) {
+            libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_TEST_MOCK, version));
+            libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_TEST_BASE, version));
+        }
+
+        libraryInfo = getSharedLibraryInfoLPr(ANDROID_TEST_MOCK, version);
+        if (libraryInfo != null) {
+            libraryInfo.addDependency(getSharedLibraryInfoLPr(ANDROID_TEST_BASE, version));
+        }
+    }
+
     public PackageManagerService(Context context, Installer installer,
             boolean factoryTest, boolean onlyCore) {
         LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
@@ -2205,6 +2233,9 @@
                 addSharedLibraryLPw(path, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
                         SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);
             }
+            // Builtin libraries cannot encode their dependency where they are
+            // defined, so fix that now.
+            setupBuiltinSharedLibraryDependenciesLocked();
 
             SELinuxMMAC.readInstallPolicy();
 
@@ -4867,7 +4898,10 @@
                     SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getPath(),
                             libInfo.getPackageName(), libInfo.getName(), libInfo.getLongVersion(),
                             libInfo.getType(), libInfo.getDeclaringPackage(),
-                            getPackagesUsingSharedLibraryLPr(libInfo, flags, userId));
+                            getPackagesUsingSharedLibraryLPr(libInfo, flags, userId),
+                            (libInfo.getDependencies() == null
+                                    ? null
+                                    : new ArrayList(libInfo.getDependencies())));
 
                     if (result == null) {
                         result = new ArrayList<>();
@@ -9598,16 +9632,34 @@
     }
 
     @GuardedBy("mPackages")
-    private void addSharedLibraryLPr(Set<String> usesLibraryFiles,
-            SharedLibraryInfo file,
-            PackageParser.Package changingLib) {
+    private void applyDefiningSharedLibraryUpdateLocked(
+            PackageParser.Package pkg, SharedLibraryInfo libInfo,
+            BiConsumer<SharedLibraryInfo, SharedLibraryInfo> action) {
+        if (pkg.isLibrary()) {
+            if (pkg.staticSharedLibName != null) {
+                SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr(
+                        pkg.staticSharedLibName, pkg.staticSharedLibVersion);
+                action.accept(definedLibrary, libInfo);
+            } else {
+                for (String libraryName : pkg.libraryNames) {
+                    SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr(
+                            libraryName, SharedLibraryInfo.VERSION_UNDEFINED);
+                    action.accept(definedLibrary, libInfo);
+                }
+            }
+        }
+    }
 
-        if (file.getPath() != null) {
-            usesLibraryFiles.add(file.getPath());
+    @GuardedBy("mPackages")
+    private void addSharedLibraryLPr(PackageParser.Package pkg, Set<String> usesLibraryFiles,
+            SharedLibraryInfo libInfo, PackageParser.Package changingLib) {
+
+        if (libInfo.getPath() != null) {
+            usesLibraryFiles.add(libInfo.getPath());
             return;
         }
-        PackageParser.Package p = mPackages.get(file.getPackageName());
-        if (changingLib != null && changingLib.packageName.equals(file.getPackageName())) {
+        PackageParser.Package p = mPackages.get(libInfo.getPackageName());
+        if (changingLib != null && changingLib.packageName.equals(libInfo.getPackageName())) {
             // If we are doing this while in the middle of updating a library apk,
             // then we need to make sure to use that new apk for determining the
             // dependencies here.  (We haven't yet finished committing the new apk
@@ -9618,6 +9670,10 @@
         }
         if (p != null) {
             usesLibraryFiles.addAll(p.getAllCodePaths());
+            // If the package provides libraries, add the dependency to them.
+            applyDefiningSharedLibraryUpdateLocked(pkg, libInfo, (definingLibrary, dependency) -> {
+                definingLibrary.addDependency(dependency);
+            });
             if (p.usesLibraryFiles != null) {
                 Collections.addAll(usesLibraryFiles, p.usesLibraryFiles);
             }
@@ -9630,6 +9686,12 @@
         if (pkg == null) {
             return;
         }
+
+        // If the package provides libraries, clear their old dependencies.
+        // This method will set them up again.
+        applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> {
+            definingLibrary.clearDependencies();
+        });
         // The collection used here must maintain the order of addition (so
         // that libraries are searched in the correct order) and must have no
         // duplicates.
@@ -9656,7 +9718,7 @@
             // usesLibraryFiles while eliminating duplicates.
             Set<String> usesLibraryFiles = new LinkedHashSet<>();
             for (SharedLibraryInfo libInfo : usesLibraryInfos) {
-                addSharedLibraryLPr(usesLibraryFiles, libInfo, changingLib);
+                addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib);
             }
             pkg.usesLibraryFiles = usesLibraryFiles.toArray(new String[usesLibraryFiles.size()]);
         } else {
@@ -11201,7 +11263,7 @@
         }
         SharedLibraryInfo libraryInfo = new SharedLibraryInfo(path, apk, name,
                 version, type, new VersionedPackage(declaringPackageName, declaringVersionCode),
-                null);
+                null, null);
         versionedLib.put(version, libraryInfo);
         return true;
     }
@@ -15153,14 +15215,8 @@
                             pkgList.add(oldPackage.applicationInfo.packageName);
                             sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
                         }
-
-                        clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
-                                | StorageManager.FLAG_STORAGE_CE
-                                | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
                     }
 
-
-
                     // Update the in-memory copy of the previous code paths.
                     PackageSetting ps1 = mSettings.mPackages.get(
                             reconciledPkg.prepareResult.existingPackage.packageName);
@@ -15364,7 +15420,8 @@
 
     /**
      * On successful install, executes remaining steps after commit completes and the package lock
-     * is released.
+     * is released. These are typically more expensive or require calls to installd, which often
+     * locks on {@link #mPackages}.
      */
     private void executePostCommitSteps(CommitRequest commitRequest) {
         for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
@@ -16093,7 +16150,6 @@
         try {
             final PackageParser.Package existingPackage;
             String renamedPackage = null;
-            boolean clearCodeCache = false;
             boolean sysPkg = false;
             String targetVolumeUuid = volumeUuid;
             int targetScanFlags = scanFlags;
@@ -16314,7 +16370,6 @@
                         Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
                                 + ", old=" + oldPackage);
                     }
-                    clearCodeCache = true;
                     res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
                     pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
                             ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
@@ -16370,7 +16425,7 @@
             shouldCloseFreezerBeforeReturn = false;
             return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName,
                     args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg,
-                    clearCodeCache, sysPkg, renamedPackage, freezer);
+                    replace /* clearCodeCache */, sysPkg, renamedPackage, freezer);
         } finally {
             if (shouldCloseFreezerBeforeReturn) {
                 freezer.close();
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index e25cca4..38bd172 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -920,7 +920,10 @@
                 pw.println("Error: must either specify a package size or an APK file");
                 return 1;
             }
-            if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
+            final boolean isApex =
+                    (params.sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0;
+            String splitName = "base." + (isApex ? "apex" : "apk");
+            if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, splitName,
                     false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
                 return 1;
             }
@@ -2262,6 +2265,9 @@
                 case "--force-sdk":
                     sessionParams.installFlags |= PackageManager.INSTALL_FORCE_SDK;
                     break;
+                case "--apex":
+                    sessionParams.installFlags |= PackageManager.INSTALL_APEX;
+                    break;
                 default:
                     throw new IllegalArgumentException("Unknown option " + opt);
             }
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 392d4d8..753c283 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -30,6 +30,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings.Global;
+import android.util.Log;
 import android.util.Slog;
 import android.util.jar.StrictJarFile;
 
@@ -74,7 +75,7 @@
     private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST =
             "pm.dexopt.priv-apps-oob-list";
 
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final Context mContext;
 
@@ -192,6 +193,16 @@
         String[] classLoaderContexts = DexoptUtils.processContextForDexLoad(
                 classLoaderNames, classPaths);
 
+        // A null classLoaderContexts means that there are unsupported class loaders in the
+        // chain.
+        if (classLoaderContexts == null) {
+            if (DEBUG) {
+                Slog.i(TAG, loadingAppInfo.packageName +
+                        " uses unsupported class loader in " + classLoaderNames);
+            }
+            return;
+        }
+
         int dexPathIndex = 0;
         for (String dexPath : dexPathsToRegister) {
             // Find the owning package name.
@@ -219,14 +230,10 @@
                 }
 
                 // Record dex file usage. If the current usage is a new pattern (e.g. new secondary,
-                // or UsedBytOtherApps), record will return true and we trigger an async write
+                // or UsedByOtherApps), record will return true and we trigger an async write
                 // to disk to make sure we don't loose the data in case of a reboot.
 
-                // A null classLoaderContexts means that there are unsupported class loaders in the
-                // chain.
-                String classLoaderContext = classLoaderContexts == null
-                        ? PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT
-                        : classLoaderContexts[dexPathIndex];
+                String classLoaderContext = classLoaderContexts[dexPathIndex];
                 if (mPackageDexUsage.record(searchResult.mOwningPackageName,
                         dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
                         loadingAppInfo.packageName, classLoaderContext)) {
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 602ce3b..86f7380 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -21,6 +21,7 @@
 import android.os.Build;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FastPrintWriter;
 import com.android.server.pm.AbstractStatsBase;
 import com.android.server.pm.PackageManagerServiceUtils;
@@ -78,14 +79,16 @@
     // skip optimizations on that dex files.
     /*package*/ static final String VARIABLE_CLASS_LOADER_CONTEXT =
             "=VariableClassLoaderContext=";
-    // The marker used for unsupported class loader contexts.
-    /*package*/ static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
-            "=UnsupportedClassLoaderContext=";
     // The markers used for unknown class loader contexts. This can happen if the dex file was
     // recorded in a previous version and we didn't have a chance to update its usage.
     /*package*/ static final String UNKNOWN_CLASS_LOADER_CONTEXT =
             "=UnknownClassLoaderContext=";
 
+    // The marker used for unsupported class loader contexts (no longer written, may occur in old
+    // files so discarded on read).
+    private static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
+            "=UnsupportedClassLoaderContext=";
+
     // Map which structures the information we have on a package.
     // Maps package name to package data (which stores info about UsedByOtherApps and
     // secondary dex files.).
@@ -365,6 +368,12 @@
                 Set<String> loadingPackages = maybeReadLoadingPackages(in, version);
                 String classLoaderContext = maybeReadClassLoaderContext(in, version);
 
+                if (UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(classLoaderContext)) {
+                    // We used to record use of unsupported class loaders, but we no longer do.
+                    // Discard such entries; they will be deleted when we next write the file.
+                    continue;
+                }
+
                 int ownerUserId = Integer.parseInt(elems[0]);
                 boolean isUsedByOtherApps = readBoolean(elems[1]);
                 DexUseInfo dexUseInfo = new DexUseInfo(isUsedByOtherApps, ownerUserId,
@@ -709,13 +718,13 @@
         //      the compiled code will be private.
         private boolean mUsedByOtherAppsBeforeUpgrade;
 
-        public PackageUseInfo() {
+        /*package*/ PackageUseInfo() {
             mCodePathsUsedByOtherApps = new HashMap<>();
             mDexUseInfoMap = new HashMap<>();
         }
 
         // Creates a deep copy of the `other`.
-        public PackageUseInfo(PackageUseInfo other) {
+        private PackageUseInfo(PackageUseInfo other) {
             mCodePathsUsedByOtherApps = new HashMap<>();
             for (Map.Entry<String, Set<String>> e : other.mCodePathsUsedByOtherApps.entrySet()) {
                 mCodePathsUsedByOtherApps.put(e.getKey(), new HashSet<>(e.getValue()));
@@ -796,8 +805,9 @@
         // Packages who load this dex file.
         private final Set<String> mLoadingPackages;
 
-        public DexUseInfo(boolean isUsedByOtherApps, int ownerUserId, String classLoaderContext,
-                String loaderIsa) {
+        @VisibleForTesting
+        /* package */ DexUseInfo(boolean isUsedByOtherApps, int ownerUserId,
+                String classLoaderContext, String loaderIsa) {
             mIsUsedByOtherApps = isUsedByOtherApps;
             mOwnerUserId = ownerUserId;
             mClassLoaderContext = classLoaderContext;
@@ -809,7 +819,7 @@
         }
 
         // Creates a deep copy of the `other`.
-        public DexUseInfo(DexUseInfo other) {
+        private DexUseInfo(DexUseInfo other) {
             mIsUsedByOtherApps = other.mIsUsedByOtherApps;
             mOwnerUserId = other.mOwnerUserId;
             mClassLoaderContext = other.mClassLoaderContext;
@@ -827,11 +837,7 @@
             if (UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext)) {
                 // Can happen if we read a previous version.
                 mClassLoaderContext = dexUseInfo.mClassLoaderContext;
-            } else if (UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(dexUseInfo.mClassLoaderContext)) {
-                // We detected an unsupported context.
-                mClassLoaderContext = UNSUPPORTED_CLASS_LOADER_CONTEXT;
-            } else if (!UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext) &&
-                    !Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
+            } else if (!Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
                 // We detected a context change.
                 mClassLoaderContext = VARIABLE_CLASS_LOADER_CONTEXT;
             }
@@ -846,7 +852,7 @@
             return mIsUsedByOtherApps;
         }
 
-        public int getOwnerUserId() {
+        /* package */ int getOwnerUserId() {
             return mOwnerUserId;
         }
 
@@ -860,17 +866,15 @@
 
         public String getClassLoaderContext() { return mClassLoaderContext; }
 
-        public boolean isUnsupportedClassLoaderContext() {
-            return UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
-        }
-
-        public boolean isUnknownClassLoaderContext() {
+        @VisibleForTesting
+        /* package */ boolean isUnknownClassLoaderContext() {
             // The class loader context may be unknown if we loaded the data from a previous version
             // which didn't save the context.
             return UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
         }
 
-        public boolean isVariableClassLoaderContext() {
+        @VisibleForTesting
+        /* package */ boolean isVariableClassLoaderContext() {
             return VARIABLE_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
         }
     }
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index f8c4ca2..5fb1def 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -1035,8 +1035,6 @@
 
         releaseSelfIfNeeded();
 
-        mSupervisor.getKeyguardController().onDisplayRemoved(mDisplayId);
-
         if (!mAllSleepTokens.isEmpty()) {
             mSupervisor.mSleepTokens.removeAll(mAllSleepTokens);
             mAllSleepTokens.clear();
@@ -1049,6 +1047,7 @@
             mWindowContainerController.removeContainer();
             mWindowContainerController = null;
             mSupervisor.removeChild(this);
+            mSupervisor.getKeyguardController().onDisplayRemoved(mDisplayId);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ba03034..348b2af 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -314,7 +314,7 @@
      * Last applied orientation of the display.
      * Constants as per {@link android.content.pm.ActivityInfo.ScreenOrientation}.
      *
-     * @see WindowManagerService#updateOrientationFromAppTokensLocked(boolean, int)
+     * @see #updateOrientationFromAppTokens()
      */
     private int mLastOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
 
@@ -1045,18 +1045,10 @@
         return mLastOrientation;
     }
 
-    void setLastOrientation(int orientation) {
-        mLastOrientation = orientation;
-    }
-
     boolean getAltOrientation() {
         return mAltOrientation;
     }
 
-    void setAltOrientation(boolean altOrientation) {
-        mAltOrientation = altOrientation;
-    }
-
     int getLastWindowForcedOrientation() {
         return mLastWindowForcedOrientation;
     }
@@ -1109,6 +1101,34 @@
         return true;
     }
 
+    /** Notify the configuration change of this display. */
+    void sendNewConfiguration() {
+        mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget();
+    }
+
+    /**
+     * Determine the new desired orientation of this display.
+     *
+     * The orientation is computed from non-application windows first. If none of the
+     * non-application windows specify orientation, the orientation is computed from application
+     * tokens.
+     *
+     * @return {@code true} if the orientation is changed.
+     */
+    boolean updateOrientationFromAppTokens() {
+        return updateOrientationFromAppTokens(false /* forceUpdate */);
+    }
+
+    boolean updateOrientationFromAppTokens(boolean forceUpdate) {
+        final int req = getOrientation();
+        if (req != mLastOrientation || forceUpdate) {
+            mLastOrientation = req;
+            mDisplayRotation.setCurrentOrientation(req);
+            return updateRotationUnchecked(forceUpdate);
+        }
+        return false;
+    }
+
     /**
      * Update rotation of the display and send configuration if the rotation is changed.
      *
@@ -1117,7 +1137,7 @@
     boolean updateRotationAndSendNewConfigIfNeeded() {
         final boolean changed = updateRotationUnchecked(false /* forceUpdate */);
         if (changed) {
-            mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mDisplayId).sendToTarget();
+            sendNewConfiguration();
         }
         return changed;
     }
@@ -2343,6 +2363,7 @@
             mWindowingLayer.release();
             mOverlayLayer.release();
         } finally {
+            mDisplayReady = false;
             mRemovingDisplay = false;
         }
 
@@ -3348,9 +3369,9 @@
 
             if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
                 if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
-                if (mService.updateOrientationFromAppTokensLocked(mDisplayId)) {
+                if (updateOrientationFromAppTokens()) {
                     setLayoutNeeded();
-                    mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, mDisplayId).sendToTarget();
+                    sendNewConfiguration();
                 }
             }
 
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 62078f7..67fe5c4 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -42,7 +42,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
 import static com.android.server.wm.WindowManagerService.H.WINDOW_FREEZE_TIMEOUT;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
@@ -911,10 +910,8 @@
         boolean changed = false;
         for (int i = mChildren.size() - 1; i >= 0; i--) {
             final DisplayContent displayContent = mChildren.get(i);
-            if (displayContent.updateRotationUnchecked()) {
+            if (displayContent.updateRotationAndSendNewConfigIfNeeded()) {
                 changed = true;
-                mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
-                        .sendToTarget();
             }
         }
         return changed;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e456d8d..a641f75 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1448,7 +1448,7 @@
             if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addWindow: New client "
                     + client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));
 
-            if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(displayId)) {
+            if (win.isVisibleOrAdding() && displayContent.updateOrientationFromAppTokens()) {
                 reportNewConfig = true;
             }
         }
@@ -2060,7 +2060,7 @@
 
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                     "relayoutWindow: updateOrientationFromAppTokens");
-            configChanged = updateOrientationFromAppTokensLocked(displayId);
+            configChanged = dc.updateOrientationFromAppTokens();
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
 
             if (toBeDisplayed && win.mIsWallpaper) {
@@ -2351,6 +2351,15 @@
         return config;
     }
 
+    /**
+     * Update orientation of the target display, returning a non-null new Configuration if it has
+     * changed from the current orientation. If a non-null configuration is returned, someone must
+     * call {@link #setNewDisplayOverrideConfiguration(Configuration, int)} to tell the window
+     * manager it can unfreeze the screen. This will typically be done by calling
+     * {@link #sendNewConfiguration(int)}.
+     *
+     * @see android.view.IWindowManager#updateOrientationFromAppTokens(Configuration, IBinder, int)
+     */
     private Configuration updateOrientationFromAppTokensLocked(Configuration currentConfig,
             IBinder freezeThisOneIfNeeded, int displayId, boolean forceUpdate) {
         if (!mDisplayReady) {
@@ -2358,7 +2367,8 @@
         }
         Configuration config = null;
 
-        if (updateOrientationFromAppTokensLocked(displayId, forceUpdate)) {
+        final DisplayContent dc = mRoot.getDisplayContent(displayId);
+        if (dc != null && dc.updateOrientationFromAppTokens(forceUpdate)) {
             // If we changed the orientation but mOrientationChangeComplete is already true,
             // we used seamless rotation, and we don't need to freeze the screen.
             if (freezeThisOneIfNeeded != null && !mRoot.mOrientationChangeComplete) {
@@ -2393,43 +2403,6 @@
         return config;
     }
 
-    /**
-     * Determine the new desired orientation of the display, returning a non-null new Configuration
-     * if it has changed from the current orientation.  IF TRUE IS RETURNED SOMEONE MUST CALL
-     * {@link #setNewDisplayOverrideConfiguration(Configuration, int)} TO TELL THE WINDOW MANAGER IT
-     * CAN UNFREEZE THE SCREEN.  This will typically be done for you if you call
-     * {@link #sendNewConfiguration(int)}.
-     *
-     * The orientation is computed from non-application windows first. If none of the
-     * non-application windows specify orientation, the orientation is computed from application
-     * tokens.
-     * @see android.view.IWindowManager#updateOrientationFromAppTokens(Configuration, IBinder, int)
-     */
-    boolean updateOrientationFromAppTokensLocked(int displayId) {
-        return updateOrientationFromAppTokensLocked(displayId, false /* forceUpdate */);
-    }
-
-    boolean updateOrientationFromAppTokensLocked(int displayId, boolean forceUpdate) {
-        long ident = Binder.clearCallingIdentity();
-        try {
-            final DisplayContent dc = mRoot.getDisplayContent(displayId);
-            if (dc == null) {
-                return false;
-            }
-            final int req = dc.getOrientation();
-            if (req != dc.getLastOrientation() || forceUpdate) {
-                dc.setLastOrientation(req);
-                //send a message to Policy indicating orientation change to take
-                //action like disabling/enabling sensors etc.,
-                dc.getDisplayRotation().setCurrentOrientation(req);
-                return dc.updateRotationUnchecked(forceUpdate);
-            }
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
     @Override
     public int[] setNewDisplayOverrideConfiguration(Configuration overrideConfig, int displayId) {
         if (!checkCallingPermission(MANAGE_APP_TOKENS, "setNewDisplayOverrideConfiguration()")) {
@@ -3676,8 +3649,7 @@
                         layoutNeeded = true;
                     }
                     if (rotationChanged || alwaysSendConfiguration) {
-                        mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
-                                .sendToTarget();
+                        displayContent.sendNewConfiguration();
                     }
                 }
 
@@ -4572,15 +4544,17 @@
                 }
 
                 case SEND_NEW_CONFIGURATION: {
-                    removeMessages(SEND_NEW_CONFIGURATION, msg.obj);
-                    final int displayId = (Integer) msg.obj;
-                    if (mRoot.getDisplayContent(displayId) != null) {
-                        sendNewConfiguration(displayId);
+                    final DisplayContent displayContent = (DisplayContent) msg.obj;
+                    removeMessages(SEND_NEW_CONFIGURATION, displayContent);
+                    if (displayContent.isReady()) {
+                        sendNewConfiguration(displayContent.getDisplayId());
                     } else {
                         // Message could come after display has already been removed.
                         if (DEBUG_CONFIGURATION) {
-                            Slog.w(TAG, "Trying to send configuration to non-existing displayId="
-                                    + displayId);
+                            final String reason = displayContent.getParent() == null
+                                    ? "detached" : "unready";
+                            Slog.w(TAG, "Trying to send configuration to " + reason + " display="
+                                    + displayContent);
                         }
                     }
                     break;
@@ -4904,7 +4878,6 @@
     /** The global settings only apply to default display. */
     private void applyForcedPropertiesForDefaultDisplay() {
         final DisplayContent displayContent = getDefaultDisplayContentLocked();
-        boolean changed = false;
         // Display size.
         String sizeStr = Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.DISPLAY_SIZE_FORCED);
@@ -4923,7 +4896,6 @@
                         Slog.i(TAG_WM, "FORCED DISPLAY SIZE: " + width + "x" + height);
                         displayContent.updateBaseDisplayMetrics(width, height,
                                 displayContent.mBaseDisplayDensity);
-                        changed = true;
                     }
                 } catch (NumberFormatException ex) {
                 }
@@ -4934,7 +4906,6 @@
         final int density = getForcedDisplayDensityForUserLocked(mCurrentUserId);
         if (density != 0) {
             displayContent.mBaseDisplayDensity = density;
-            changed = true;
         }
 
         // Display scaling mode.
@@ -4943,11 +4914,6 @@
         if (mode != 0) {
             Slog.i(TAG_WM, "FORCED DISPLAY SCALING DISABLED");
             displayContent.mDisplayScalingDisabled = true;
-            changed = true;
-        }
-
-        if (changed) {
-            reconfigureDisplayLocked(displayContent);
         }
     }
 
@@ -5074,8 +5040,7 @@
         displayContent.configureDisplayPolicy();
         displayContent.setLayoutNeeded();
 
-        final int displayId = displayContent.getDisplayId();
-        boolean configChanged = updateOrientationFromAppTokensLocked(displayId);
+        boolean configChanged = displayContent.updateOrientationFromAppTokens();
         final Configuration currentDisplayConfig = displayContent.getConfiguration();
         mTempConfiguration.setTo(currentDisplayConfig);
         displayContent.computeScreenConfiguration(mTempConfiguration);
@@ -5085,7 +5050,7 @@
             mWaitingForConfig = true;
             startFreezingDisplayLocked(0 /* exitAnim */,
                     0 /* enterAnim */, displayContent);
-            mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayId).sendToTarget();
+            displayContent.sendNewConfiguration();
         }
 
         mWindowPlacerLocked.performSurfacePlacement();
@@ -5389,7 +5354,6 @@
         if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
                 && screenRotationAnimation.hasScreenshot()) {
             if (DEBUG_ORIENTATION) Slog.i(TAG_WM, "**** Dismissing screen rotation animation");
-            // TODO(multidisplay): rotation on main screen only.
             DisplayInfo displayInfo = displayContent.getDisplayInfo();
             // Get rotation animation again, with new top window
             if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) {
@@ -5419,7 +5383,7 @@
         // to avoid inconsistent states.  However, something interesting
         // could have actually changed during that time so re-evaluate it
         // now to catch that.
-        configChanged = updateOrientationFromAppTokensLocked(displayId);
+        configChanged = displayContent != null && displayContent.updateOrientationFromAppTokens();
 
         // A little kludge: a lot could have happened while the
         // display was frozen, so now that we are coming back we
@@ -5437,7 +5401,7 @@
         }
 
         if (configChanged) {
-            mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayId).sendToTarget();
+            displayContent.sendNewConfiguration();
         }
         mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN);
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d4c812b..99f65c3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -103,7 +103,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
 import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
@@ -1942,8 +1941,11 @@
             removeImmediately();
             // Removing a visible window will effect the computed orientation
             // So just update orientation if needed.
-            if (wasVisible && mService.updateOrientationFromAppTokensLocked(displayId)) {
-                mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
+            if (wasVisible) {
+                final DisplayContent displayContent = getDisplayContent();
+                if (displayContent.updateOrientationFromAppTokens()) {
+                    displayContent.sendNewConfiguration();
+                }
             }
             mService.updateFocusedWindowLocked(isFocused()
                             ? UPDATE_FOCUS_REMOVING_FOCUS
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
index 4514492..e9b2d7f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
@@ -53,8 +53,8 @@
 
     private final INetdEventCallback mNetdEventCallback = new BaseNetdEventCallback() {
         @Override
-        public void onDnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount,
-                long timestamp, int uid) {
+        public void onDnsEvent(int netId, int eventType, int returnCode, String hostname,
+                String[] ipAddresses, int ipAddressesCount, long timestamp, int uid) {
             if (!mIsLoggingEnabled.get()) {
                 return;
             }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 49f410d..f432c8d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -68,6 +68,7 @@
 import com.android.server.appbinding.AppBindingService;
 import com.android.server.audio.AudioService;
 import com.android.server.biometrics.BiometricService;
+import com.android.server.biometrics.iris.IrisService;
 import com.android.server.broadcastradio.BroadcastRadioService;
 import com.android.server.camera.CameraServiceProxy;
 import com.android.server.clipboard.ClipboardService;
@@ -1589,6 +1590,8 @@
 
             final boolean hasFeatureFace
                     = mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE);
+            final boolean hasFeatureIris
+                    = mPackageManager.hasSystemFeature(PackageManager.FEATURE_IRIS);
             final boolean hasFeatureFingerprint
                     = mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
 
@@ -1598,13 +1601,19 @@
                 traceEnd();
             }
 
+            if (hasFeatureIris) {
+                traceBeginAndSlog("StartIrisSensor");
+                mSystemServiceManager.startService(IrisService.class);
+                traceEnd();
+            }
+
             if (hasFeatureFingerprint) {
                 traceBeginAndSlog("StartFingerprintSensor");
                 mSystemServiceManager.startService(FingerprintService.class);
                 traceEnd();
             }
 
-            if (hasFeatureFace || hasFeatureFingerprint) {
+            if (hasFeatureFace || hasFeatureIris || hasFeatureFingerprint) {
                 // Start this service after all biometric services.
                 traceBeginAndSlog("StartBiometricPromptService");
                 mSystemServiceManager.startService(BiometricService.class);
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java
index b5a354c..9c8a382 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java
@@ -51,7 +51,9 @@
 @MediumTest
 public class NetworkWatchlistServiceTests {
 
-    private static final long NETWOR_EVENT_TIMEOUT_SEC = 1;
+    private static final long NETWORK_EVENT_TIMEOUT_SEC = 1;
+    private static final int TEST_NETID = 100;
+    private static final int TEST_EVENT_TYPE = 1;
     private static final String TEST_HOST = "testhost.com";
     private static final String TEST_IP = "7.6.8.9";
     private static final String[] TEST_IPS =
@@ -180,8 +182,9 @@
                     }
                 };
         mWatchlistService.mNetworkWatchlistHandler = testDnsHandler;
-        connectivityMetrics.callback.onDnsEvent(TEST_HOST, TEST_IPS, TEST_IPS.length, 123L, 456);
-        if (!testDnsLatch.await(NETWOR_EVENT_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+        connectivityMetrics.callback.onDnsEvent(TEST_NETID, TEST_EVENT_TYPE, 0,
+                TEST_HOST, TEST_IPS, TEST_IPS.length, 123L, 456);
+        if (!testDnsLatch.await(NETWORK_EVENT_TIMEOUT_SEC, TimeUnit.SECONDS)) {
             fail("Timed out waiting for network event");
         }
         assertEquals(TEST_HOST, dnsParams[0]);
@@ -206,7 +209,7 @@
                 };
         mWatchlistService.mNetworkWatchlistHandler = testConnectHandler;
         connectivityMetrics.callback.onConnectEvent(TEST_IP, 80, 123L, 456);
-        if (!testConnectLatch.await(NETWOR_EVENT_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+        if (!testConnectLatch.await(NETWORK_EVENT_TIMEOUT_SEC, TimeUnit.SECONDS)) {
             fail("Timed out waiting for network event");
         }
         assertNull(connectParams[0]);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index b0b7def..ebac8fb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -38,12 +38,12 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import libcore.io.IoUtils;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import libcore.io.IoUtils;
-
 import java.io.File;
 import java.lang.reflect.Array;
 import java.lang.reflect.Field;
@@ -490,7 +490,7 @@
         pkg.usesLibraryFiles = new String[] { "foo13"};
 
         pkg.usesLibraryInfos = new ArrayList<>();
-        pkg.usesLibraryInfos.add(new SharedLibraryInfo(null, null, null, 0L, 0, null, null));
+        pkg.usesLibraryInfos.add(new SharedLibraryInfo(null, null, null, 0L, 0, null, null, null));
 
         pkg.mOriginalPackages = new ArrayList<>();
         pkg.mOriginalPackages.add("foo14");
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 416a616..bd42b73 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -401,15 +401,7 @@
         List<String> secondaries = mBarUser0UnsupportedClassLoader.getSecondaryDexPaths();
         notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
 
-        PackageUseInfo pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
-        assertIsUsedByOtherApps(mBarUser0UnsupportedClassLoader, pui, false);
-        assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
-        // We expect that all the contexts are unsupported.
-        String[] expectedContexts =
-                Collections.nCopies(secondaries.size(),
-                        PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT).toArray(new String[0]);
-        assertSecondaryUse(mBarUser0UnsupportedClassLoader, pui, secondaries,
-                /*isUsedByOtherApps*/false, mUser0, expectedContexts);
+        assertNoUseInfo(mBarUser0UnsupportedClassLoader);
     }
 
     @Test
@@ -438,27 +430,18 @@
     }
 
     @Test
-    public void testNotifyUnsupportedClassLoaderDoesNotChange() {
-        List<String> secondaries = mBarUser0UnsupportedClassLoader.getSecondaryDexPaths();
+    public void testNotifyUnsupportedClassLoaderDoesNotChangeExisting() {
+        List<String> secondaries = mBarUser0.getSecondaryDexPaths();
+
+        notifyDexLoad(mBarUser0, secondaries, mUser0);
+        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
+        assertSecondaryUse(mBarUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
+
+        // Record bar secondaries again with an unsupported class loader. This should not change the
+        // context.
         notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
-
-        PackageUseInfo pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
-        assertIsUsedByOtherApps(mBarUser0UnsupportedClassLoader, pui, false);
-        assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
-        // We expect that all the contexts are unsupported.
-        String[] expectedContexts =
-                Collections.nCopies(secondaries.size(),
-                        PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT).toArray(new String[0]);
-        assertSecondaryUse(mBarUser0UnsupportedClassLoader, pui, secondaries,
-                /*isUsedByOtherApps*/false, mUser0, expectedContexts);
-
-        // Record bar secondaries again with a different class loader. This will change the context.
-        // However, because the context was already marked as unsupported we should not chage it.
-        notifyDexLoad(mBarUser0DelegateLastClassLoader, secondaries, mUser0);
-        pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
-        assertSecondaryUse(mBarUser0UnsupportedClassLoader, pui, secondaries,
-                /*isUsedByOtherApps*/false, mUser0, expectedContexts);
-
+        pui = getPackageUseInfo(mBarUser0);
+        assertSecondaryUse(mBarUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index 3e93dcf..7755e94 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -399,20 +399,6 @@
     }
 
     @Test
-    public void testRecordClassLoaderContextUnsupportedContext() {
-        // Record a secondary dex file.
-        assertTrue(record(mFooSecondary1User0));
-        // Now update its context.
-        TestData unsupportedContext = mFooSecondary1User0.updateClassLoaderContext(
-                PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT);
-        assertTrue(record(unsupportedContext));
-
-        assertPackageDexUsage(null, unsupportedContext);
-        writeAndReadBack();
-        assertPackageDexUsage(null, unsupportedContext);
-    }
-
-    @Test
     public void testRecordClassLoaderContextTransitionFromUnknown() {
         // Record a secondary dex file.
         TestData unknownContext = mFooSecondary1User0.updateClassLoaderContext(
@@ -440,29 +426,41 @@
         PackageDexUsage.DexUseInfo validContext = new DexUseInfo(isUsedByOtherApps, userId,
                 "valid_context", "arm");
         assertFalse(validContext.isUnknownClassLoaderContext());
-        assertFalse(validContext.isUnsupportedClassLoaderContext());
         assertFalse(validContext.isVariableClassLoaderContext());
 
-        PackageDexUsage.DexUseInfo unsupportedContext = new DexUseInfo(isUsedByOtherApps, userId,
-                PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT, "arm");
-        assertFalse(unsupportedContext.isUnknownClassLoaderContext());
-        assertTrue(unsupportedContext.isUnsupportedClassLoaderContext());
-        assertFalse(unsupportedContext.isVariableClassLoaderContext());
-
         PackageDexUsage.DexUseInfo variableContext = new DexUseInfo(isUsedByOtherApps, userId,
                 PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT, "arm");
         assertFalse(variableContext.isUnknownClassLoaderContext());
-        assertFalse(variableContext.isUnsupportedClassLoaderContext());
         assertTrue(variableContext.isVariableClassLoaderContext());
 
         PackageDexUsage.DexUseInfo unknownContext = new DexUseInfo(isUsedByOtherApps, userId,
                 PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT, "arm");
         assertTrue(unknownContext.isUnknownClassLoaderContext());
-        assertFalse(unknownContext.isUnsupportedClassLoaderContext());
         assertFalse(unknownContext.isVariableClassLoaderContext());
     }
 
     @Test
+    public void testUnsupportedClassLoaderDiscardedOnRead() throws Exception {
+        String content = "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__2\n"
+                + mBarSecondary1User0.mPackageName + "\n"
+                + "#" + mBarSecondary1User0.mDexFile + "\n"
+                + "0,0," + mBarSecondary1User0.mLoaderIsa + "\n"
+                + "@\n"
+                + "=UnsupportedClassLoaderContext=\n"
+
+                + mFooSecondary1User0.mPackageName + "\n"
+                + "#" + mFooSecondary1User0.mDexFile + "\n"
+                + "0,0," + mFooSecondary1User0.mLoaderIsa + "\n"
+                + "@\n"
+                + mFooSecondary1User0.mClassLoaderContext + "\n";
+
+        mPackageDexUsage.read(new StringReader(content));
+
+        assertPackageDexUsage(mFooBaseUser0, mFooSecondary1User0);
+        assertPackageDexUsage(mBarBaseUser0);
+    }
+
+    @Test
     public void testReadVersion1() {
         String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
         // Equivalent to
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/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 73a34b6..ff84803 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -41,7 +41,8 @@
         <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
         <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskChangeCallbacks" />
         <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskDescriptionChange" />
-        <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity" />
+        <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity"
+                  android:showWhenLocked="true" />
     </application>
 
     <instrumentation
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/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 3127b35..fa16bfe 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -15,6 +15,7 @@
 package android.telecom;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressAutoDoc;
 import android.annotation.SuppressLint;
@@ -175,6 +176,33 @@
             "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
 
     /**
+     * Broadcast intent action indicating that the current default call screening app has changed.
+     *
+     * The string extra {@link #EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME} will contain the
+     * name of the Component of the previous or the new call screening app.
+     *
+     * The boolean extra {@link #EXTRA_IS_DEFAULT_CALL_SCREENING_APP} will indicate the component
+     * name in the String extra {@link #EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME} is default
+     * call screening app or not.
+     */
+    public static final String ACTION_DEFAULT_CALL_SCREENING_APP_CHANGED =
+        "android.telecom.action.DEFAULT_CALL_SCREENING_APP_CHANGED";
+
+    /**
+     * Extra value used with {@link #ACTION_DEFAULT_CALL_SCREENING_APP_CHANGED} broadcast to
+     * indicate the ComponentName of the call screening app which has changed.
+     */
+    public static final String EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME =
+            "android.telecom.extra.DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME";
+
+    /**
+     * Extra value used with {@link #ACTION_DEFAULT_CALL_SCREENING_APP_CHANGED} broadcast to
+     * indicate whether an app is the default call screening app.
+     */
+    public static final String EXTRA_IS_DEFAULT_CALL_SCREENING_APP =
+            "android.telecom.extra.IS_DEFAULT_CALL_SCREENING_APP";
+
+    /**
      * Optional extra for {@link android.content.Intent#ACTION_CALL} containing a boolean that
      * determines whether the speakerphone should be automatically turned on for an outgoing call.
      */
@@ -1169,6 +1197,79 @@
     }
 
     /**
+     * Used to trigger display of the ChangeDefaultCallScreeningApp activity to prompt the user to
+     * change the call screening app.
+     *
+     * A {@link SecurityException} will be thrown if calling package name doesn't match the package
+     * of the passed {@link ComponentName}
+     *
+     * @param componentName to verify that the calling package name matches the package of the
+     * passed ComponentName.
+     */
+    public void requestChangeDefaultCallScreeningApp(@NonNull ComponentName componentName) {
+        try {
+            if (isServiceConnected()) {
+                getTelecomService().requestChangeDefaultCallScreeningApp(componentName, mContext
+                    .getOpPackageName());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG,
+                "RemoteException calling ITelecomService#requestChangeDefaultCallScreeningApp.",
+                e);
+        }
+    }
+
+    /**
+     * Used to verify that the passed ComponentName is default call screening app.
+     *
+     * @param componentName to verify that the package of the passed ComponentName matched the default
+     * call screening packageName.
+     *
+     * @return {@code true} if the passed componentName matches the default call screening's, {@code
+     * false} if the passed componentName is null, or it doesn't match default call screening's.
+     */
+    public boolean isDefaultCallScreeningApp(ComponentName componentName) {
+        try {
+            if (isServiceConnected()) {
+                return getTelecomService().isDefaultCallScreeningApp(componentName);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG,
+                "RemoteException calling ITelecomService#isDefaultCallScreeningApp.",
+                e);
+        }
+        return false;
+    }
+
+    /**
+     * Used to set the default call screening package.
+     *
+     * Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} Requires
+     * permission: {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
+     *
+     * A {@link IllegalArgumentException} will be thrown if the specified package and component name
+     * of {@link ComponentName} does't exist, or the specified component of {@link ComponentName}
+     * does't have {@link android.Manifest.permission#BIND_SCREENING_SERVICE}.
+     *
+     * @param componentName to set the default call screening to.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+        android.Manifest.permission.MODIFY_PHONE_STATE,
+        android.Manifest.permission.WRITE_SECURE_SETTINGS
+    })
+    public void setDefaultCallScreeningApp(ComponentName componentName) {
+        try {
+            if (isServiceConnected()) {
+                getTelecomService().setDefaultCallScreeningApp(componentName);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG,
+                "RemoteException calling ITelecomService#setDefaultCallScreeningApp.", e);
+        }
+    }
+
+    /**
      * Return whether a given phone number is the configured voicemail number for a
      * particular phone account.
      *
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index df7d683..d97f0c5 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -256,6 +256,21 @@
     boolean setDefaultDialer(in String packageName);
 
     /**
+     * @see TelecomServiceImpl#requestChangeDefaultCallScreeningApp
+     */
+    void requestChangeDefaultCallScreeningApp(in ComponentName componentNamem, String callingPackage);
+
+    /**
+     * @see TelecomServiceImpl#isDefaultCallScreeningApp
+     */
+    boolean isDefaultCallScreeningApp(in ComponentName componentName);
+
+    /**
+     * @see TelecomServiceImpl#setDefaultCallScreeningApp
+     */
+    void setDefaultCallScreeningApp(in ComponentName componentName);
+
+    /**
     * @see TelecomServiceImpl#createManageBlockedNumbersIntent
     **/
     Intent createManageBlockedNumbersIntent();
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/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index 04266c5..b9222a8 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -17,8 +17,8 @@
 package android.net;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import android.support.test.filters.SmallTest;
@@ -252,6 +252,39 @@
         }
     }
 
+    @Test
+    public void testMatches() {
+        // match 4 bytes prefix
+        assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches(
+                MacAddress.fromString("aa:bb:cc:dd:00:00"),
+                MacAddress.fromString("ff:ff:ff:ff:00:00")));
+
+        // match bytes 0,1,2 and 5
+        assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches(
+                MacAddress.fromString("aa:bb:cc:00:00:11"),
+                MacAddress.fromString("ff:ff:ff:00:00:ff")));
+
+        // match 34 bit prefix
+        assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches(
+                MacAddress.fromString("aa:bb:cc:dd:c0:00"),
+                MacAddress.fromString("ff:ff:ff:ff:c0:00")));
+
+        // fail to match 36 bit prefix
+        assertFalse(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches(
+                MacAddress.fromString("aa:bb:cc:dd:40:00"),
+                MacAddress.fromString("ff:ff:ff:ff:f0:00")));
+
+        // match all 6 bytes
+        assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches(
+                MacAddress.fromString("aa:bb:cc:dd:ee:11"),
+                MacAddress.fromString("ff:ff:ff:ff:ff:ff")));
+
+        // match none of 6 bytes
+        assertTrue(MacAddress.fromString("aa:bb:cc:dd:ee:11").matches(
+                MacAddress.fromString("00:00:00:00:00:00"),
+                MacAddress.fromString("00:00:00:00:00:00")));
+    }
+
     static byte[] toByteArray(int... in) {
         byte[] out = new byte[in.length];
         for (int i = 0; i < in.length; i++) {
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 0a517ab..0bc5221 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -306,8 +306,29 @@
             break;
         }
 
-        if (entry->overlayable) {
-          printer->Print(" OVERLAYABLE");
+        for (size_t i = 0; i < entry->overlayable_declarations.size(); i++) {
+          printer->Print((i == 0) ? " " : "|");
+          printer->Print("OVERLAYABLE");
+
+          if (entry->overlayable_declarations[i].policy) {
+            switch (entry->overlayable_declarations[i].policy.value()) {
+              case Overlayable::Policy::kProduct:
+                printer->Print("_PRODUCT");
+                break;
+              case Overlayable::Policy::kProductServices:
+                printer->Print("_PRODUCT_SERVICES");
+                break;
+              case Overlayable::Policy::kSystem:
+                printer->Print("_SYSTEM");
+                break;
+              case Overlayable::Policy::kVendor:
+                printer->Print("_VENDOR");
+                break;
+              case Overlayable::Policy::kPublic:
+                printer->Print("_PUBLIC");
+                break;
+            }
+          }
         }
 
         printer->Println();
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 9a3f14c..4f25e09 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -99,7 +99,7 @@
   ResourceId id;
   Visibility::Level visibility_level = Visibility::Level::kUndefined;
   bool allow_new = false;
-  bool overlayable = false;
+  std::vector<Overlayable> overlayable_declarations;
 
   std::string comment;
   std::unique_ptr<Value> value;
@@ -133,11 +133,8 @@
     }
   }
 
-  if (res->overlayable) {
-    Overlayable overlayable;
-    overlayable.source = res->source;
-    overlayable.comment = res->comment;
-    if (!table->SetOverlayable(res->name, overlayable, diag)) {
+  for (auto& overlayable : res->overlayable_declarations) {
+    if (!table->AddOverlayable(res->name, overlayable, diag)) {
       return false;
     }
   }
@@ -673,7 +670,7 @@
   if (can_be_bag) {
     const auto bag_iter = elToBagMap.find(resource_type);
     if (bag_iter != elToBagMap.end()) {
-      // Ensure we have a name (unless this is a <public-group>).
+      // Ensure we have a name (unless this is a <public-group> or <overlayable>).
       if (resource_type != "public-group" && resource_type != "overlayable") {
         if (!maybe_name) {
           diag_->Error(DiagMessage(out_resource->source)
@@ -1062,74 +1059,137 @@
 bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource) {
   if (out_resource->config != ConfigDescription::DefaultConfig()) {
     diag_->Warn(DiagMessage(out_resource->source)
-                << "ignoring configuration '" << out_resource->config << "' for <overlayable> tag");
+                    << "ignoring configuration '" << out_resource->config
+                    << "' for <overlayable> tag");
   }
 
-  if (Maybe<StringPiece> maybe_policy = xml::FindNonEmptyAttribute(parser, "policy")) {
-    const StringPiece& policy = maybe_policy.value();
-    if (policy != "system") {
-      diag_->Error(DiagMessage(out_resource->source)
-                   << "<overlayable> has invalid policy '" << policy << "'");
-      return false;
-    }
-  }
+  std::string comment;
+  std::vector<Overlayable::Policy> policies;
 
   bool error = false;
-  const size_t depth = parser->depth();
-  while (xml::XmlPullParser::NextChildNode(parser, depth)) {
-    if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
-      // Skip text/comments.
+  const size_t start_depth = parser->depth();
+  while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
+    xml::XmlPullParser::Event event = parser->event();
+    if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth) {
+      // Break the loop when exiting the overyabale element
+      break;
+    } else if (event == xml::XmlPullParser::Event::kEndElement
+               && parser->depth() == start_depth + 1) {
+      // Clear the current policies when exiting the policy element
+      policies.clear();
+      continue;
+    } else if (event == xml::XmlPullParser::Event::kComment) {
+      // Get the comment of individual item elements
+      comment = parser->comment();
+      continue;
+    } else if (event != xml::XmlPullParser::Event::kStartElement) {
+      // Skip to the next element
       continue;
     }
 
     const Source item_source = source_.WithLine(parser->line_number());
-    const std::string& element_namespace = parser->element_namespace();
     const std::string& element_name = parser->element_name();
+    const std::string& element_namespace = parser->element_namespace();
+
     if (element_namespace.empty() && element_name == "item") {
-      Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
-      if (!maybe_name) {
-        diag_->Error(DiagMessage(item_source)
-                     << "<item> within an <overlayable> tag must have a 'name' attribute");
+      if (!ParseOverlayableItem(parser, policies, comment, out_resource)) {
         error = true;
-        continue;
       }
-
-      Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
-      if (!maybe_type) {
-        diag_->Error(DiagMessage(item_source)
-                     << "<item> within an <overlayable> tag must have a 'type' attribute");
+    } else if (element_namespace.empty() && element_name == "policy") {
+      if (!policies.empty()) {
+        // If the policy list is not empty, then we are currently inside a policy element
+        diag_->Error(DiagMessage(item_source) << "<policy> blocks cannot be recursively nested");
         error = true;
-        continue;
+        break;
+      } else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
+        // Parse the polices separated by vertical bar characters to allow for specifying multiple
+        // policies at once
+        for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) {
+          StringPiece trimmed_part = util::TrimWhitespace(part);
+          if (trimmed_part == "public") {
+            policies.push_back(Overlayable::Policy::kPublic);
+          } else if (trimmed_part == "product") {
+            policies.push_back(Overlayable::Policy::kProduct);
+          } else if (trimmed_part == "product_services") {
+            policies.push_back(Overlayable::Policy::kProductServices);
+          } else if (trimmed_part == "system") {
+            policies.push_back(Overlayable::Policy::kSystem);
+          } else if (trimmed_part == "vendor") {
+            policies.push_back(Overlayable::Policy::kVendor);
+          } else {
+            diag_->Error(DiagMessage(out_resource->source)
+                             << "<policy> has unsupported type '" << trimmed_part << "'");
+            error = true;
+            continue;
+          }
+        }
       }
-
-      const ResourceType* type = ParseResourceType(maybe_type.value());
-      if (type == nullptr) {
-        diag_->Error(DiagMessage(out_resource->source)
-                     << "invalid resource type '" << maybe_type.value()
-                     << "' in <item> within an <overlayable>");
-        error = true;
-        continue;
-      }
-
-      ParsedResource child_resource;
-      child_resource.name.type = *type;
-      child_resource.name.entry = maybe_name.value().to_string();
-      child_resource.source = item_source;
-      child_resource.overlayable = true;
-      if (options_.visibility) {
-        child_resource.visibility_level = options_.visibility.value();
-      }
-      out_resource->child_resources.push_back(std::move(child_resource));
-
-      xml::XmlPullParser::SkipCurrentElement(parser);
     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
-      diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
+      diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> in "
+                                            << " <overlayable>");
       error = true;
+      break;
     }
   }
+
   return !error;
 }
 
+bool ResourceParser::ParseOverlayableItem(xml::XmlPullParser* parser,
+                                          const std::vector<Overlayable::Policy>& policies,
+                                          const std::string& comment,
+                                          ParsedResource* out_resource) {
+  const Source item_source = source_.WithLine(parser->line_number());
+
+  Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+  if (!maybe_name) {
+    diag_->Error(DiagMessage(item_source)
+                     << "<item> within an <overlayable> tag must have a 'name' attribute");
+    return false;
+  }
+
+  Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+  if (!maybe_type) {
+    diag_->Error(DiagMessage(item_source)
+                     << "<item> within an <overlayable> tag must have a 'type' attribute");
+    return false;
+  }
+
+  const ResourceType* type = ParseResourceType(maybe_type.value());
+  if (type == nullptr) {
+    diag_->Error(DiagMessage(out_resource->source)
+                     << "invalid resource type '" << maybe_type.value()
+                     << "' in <item> within an <overlayable>");
+    return false;
+  }
+
+  ParsedResource child_resource;
+  child_resource.name.type = *type;
+  child_resource.name.entry = maybe_name.value().to_string();
+  child_resource.source = item_source;
+
+  if (policies.empty()) {
+    Overlayable overlayable;
+    overlayable.source = item_source;
+    overlayable.comment = comment;
+    child_resource.overlayable_declarations.push_back(overlayable);
+  } else {
+    for (Overlayable::Policy policy : policies) {
+      Overlayable overlayable;
+      overlayable.policy = policy;
+      overlayable.source = item_source;
+      overlayable.comment = comment;
+      child_resource.overlayable_declarations.push_back(overlayable);
+    }
+  }
+
+  if (options_.visibility) {
+    child_resource.visibility_level = options_.visibility.value();
+  }
+  out_resource->child_resources.push_back(std::move(child_resource));
+  return true;
+}
+
 bool ResourceParser::ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource) {
   if (ParseSymbolImpl(parser, out_resource)) {
     out_resource->visibility_level = Visibility::Level::kUndefined;
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 06bb0c9..ebacd6f 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -96,6 +96,10 @@
   bool ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource);
+  bool ParseOverlayableItem(xml::XmlPullParser* parser,
+                            const std::vector<Overlayable::Policy>& policies,
+                            const std::string& comment,
+                            ParsedResource* out_resource);
   bool ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseAttr(xml::XmlPullParser* parser, ParsedResource* out_resource);
   bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 0dff664..c6f29ac 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -891,56 +891,162 @@
   ASSERT_TRUE(TestParse(R"(<string name="foo">%1$s %n %2$s</string>)"));
 }
 
-TEST_F(ResourceParserTest, ParseOverlayableTagWithSystemPolicy) {
-  std::string input = R"(
-      <overlayable policy="illegal_policy">
-        <item type="string" name="foo" />
-      </overlayable>)";
-  EXPECT_FALSE(TestParse(input));
-
-  input = R"(
-      <overlayable policy="system">
-        <item name="foo" />
-      </overlayable>)";
-  EXPECT_FALSE(TestParse(input));
-
-  input = R"(
-      <overlayable policy="system">
-        <item type="attr" />
-      </overlayable>)";
-  EXPECT_FALSE(TestParse(input));
-
-  input = R"(
-      <overlayable policy="system">
-        <item type="bad_type" name="foo" />
-      </overlayable>)";
-  EXPECT_FALSE(TestParse(input));
-
-  input = R"(<overlayable policy="system" />)";
+TEST_F(ResourceParserTest, ParseOverlayable) {
+  std::string input = R"(<overlayable />)";
   EXPECT_TRUE(TestParse(input));
 
-  input = R"(<overlayable />)";
-  EXPECT_TRUE(TestParse(input));
-
-  input = R"(
-      <overlayable policy="system">
-        <item type="string" name="foo" />
-        <item type="dimen" name="foo" />
-      </overlayable>)";
-  ASSERT_TRUE(TestParse(input));
-
   input = R"(
       <overlayable>
-        <item type="string" name="bar" />
+        <item type="string" name="foo" />
+        <item type="drawable" name="bar" />
       </overlayable>)";
   ASSERT_TRUE(TestParse(input));
 
-  Maybe<ResourceTable::SearchResult> search_result =
-      table_.FindResource(test::ParseNameOrDie("string/bar"));
+  auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
   EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
-  EXPECT_TRUE(search_result.value().entry->overlayable);
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
+
+  search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
+}
+
+TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
+  std::string input = R"(<overlayable />)";
+  EXPECT_TRUE(TestParse(input));
+
+  input = R"(
+      <overlayable>
+        <item type="string" name="foo" />
+        <policy type="product">
+          <item type="string" name="bar" />
+        </policy>
+        <policy type="product_services">
+          <item type="string" name="baz" />
+        </policy>
+        <policy type="system">
+          <item type="string" name="fiz" />
+        </policy>
+        <policy type="vendor">
+          <item type="string" name="fuz" />
+        </policy>
+        <policy type="public">
+          <item type="string" name="faz" />
+        </policy>
+      </overlayable>)";
+  ASSERT_TRUE(TestParse(input));
+
+  auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kProduct));
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/baz"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kProductServices));
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kSystem));
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/fuz"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kVendor));
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/faz"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kPublic));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
+  std::string input = R"(
+      <overlayable>
+        <policy type="illegal_policy">
+          <item type="string" name="foo" />
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable>
+        <policy type="product">
+          <item name="foo" />
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable>
+        <policy type="vendor">
+          <item type="string" />
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) {
+  std::string input = R"(
+      <overlayable>
+        <policy type="vendor|product_services">
+          <item type="string" name="foo" />
+        </policy>
+        <policy type="product|system">
+          <item type="string" name="bar" />
+        </policy>
+      </overlayable>)";
+  ASSERT_TRUE(TestParse(input));
+
+  auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(2));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kVendor));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[1].policy,
+              Eq(Overlayable::Policy::kProductServices));
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(2));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kProduct));
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations[1].policy,
+              Eq(Overlayable::Policy::kSystem));
 }
 
 TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
@@ -950,6 +1056,85 @@
         <item type="string" name="foo" />
       </overlayable>)";
   EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable>
+        <item type="string" name="foo" />
+      </overlayable>
+      <overlayable>
+        <item type="string" name="foo" />
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable">
+        <policy type="product">
+          <item type="string" name="foo" />
+          <item type="string" name="foo" />
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable>
+        <policy type="product">
+          <item type="string" name="foo" />
+        </policy>
+      </overlayable>
+
+      <overlayable>
+        <policy type="product">
+          <item type="string" name="foo" />
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+}
+
+TEST_F(ResourceParserTest, PolicyAndNonPolicyOverlayableError) {
+  std::string input = R"(
+        <overlayable policy="product">
+          <item type="string" name="foo" />
+        </overlayable>
+        <overlayable policy="">
+          <item type="string" name="foo" />
+        </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+        <overlayable policy="">
+          <item type="string" name="foo" />
+        </overlayable>
+        <overlayable policy="product">
+          <item type="string" name="foo" />
+        </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+}
+
+TEST_F(ResourceParserTest, DuplicateOverlayableMultiplePolicyError) {
+  std::string input = R"(
+      <overlayable>
+        <policy type="vendor|product">
+          <item type="string" name="foo" />
+        </policy>
+      </overlayable>
+      <overlayable>
+        <policy type="product_services|vendor">
+          <item type="string" name="foo" />
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+}
+
+TEST_F(ResourceParserTest, NestPolicyInOverlayableError) {
+  std::string input = R"(
+      <overlayable>
+        <policy type="vendor|product">
+          <policy type="product_services">
+            <item type="string" name="foo" />
+          </policy>
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
 }
 
 TEST_F(ResourceParserTest, ParseIdItem) {
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 056a27b..bc8a4d1 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -625,17 +625,17 @@
   return true;
 }
 
-bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+bool ResourceTable::AddOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
                                    IDiagnostics* diag) {
-  return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
+  return AddOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
 }
 
-bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
+bool ResourceTable::AddOverlayableMangled(const ResourceNameRef& name,
                                           const Overlayable& overlayable, IDiagnostics* diag) {
-  return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
+  return AddOverlayableImpl(name, overlayable, SkipNameValidator, diag);
 }
 
-bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
+bool ResourceTable::AddOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
                                        NameValidator name_validator, IDiagnostics* diag) {
   CHECK(diag != nullptr);
 
@@ -646,13 +646,28 @@
   ResourceTablePackage* package = FindOrCreatePackage(name.package);
   ResourceTableType* type = package->FindOrCreateType(name.type);
   ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
-  if (entry->overlayable) {
-    diag->Error(DiagMessage(overlayable.source)
-                << "duplicate overlayable declaration for resource '" << name << "'");
-    diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
-    return false;
+
+  for (auto& overlayable_declaration : entry->overlayable_declarations) {
+    // An overlayable resource cannot be declared twice with the same policy
+    if (overlayable.policy == overlayable_declaration.policy) {
+      diag->Error(DiagMessage(overlayable.source)
+                    << "duplicate overlayable declaration for resource '" << name << "'");
+      diag->Error(DiagMessage(overlayable_declaration.source) << "previous declaration here");
+      return false;
+    }
+
+    // An overlayable resource cannot be declared once with a policy and without a policy because
+    // the policy becomes unused
+    if (!overlayable.policy || !overlayable_declaration.policy) {
+      diag->Error(DiagMessage(overlayable.source)
+                    << "overlayable resource '" << name << "'"
+                    << " declared once with a policy and once with no policy");
+      diag->Error(DiagMessage(overlayable_declaration.source) << "previous declaration here");
+      return false;
+    }
   }
-  entry->overlayable = overlayable;
+
+  entry->overlayable_declarations.push_back(overlayable);
   return true;
 }
 
@@ -688,7 +703,7 @@
         new_entry->id = entry->id;
         new_entry->visibility = entry->visibility;
         new_entry->allow_new = entry->allow_new;
-        new_entry->overlayable = entry->overlayable;
+        new_entry->overlayable_declarations = entry->overlayable_declarations;
 
         for (const auto& config_value : entry->values) {
           ResourceConfigValue* new_value =
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 1917d7e..3dd0a769 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -57,8 +57,27 @@
   std::string comment;
 };
 
-// The policy dictating whether an entry is overlayable at runtime by RROs.
+// Represents a declaration that a resource is overayable at runtime.
 struct Overlayable {
+  // Represents the types overlays that are allowed to overlay the resource.
+  enum class Policy {
+    // The resource can be overlaid by any overlay.
+    kPublic,
+
+    // The resource can be overlaid by any overlay on the system partition.
+    kSystem,
+
+    // The resource can be overlaid by any overlay on the vendor partition.
+    kVendor,
+
+    // The resource can be overlaid by any overlay on the product partition.
+    kProduct,
+
+    // The resource can be overlaid by any overlay on the product services partition.
+    kProductServices,
+  };
+
+  Maybe<Policy> policy;
   Source source;
   std::string comment;
 };
@@ -96,7 +115,8 @@
 
   Maybe<AllowNew> allow_new;
 
-  Maybe<Overlayable> overlayable;
+  // The declarations of this resource as overlayable for RROs
+  std::vector<Overlayable> overlayable_declarations;
 
   // The resource's values for each configuration.
   std::vector<std::unique_ptr<ResourceConfigValue>> values;
@@ -226,9 +246,9 @@
   bool SetVisibilityWithIdMangled(const ResourceNameRef& name, const Visibility& visibility,
                                   const ResourceId& res_id, IDiagnostics* diag);
 
-  bool SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
+  bool AddOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
                       IDiagnostics* diag);
-  bool SetOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable,
+  bool AddOverlayableMangled(const ResourceNameRef& name, const Overlayable& overlayable,
                              IDiagnostics* diag);
 
   bool SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new, IDiagnostics* diag);
@@ -303,7 +323,7 @@
   bool SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
                        NameValidator name_validator, IDiagnostics* diag);
 
-  bool SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
+  bool AddOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
                           NameValidator name_validator, IDiagnostics* diag);
 
   bool SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 05c6f15..7c28f07 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -242,21 +242,69 @@
   ASSERT_THAT(result.value().entry->allow_new.value().comment, StrEq("second"));
 }
 
-TEST(ResourceTableTest, SetOverlayable) {
+TEST(ResourceTableTest, AddOverlayable) {
   ResourceTable table;
   const ResourceName name = test::ParseNameOrDie("android:string/foo");
 
   Overlayable overlayable;
-
+  overlayable.policy = Overlayable::Policy::kProduct;
   overlayable.comment = "first";
-  ASSERT_TRUE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+  ASSERT_TRUE(table.AddOverlayable(name, overlayable, test::GetDiagnostics()));
   Maybe<ResourceTable::SearchResult> result = table.FindResource(name);
   ASSERT_TRUE(result);
-  ASSERT_TRUE(result.value().entry->overlayable);
-  ASSERT_THAT(result.value().entry->overlayable.value().comment, StrEq("first"));
+  ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(1));
+  ASSERT_THAT(result.value().entry->overlayable_declarations[0].comment, StrEq("first"));
+  ASSERT_THAT(result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kProduct));
 
-  overlayable.comment = "second";
-  ASSERT_FALSE(table.SetOverlayable(name, overlayable, test::GetDiagnostics()));
+  Overlayable overlayable2;
+  overlayable2.comment = "second";
+  overlayable2.policy = Overlayable::Policy::kProductServices;
+  ASSERT_TRUE(table.AddOverlayable(name, overlayable2, test::GetDiagnostics()));
+  result = table.FindResource(name);
+  ASSERT_TRUE(result);
+  ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2));
+  ASSERT_THAT(result.value().entry->overlayable_declarations[0].comment, StrEq("first"));
+  ASSERT_THAT(result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kProduct));
+  ASSERT_THAT(result.value().entry->overlayable_declarations[1].comment, StrEq("second"));
+  ASSERT_THAT(result.value().entry->overlayable_declarations[1].policy,
+              Eq(Overlayable::Policy::kProductServices));
+}
+
+TEST(ResourceTableTest, AddDuplicateOverlayableFail) {
+  ResourceTable table;
+  const ResourceName name = test::ParseNameOrDie("android:string/foo");
+
+  Overlayable overlayable;
+  overlayable.policy = Overlayable::Policy::kProduct;
+  ASSERT_TRUE(table.AddOverlayable(name, overlayable, test::GetDiagnostics()));
+
+  Overlayable overlayable2;
+  overlayable2.policy = Overlayable::Policy::kProduct;
+  ASSERT_FALSE(table.AddOverlayable(name, overlayable2, test::GetDiagnostics()));
+}
+
+TEST(ResourceTableTest, AddOverlayablePolicyAndNoneFirstFail) {
+  ResourceTable table;
+  const ResourceName name = test::ParseNameOrDie("android:string/foo");
+
+  ASSERT_TRUE(table.AddOverlayable(name, {}, test::GetDiagnostics()));
+
+  Overlayable overlayable2;
+  overlayable2.policy = Overlayable::Policy::kProduct;
+  ASSERT_FALSE(table.AddOverlayable(name, overlayable2, test::GetDiagnostics()));
+}
+
+TEST(ResourceTableTest, AddOverlayablePolicyAndNoneLastFail) {
+  ResourceTable table;
+  const ResourceName name = test::ParseNameOrDie("android:string/foo");
+
+  Overlayable overlayable;
+  overlayable.policy = Overlayable::Policy::kProduct;
+  ASSERT_TRUE(table.AddOverlayable(name, overlayable, test::GetDiagnostics()));
+
+  ASSERT_FALSE(table.AddOverlayable(name, {}, test::GetDiagnostics()));
 }
 
 TEST(ResourceTableTest, AllowDuplictaeResourcesNames) {
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index d7a3771..bf9fe49 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -133,13 +133,25 @@
   string comment = 2;
 }
 
-// Whether a resource is overlayable by runtime resource overlays (RRO).
+// Represents a declaration that a resource is overayable at runtime.
 message Overlayable {
+  enum Policy {
+    NONE = 0;
+    PUBLIC = 1;
+    SYSTEM = 2;
+    VENDOR = 3;
+    PRODUCT = 4;
+    PRODUCT_SERVICES = 5;
+  }
+
   // Where this declaration was defined in source.
   Source source = 1;
 
   // Any comment associated with the declaration.
   string comment = 2;
+
+  // The policy of the overlayable declaration
+  Policy policy = 3;
 }
 
 // An entry ID in the range [0x0000, 0xffff].
@@ -169,7 +181,7 @@
   AllowNew allow_new = 4;
 
   // Whether this resource can be overlaid by a runtime resource overlay (RRO).
-  Overlayable overlayable = 5;
+  repeated Overlayable overlayable = 5;
 
   // The set of values defined for this entry, each corresponding to a different
   // configuration/variant.
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 3a39a6b..ed70fb3 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -398,7 +398,7 @@
       if (type_spec_flags & ResTable_typeSpec::SPEC_OVERLAYABLE) {
         Overlayable overlayable;
         overlayable.source = source_.WithLine(0);
-        if (!table_->SetOverlayableMangled(name, overlayable, diag_)) {
+        if (!table_->AddOverlayableMangled(name, overlayable, diag_)) {
           return false;
         }
       }
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 8641a7c..8a86f63a 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -446,7 +446,7 @@
         config_masks[entry->id.value()] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
       }
 
-      if (entry->overlayable) {
+      if (!entry->overlayable_declarations.empty()) {
         config_masks[entry->id.value()] |=
             util::HostToDevice32(ResTable_typeSpec::SPEC_OVERLAYABLE);
       }
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index af19b98..cd1414c 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -634,7 +634,7 @@
           .AddSimple("com.app.test:integer/overlayable", ResourceId(0x7f020000))
           .Build();
 
-  ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.test:integer/overlayable"),
+  ASSERT_TRUE(table->AddOverlayable(test::ParseNameOrDie("com.app.test:integer/overlayable"),
                                     Overlayable{}, test::GetDiagnostics()));
 
   ResTable res_table;
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index d1b2fdb..f612914 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -437,15 +437,37 @@
         entry->allow_new = std::move(allow_new);
       }
 
-      if (pb_entry.has_overlayable()) {
-        const pb::Overlayable& pb_overlayable = pb_entry.overlayable();
-
+      for (const pb::Overlayable& pb_overlayable : pb_entry.overlayable()) {
         Overlayable overlayable;
+        switch (pb_overlayable.policy()) {
+          case pb::Overlayable::NONE:
+            overlayable.policy = {};
+            break;
+          case pb::Overlayable::PUBLIC:
+            overlayable.policy = Overlayable::Policy::kPublic;
+            break;
+          case pb::Overlayable::PRODUCT:
+            overlayable.policy = Overlayable::Policy::kProduct;
+            break;
+          case pb::Overlayable::PRODUCT_SERVICES:
+            overlayable.policy = Overlayable::Policy::kProductServices;
+            break;
+          case pb::Overlayable::SYSTEM:
+            overlayable.policy = Overlayable::Policy::kSystem;
+            break;
+          case pb::Overlayable::VENDOR:
+            overlayable.policy = Overlayable::Policy::kVendor;
+            break;
+          default:
+            *out_error = "unknown overlayable policy";
+            return false;
+        }
+
         if (pb_overlayable.has_source()) {
           DeserializeSourceFromPb(pb_overlayable.source(), src_pool, &overlayable.source);
         }
         overlayable.comment = pb_overlayable.comment();
-        entry->overlayable = std::move(overlayable);
+        entry->overlayable_declarations.push_back(overlayable);
       }
 
       ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(),
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 7e35ea7..f1e96d6 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -310,11 +310,31 @@
           pb_allow_new->set_comment(entry->allow_new.value().comment);
         }
 
-        if (entry->overlayable) {
-          pb::Overlayable* pb_overlayable = pb_entry->mutable_overlayable();
-          SerializeSourceToPb(entry->overlayable.value().source, &source_pool,
+        for (const Overlayable& overlayable : entry->overlayable_declarations) {
+          pb::Overlayable* pb_overlayable = pb_entry->add_overlayable();
+          if (overlayable.policy) {
+            switch (overlayable.policy.value()) {
+              case Overlayable::Policy::kPublic:
+                pb_overlayable->set_policy(pb::Overlayable::PUBLIC);
+                break;
+              case Overlayable::Policy::kProduct:
+                pb_overlayable->set_policy(pb::Overlayable::PRODUCT);
+                break;
+              case Overlayable::Policy::kProductServices:
+                pb_overlayable->set_policy(pb::Overlayable::PRODUCT_SERVICES);
+                break;
+              case Overlayable::Policy::kSystem:
+                pb_overlayable->set_policy(pb::Overlayable::SYSTEM);
+                break;
+              case Overlayable::Policy::kVendor:
+                pb_overlayable->set_policy(pb::Overlayable::VENDOR);
+                break;
+            }
+          }
+
+          SerializeSourceToPb(overlayable.source, &source_pool,
                               pb_overlayable->mutable_source());
-          pb_overlayable->set_comment(entry->overlayable.value().comment);
+          pb_overlayable->set_comment(overlayable.comment);
         }
 
         for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 3c4d41a..95dbbeb 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -93,7 +93,7 @@
       util::make_unique<Reference>(expected_ref), context->GetDiagnostics()));
 
   // Make an overlayable resource.
-  ASSERT_TRUE(table->SetOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"),
+  ASSERT_TRUE(table->AddOverlayable(test::ParseNameOrDie("com.app.a:integer/overlayable"),
                                     Overlayable{}, test::GetDiagnostics()));
 
   pb::ResourceTable pb_table;
@@ -106,7 +106,7 @@
 
   ResourceTable new_table;
   std::string error;
-  ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
+  ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error)) << error;
   EXPECT_THAT(error, IsEmpty());
 
   Id* new_id = test::GetValue<Id>(&new_table, "com.app.a:id/foo");
@@ -160,7 +160,8 @@
       new_table.FindResource(test::ParseNameOrDie("com.app.a:integer/overlayable"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
-  EXPECT_TRUE(search_result.value().entry->overlayable);
+  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
 }
 
 TEST(ProtoSerializeTest, SerializeAndDeserializeXml) {
@@ -464,4 +465,59 @@
       "night-xhdpi-stylus-keysexposed-qwerty-navhidden-dpad-300x200-v23");
 }
 
+TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddOverlayable("com.app.a:bool/foo", Overlayable::Policy::kSystem)
+          .AddOverlayable("com.app.a:bool/foo", Overlayable::Policy::kProduct)
+          .AddOverlayable("com.app.a:bool/bar", Overlayable::Policy::kProductServices)
+          .AddOverlayable("com.app.a:bool/bar", Overlayable::Policy::kVendor)
+          .AddOverlayable("com.app.a:bool/baz", Overlayable::Policy::kPublic)
+          .AddOverlayable("com.app.a:bool/biz", {})
+          .AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
+          .Build();
+
+  pb::ResourceTable pb_table;
+  SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
+
+  MockFileCollection files;
+  ResourceTable new_table;
+  std::string error;
+  ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
+  EXPECT_THAT(error, IsEmpty());
+
+  Maybe<ResourceTable::SearchResult> result =
+      new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
+  ASSERT_TRUE(result);
+  ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2));
+  EXPECT_THAT(result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kSystem));
+  EXPECT_THAT(result.value().entry->overlayable_declarations[1].policy,
+              Eq(Overlayable::Policy::kProduct));
+
+  result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
+  ASSERT_TRUE(result);
+  ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2));
+  EXPECT_THAT(result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kProductServices));
+  EXPECT_THAT(result.value().entry->overlayable_declarations[1].policy,
+              Eq(Overlayable::Policy::kVendor));
+
+  result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
+  ASSERT_TRUE(result);
+  ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_THAT(result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kPublic));
+
+  result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
+  ASSERT_TRUE(result);
+  ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(1));
+  EXPECT_FALSE(result.value().entry->overlayable_declarations[0].policy);
+
+  result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
+  ASSERT_TRUE(result);
+  EXPECT_THAT(result.value().entry->overlayable_declarations.size(), Eq(0));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index afb8ae0..d777e22 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -101,7 +101,7 @@
   return true;
 }
 
-static bool MergeEntry(IAaptContext* context, const Source& src, bool overlay,
+static bool MergeEntry(IAaptContext* context, const Source& src,
                        ResourceEntry* dst_entry, ResourceEntry* src_entry,
                        bool strict_visibility) {
   if (strict_visibility
@@ -134,17 +134,35 @@
     dst_entry->allow_new = std::move(src_entry->allow_new);
   }
 
-  if (src_entry->overlayable) {
-    if (dst_entry->overlayable && !overlay) {
-      context->GetDiagnostics()->Error(DiagMessage(src_entry->overlayable.value().source)
-                                       << "duplicate overlayable declaration for resource '"
-                                       << src_entry->name << "'");
-      context->GetDiagnostics()->Error(DiagMessage(dst_entry->overlayable.value().source)
-                                       << "previous declaration here");
-      return false;
+  for (auto& src_overlayable : src_entry->overlayable_declarations) {
+    for (auto& dst_overlayable : dst_entry->overlayable_declarations) {
+      // An overlayable resource cannot be declared twice with the same policy
+      if (src_overlayable.policy == dst_overlayable.policy) {
+        context->GetDiagnostics()->Error(DiagMessage(src_overlayable.source)
+                                             << "duplicate overlayable declaration for resource '"
+                                             << src_entry->name << "'");
+        context->GetDiagnostics()->Error(DiagMessage(dst_overlayable.source)
+                                             << "previous declaration here");
+        return false;
+      }
+
+      // An overlayable resource cannot be declared once with a policy and without a policy because
+      // the policy becomes unused
+      if (!src_overlayable.policy || !dst_overlayable.policy) {
+        context->GetDiagnostics()->Error(DiagMessage(src_overlayable.source)
+                                             << "overlayable resource '" << src_entry->name
+                                             << "' declared once with a policy and once with no "
+                                             << "policy");
+        context->GetDiagnostics()->Error(DiagMessage(dst_overlayable.source)
+                                             << "previous declaration here");
+        return false;
+      }
     }
-    dst_entry->overlayable = std::move(src_entry->overlayable);
   }
+
+  dst_entry->overlayable_declarations.insert(dst_entry->overlayable_declarations.end(),
+                                             src_entry->overlayable_declarations.begin(),
+                                             src_entry->overlayable_declarations.end());
   return true;
 }
 
@@ -244,7 +262,7 @@
         continue;
       }
 
-      if (!MergeEntry(context_, src, overlay, dst_entry, src_entry.get(), options_.strict_visibility)) {
+      if (!MergeEntry(context_, src, dst_entry, src_entry.get(), options_.strict_visibility)) {
         error = true;
         continue;
       }
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 79a734b..d6579d3 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -436,4 +436,97 @@
               Eq(make_value(Reference(test::ParseNameOrDie("com.app.a:style/OverlayParent")))));
 }
 
+TEST_F(TableMergerTest, AddOverlayable) {
+  std::unique_ptr<ResourceTable> table_a =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.a", 0x7f)
+          .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
+          .Build();
+
+  std::unique_ptr<ResourceTable> table_b =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.a", 0x7f)
+          .AddOverlayable("bool/foo", Overlayable::Policy::kProductServices)
+          .Build();
+
+  ResourceTable final_table;
+  TableMergerOptions options;
+  options.auto_add_overlay = true;
+  TableMerger merger(context_.get(), &final_table, options);
+  ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
+  ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
+
+  const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
+  Maybe<ResourceTable::SearchResult> result = final_table.FindResource(name);
+  ASSERT_TRUE(result);
+  ASSERT_THAT(result.value().entry->overlayable_declarations.size(), Eq(2));
+  ASSERT_THAT(result.value().entry->overlayable_declarations[0].policy,
+              Eq(Overlayable::Policy::kProduct));
+  ASSERT_THAT(result.value().entry->overlayable_declarations[1].policy,
+              Eq(Overlayable::Policy::kProductServices));
+}
+
+TEST_F(TableMergerTest, AddDuplicateOverlayableFail) {
+  std::unique_ptr<ResourceTable> table_a =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.a", 0x7f)
+          .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
+          .Build();
+
+  std::unique_ptr<ResourceTable> table_b =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.a", 0x7f)
+          .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
+          .Build();
+
+  ResourceTable final_table;
+  TableMergerOptions options;
+  options.auto_add_overlay = true;
+  TableMerger merger(context_.get(), &final_table, options);
+  ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
+  ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
+}
+
+TEST_F(TableMergerTest, AddOverlayablePolicyAndNoneFirstFail) {
+  std::unique_ptr<ResourceTable> table_a =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.a", 0x7f)
+          .AddOverlayable("bool/foo", {})
+          .Build();
+
+  std::unique_ptr<ResourceTable> table_b =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.a", 0x7f)
+          .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
+          .Build();
+
+  ResourceTable final_table;
+  TableMergerOptions options;
+  options.auto_add_overlay = true;
+  TableMerger merger(context_.get(), &final_table, options);
+  ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
+  ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
+}
+
+TEST_F(TableMergerTest, AddOverlayablePolicyAndNoneLastFail) {
+  std::unique_ptr<ResourceTable> table_a =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.a", 0x7f)
+          .AddOverlayable("bool/foo", Overlayable::Policy::kProduct)
+          .Build();
+
+  std::unique_ptr<ResourceTable> table_b =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.a", 0x7f)
+          .AddOverlayable("bool/foo", {})
+          .Build();
+
+  ResourceTable final_table;
+  TableMergerOptions options;
+  options.auto_add_overlay = true;
+  TableMerger merger(context_.get(), &final_table, options);
+  ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
+  ASSERT_FALSE(merger.Merge({}, table_b.get(), false /*overlay*/));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index f33ae31..03b59e0 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -135,6 +135,15 @@
   return *this;
 }
 
+ResourceTableBuilder& ResourceTableBuilder::AddOverlayable(const StringPiece& name,
+                                                           const Maybe<Overlayable::Policy> p) {
+  ResourceName res_name = ParseNameOrDie(name);
+  Overlayable overlayable;
+  overlayable.policy = p;
+  CHECK(table_->AddOverlayable(res_name, overlayable, GetDiagnostics()));
+  return *this;
+}
+
 StringPool* ResourceTableBuilder::string_pool() {
   return &table_->string_pool;
 }
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 9159599..d68c24d 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -73,6 +73,8 @@
                                  const ResourceId& id, std::unique_ptr<Value> value);
   ResourceTableBuilder& SetSymbolState(const android::StringPiece& name, const ResourceId& id,
                                        Visibility::Level level, bool allow_new = false);
+  ResourceTableBuilder& AddOverlayable(const android::StringPiece& name,
+                                       Maybe<Overlayable::Policy> policy);
 
   StringPool* string_pool();
   std::unique_ptr<ResourceTable> Build();
diff --git a/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl b/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl
new file mode 100644
index 0000000..f472a02
--- /dev/null
+++ b/wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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.net.wifi;
+
+import android.net.wifi.INetworkRequestUserSelectionCallback;
+import android.net.wifi.WifiConfiguration;
+
+/**
+ * Interface for network request match callback.
+ *
+ * @hide
+ */
+oneway interface INetworkRequestMatchCallback
+{
+   void onUserSelectionCallbackRegistration(in INetworkRequestUserSelectionCallback userSelectionCallback);
+
+   void onMatch(in List<WifiConfiguration> wificonfigurations);
+
+   void onUserSelectionConnectSuccess(in WifiConfiguration wificonfiguration);
+
+   void onUserSelectionConnectFailure(in WifiConfiguration wificonfiguration);
+}
diff --git a/wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl b/wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl
new file mode 100644
index 0000000..524cefb
--- /dev/null
+++ b/wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.net.wifi;
+
+import android.net.wifi.WifiConfiguration;
+
+/**
+ * Interface for providing user selection in response to
+ * network request match callback.
+ * @hide
+ */
+oneway interface INetworkRequestUserSelectionCallback
+{
+   void select(in WifiConfiguration wificonfiguration);
+
+   void reject();
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 12f50c8..1fd68ec 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -25,6 +25,7 @@
 
 import android.net.DhcpInfo;
 import android.net.Network;
+import android.net.wifi.INetworkRequestMatchCallback;
 import android.net.wifi.ISoftApCallback;
 import android.net.wifi.ITrafficStateCallback;
 import android.net.wifi.PasspointManagementObjectDefinition;
@@ -185,5 +186,9 @@
     void registerTrafficStateCallback(in IBinder binder, in ITrafficStateCallback callback, int callbackIdentifier);
 
     void unregisterTrafficStateCallback(int callbackIdentifier);
+
+    void registerNetworkRequestMatchCallback(in IBinder binder, in INetworkRequestMatchCallback callback, int callbackIdentifier);
+
+    void unregisterNetworkRequestMatchCallback(int callbackIdentifier);
 }
 
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 0330614..0574716 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -47,7 +47,11 @@
 /**
  * A class representing a configured Wi-Fi network, including the
  * security configuration.
+ *
+ * @deprecated Use {@link WifiNetworkConfigBuilder} to create {@link NetworkSpecifier} and
+ * {@link WifiNetworkSuggestion}. This will become a system use only object in the future.
  */
+@Deprecated
 public class WifiConfiguration implements Parcelable {
     private static final String TAG = "WifiConfiguration";
     /**
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 9adbe67..9ce5486 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -25,19 +25,17 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.UnsupportedAppUsage;
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
 import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
 import android.net.wifi.hotspot2.IProvisioningCallback;
 import android.net.wifi.hotspot2.OsuProvider;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.hotspot2.ProvisioningCallback;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -52,7 +50,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
-import com.android.server.net.NetworkPinner;
 
 import dalvik.system.CloseGuard;
 
@@ -1035,7 +1032,17 @@
      * </ul>
      * @return a list of network configurations in the form of a list
      * of {@link WifiConfiguration} objects.
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return an empty list.
      */
+    @Deprecated
     public List<WifiConfiguration> getConfiguredNetworks() {
         try {
             ParceledListSlice<WifiConfiguration> parceledList =
@@ -1135,7 +1142,17 @@
      * @return the ID of the newly created network description. This is used in
      *         other operations to specified the network to be acted upon.
      *         Returns {@code -1} on failure.
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return {@code -1}.
      */
+    @Deprecated
     public int addNetwork(WifiConfiguration config) {
         if (config == null) {
             return -1;
@@ -1160,7 +1177,17 @@
      *         Returns {@code -1} on failure, including when the {@code networkId}
      *         field of the {@code WifiConfiguration} does not refer to an
      *         existing network.
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return {@code -1}.
      */
+    @Deprecated
     public int updateNetwork(WifiConfiguration config) {
         if (config == null || config.networkId < 0) {
             return -1;
@@ -1185,6 +1212,302 @@
     }
 
     /**
+     * Interface for indicating user selection from the list of networks presented in the
+     * {@link NetworkRequestMatchCallback#onMatch(List)}.
+     *
+     * The platform will implement this callback and pass it along with the
+     * {@link NetworkRequestMatchCallback#onUserSelectionCallbackRegistration(
+     * NetworkRequestUserSelectionCallback)}. The UI component handling
+     * {@link NetworkRequestMatchCallback} will invoke {@link #select(WifiConfiguration)} or
+     * {@link #reject()} to return the user's selection back to the platform via this callback.
+     * @hide
+     */
+    @SystemApi
+    public interface NetworkRequestUserSelectionCallback {
+        /**
+         * User selected this network to connect to.
+         * @param wifiConfiguration WifiConfiguration object corresponding to the network
+         *                          user selected.
+         */
+        void select(@NonNull WifiConfiguration wifiConfiguration);
+
+        /**
+         * User rejected the app's request.
+         */
+        void reject();
+    }
+
+    /**
+     * Interface for network request callback. Should be implemented by applications and passed when
+     * calling {@link #registerNetworkRequestMatchCallback(NetworkRequestMatchCallback, Handler)}.
+     *
+     * This is meant to be implemented by a UI component to present the user with a list of networks
+     * matching the app's request. The user is allowed to pick one of these networks to connect to
+     * or reject the request by the app.
+     * @hide
+     */
+    @SystemApi
+    public interface NetworkRequestMatchCallback {
+        /**
+         * Invoked to register a callback to be invoked to convey user selection. The callback
+         * object paased in this method is to be invoked by the UI component after the service sends
+         * a list of matching scan networks using {@link #onMatch(List)} and user picks a network
+         * from that list.
+         *
+         * @param userSelectionCallback Callback object to send back the user selection.
+         */
+        void onUserSelectionCallbackRegistration(
+                @NonNull NetworkRequestUserSelectionCallback userSelectionCallback);
+
+        /**
+         * Invoked when a network request initiated by an app matches some networks in scan results.
+         * This may be invoked multiple times for a single network request as the platform finds new
+         * networks in scan results.
+         *
+         * @param wifiConfigurations List of {@link WifiConfiguration} objects corresponding to the
+         *                           networks matching the request.
+         */
+        void onMatch(@NonNull List<WifiConfiguration> wifiConfigurations);
+
+        /**
+         * Invoked on a successful connection with the network that the user selected
+         * via {@link NetworkRequestUserSelectionCallback}.
+         *
+         * @param wifiConfiguration WifiConfiguration object corresponding to the network that the
+         *                          user selected.
+         */
+        void onUserSelectionConnectSuccess(@NonNull WifiConfiguration wifiConfiguration);
+
+        /**
+         * Invoked on failure to establish connection with the network that the user selected
+         * via {@link NetworkRequestUserSelectionCallback}.
+         *
+         * @param wifiConfiguration WifiConfiguration object corresponding to the network
+         *                          user selected.
+         */
+        void onUserSelectionConnectFailure(@NonNull WifiConfiguration wifiConfiguration);
+    }
+
+    /**
+     * Callback proxy for NetworkRequestUserSelectionCallback objects.
+     * @hide
+     */
+    private class NetworkRequestUserSelectionCallbackProxy implements
+            NetworkRequestUserSelectionCallback {
+        private final INetworkRequestUserSelectionCallback mCallback;
+
+        NetworkRequestUserSelectionCallbackProxy(
+                INetworkRequestUserSelectionCallback callback) {
+            mCallback = callback;
+        }
+
+        @Override
+        public void select(@NonNull WifiConfiguration wifiConfiguration) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "NetworkRequestUserSelectionCallbackProxy: select "
+                        + "wificonfiguration: " + wifiConfiguration);
+            }
+            try {
+                mCallback.select(wifiConfiguration);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to invoke onSelected", e);
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        @Override
+        public void reject() {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "NetworkRequestUserSelectionCallbackProxy: reject");
+            }
+            try {
+                mCallback.reject();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to invoke onRejected", e);
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Callback proxy for NetworkRequestMatchCallback objects.
+     * @hide
+     */
+    private class NetworkRequestMatchCallbackProxy extends INetworkRequestMatchCallback.Stub {
+        private final Handler mHandler;
+        private final NetworkRequestMatchCallback mCallback;
+
+        NetworkRequestMatchCallbackProxy(Looper looper, NetworkRequestMatchCallback callback) {
+            mHandler = new Handler(looper);
+            mCallback = callback;
+        }
+
+        @Override
+        public void onUserSelectionCallbackRegistration(
+                INetworkRequestUserSelectionCallback userSelectionCallback) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "NetworkRequestMatchCallbackProxy: "
+                        + "onUserSelectionCallbackRegistration callback: " + userSelectionCallback);
+            }
+            mHandler.post(() -> {
+                mCallback.onUserSelectionCallbackRegistration(
+                        new NetworkRequestUserSelectionCallbackProxy(userSelectionCallback));
+            });
+        }
+
+        @Override
+        public void onMatch(List<WifiConfiguration> wifiConfigurations) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "NetworkRequestMatchCallbackProxy: onMatch wificonfigurations: "
+                        + wifiConfigurations);
+            }
+            mHandler.post(() -> {
+                mCallback.onMatch(wifiConfigurations);
+            });
+        }
+
+        @Override
+        public void onUserSelectionConnectSuccess(WifiConfiguration wifiConfiguration) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "NetworkRequestMatchCallbackProxy: onUserSelectionConnectSuccess "
+                        + " wificonfiguration: " + wifiConfiguration);
+            }
+            mHandler.post(() -> {
+                mCallback.onUserSelectionConnectSuccess(wifiConfiguration);
+            });
+        }
+
+        @Override
+        public void onUserSelectionConnectFailure(WifiConfiguration wifiConfiguration) {
+            if (mVerboseLoggingEnabled) {
+                Log.v(TAG, "NetworkRequestMatchCallbackProxy: onUserSelectionConnectFailure"
+                        + " wificonfiguration: " + wifiConfiguration);
+            }
+            mHandler.post(() -> {
+                mCallback.onUserSelectionConnectFailure(wifiConfiguration);
+            });
+        }
+    }
+
+    /**
+     * Registers a callback for NetworkRequest matches. See {@link NetworkRequestMatchCallback}.
+     * Caller can unregister a previously registered callback using
+     * {@link #unregisterNetworkRequestMatchCallback(NetworkRequestMatchCallback)}
+     * <p>
+     * Applications should have the
+     * {@link android.Manifest.permission#NETWORK_SETTINGS} permission. Callers
+     * without the permission will trigger a {@link java.lang.SecurityException}.
+     * <p>
+     *
+     * @param callback Callback for network match events
+     * @param handler  The Handler on whose thread to execute the callbacks of the {@code callback}
+     *                 object. If null, then the application's main thread will be used.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void registerNetworkRequestMatchCallback(@NonNull NetworkRequestMatchCallback callback,
+                                                    @Nullable Handler handler) {
+        if (callback == null) throw new IllegalArgumentException("callback cannot be null");
+        Log.v(TAG, "registerNetworkRequestMatchCallback: callback=" + callback
+                + ", handler=" + handler);
+
+        Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
+        Binder binder = new Binder();
+        try {
+            mService.registerNetworkRequestMatchCallback(
+                    binder, new NetworkRequestMatchCallbackProxy(looper, callback),
+                    callback.hashCode());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregisters a callback for NetworkRequest matches. See {@link NetworkRequestMatchCallback}.
+     * <p>
+     * Applications should have the
+     * {@link android.Manifest.permission#NETWORK_SETTINGS} permission. Callers
+     * without the permission will trigger a {@link java.lang.SecurityException}.
+     * <p>
+     *
+     * @param callback Callback for network match events
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void unregisterNetworkRequestMatchCallback(
+            @NonNull NetworkRequestMatchCallback callback) {
+        if (callback == null) throw new IllegalArgumentException("callback cannot be null");
+        Log.v(TAG, "unregisterNetworkRequestMatchCallback: callback=" + callback);
+
+        try {
+            mService.unregisterNetworkRequestMatchCallback(callback.hashCode());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Provide a list of network suggestions to the device. See {@link WifiNetworkSuggestion}
+     * for a detailed explanation of the parameters.
+     *<p>
+     * When the device decides to connect to one of the provided network suggestions, platform fires
+     * the associated {@code pendingIntent} if
+     * {@link WifiNetworkSuggestion#isAppInteractionRequired} is {@code true} and the
+     * provided {@code pendingIntent} is non-null.
+     *<p>
+     * Registration of a non-null pending intent {@code pendingIntent} requires
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
+     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission.
+     *<p>
+     * NOTE:
+     * <li> These networks are just a suggestion to the platform. The platform will ultimately
+     * decide on which network the device connects to. </li>
+     * <li> When an app is uninstalled, all its suggested networks are discarded. If the device is
+     * currently connected to a suggested network which is being removed then the device will
+     * disconnect from that network.</li>
+     * <li> No in-place modification of existing suggestions are allowed. Apps are expected to
+     * remove suggestions using {@link #removeNetworkSuggestions(List)} and then add the modified
+     * suggestion back using this API.</li>
+     *
+     * @param networkSuggestions List of network suggestions provided by the app.
+     * @param pendingIntent Pending intent to be fired post connection for networks. These will be
+     *                      fired only when connecting to a network which has the
+     *                      {@link WifiNetworkSuggestion#isAppInteractionRequired} flag set.
+     *                      Pending intent must hold a foreground service, else will be rejected.
+     *                      (i.e {@link PendingIntent#isForegroundService()} should return true)
+     * @return true on success, false if any of the suggestions match (See
+     * {@link WifiNetworkSuggestion#equals(Object)} any previously provided suggestions by the app.
+     * @throws {@link SecurityException} if the caller is missing required permissions.
+     */
+    public boolean addNetworkSuggestions(
+            @NonNull List<WifiNetworkSuggestion> networkSuggestions,
+            @Nullable PendingIntent pendingIntent) {
+        // TODO(b/115504887): Implementation
+        return false;
+    }
+
+
+    /**
+     * Remove a subset of or all of networks from previously provided suggestions by the app to the
+     * device.
+     * See {@link WifiNetworkSuggestion} for a detailed explanation of the parameters.
+     * See {@link WifiNetworkSuggestion#equals(Object)} for the equivalence evaluation used.
+     *
+     * @param networkSuggestions List of network suggestions to be removed. Pass an empty list
+     *                           to remove all the previous suggestions provided by the app.
+     * @return true on success, false if any of the suggestions do not match any suggestions
+     * previously provided by the app. Any matching suggestions are removed from the device and
+     * will not be considered for any further connection attempts.
+     */
+    public boolean removeNetworkSuggestions(
+            @NonNull List<WifiNetworkSuggestion> networkSuggestions) {
+        // TODO(b/115504887): Implementation
+        return false;
+    }
+
+    /**
      * Add or update a Passpoint configuration.  The configuration provides a credential
      * for connecting to Passpoint networks that are operated by the Passpoint
      * service provider specified in the configuration.
@@ -1299,7 +1622,17 @@
      * @param netId the ID of the network as returned by {@link #addNetwork} or {@link
      *        #getConfiguredNetworks}.
      * @return {@code true} if the operation succeeded
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false.
      */
+    @Deprecated
     public boolean removeNetwork(int netId) {
         try {
             return mService.removeNetwork(netId, mContext.getOpPackageName());
@@ -1314,10 +1647,8 @@
      * network is initiated. This may result in the asynchronous delivery
      * of state change events.
      * <p>
-     * <b>Note:</b> If an application's target SDK version is
-     * {@link android.os.Build.VERSION_CODES#LOLLIPOP} or newer, network
-     * communication may not use Wi-Fi even if Wi-Fi is connected; traffic may
-     * instead be sent through another network, such as cellular data,
+     * <b>Note:</b> Network communication may not use Wi-Fi even if Wi-Fi is connected;
+     * traffic may instead be sent through another network, such as cellular data,
      * Bluetooth tethering, or Ethernet. For example, traffic will never use a
      * Wi-Fi network that does not provide Internet access (e.g. a wireless
      * printer), if another network that does offer Internet access (e.g.
@@ -1335,29 +1666,24 @@
      * @param attemptConnect The way to select a particular network to connect to is specify
      *        {@code true} for this parameter.
      * @return {@code true} if the operation succeeded
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false.
      */
+    @Deprecated
     public boolean enableNetwork(int netId, boolean attemptConnect) {
-        final boolean pin = attemptConnect && mTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP;
-        if (pin) {
-            NetworkRequest request = new NetworkRequest.Builder()
-                    .clearCapabilities()
-                    .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
-                    .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
-                    .build();
-            NetworkPinner.pin(mContext, request);
-        }
-
         boolean success;
         try {
             success = mService.enableNetwork(netId, attemptConnect, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-
-        if (pin && !success) {
-            NetworkPinner.unpin();
-        }
-
         return success;
     }
 
@@ -1372,7 +1698,17 @@
      * @param netId the ID of the network as returned by {@link #addNetwork} or {@link
      *        #getConfiguredNetworks}.
      * @return {@code true} if the operation succeeded
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false.
      */
+    @Deprecated
     public boolean disableNetwork(int netId) {
         try {
             return mService.disableNetwork(netId, mContext.getOpPackageName());
@@ -1385,7 +1721,17 @@
      * Disassociate from the currently active access point. This may result
      * in the asynchronous delivery of state change events.
      * @return {@code true} if the operation succeeded
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false.
      */
+    @Deprecated
     public boolean disconnect() {
         try {
             mService.disconnect(mContext.getOpPackageName());
@@ -1400,7 +1746,17 @@
      * disconnected. This may result in the asynchronous delivery of state
      * change events.
      * @return {@code true} if the operation succeeded
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false.
      */
+    @Deprecated
     public boolean reconnect() {
         try {
             mService.reconnect(mContext.getOpPackageName());
@@ -1415,7 +1771,17 @@
      * connected. This may result in the asynchronous delivery of state
      * change events.
      * @return {@code true} if the operation succeeded
+     *
+     * @deprecated
+     * a) See {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for new
+     * mechanism to trigger connection to a Wi-Fi network.
+     * b) See {@link #addNetworkSuggestions(List, PendingIntent)},
+     * {@link #removeNetworkSuggestions(List)} for new API to add Wi-Fi networks for consideration
+     * when auto-connecting to wifi.
+     * <b>Compatibility Note:</b> For applications targeting
+     * {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return false.
      */
+    @Deprecated
     public boolean reassociate() {
         try {
             mService.reassociate(mContext.getOpPackageName());
@@ -1821,7 +2187,12 @@
      * @return {@code false} if the request cannot be satisfied; {@code true} indicates that wifi is
      *         either already in the requested state, or in progress toward the requested state.
      * @throws  {@link java.lang.SecurityException} if the caller is missing required permissions.
+     *
+     * @deprecated Starting with Build.VERSION_CODES#Q, applications are not allowed to
+     * enable/disable Wi-Fi regardless of application's target SDK. This API will have no effect
+     * and will always return false.
      */
+    @Deprecated
     public boolean setWifiEnabled(boolean enabled) {
         try {
             return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);
diff --git a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
new file mode 100644
index 0000000..55fde4ca
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
@@ -0,0 +1,187 @@
+/*
+ * 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.net.wifi;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.Preconditions.checkState;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.MacAddress;
+import android.net.MatchAllNetworkSpecifier;
+import android.net.NetworkAgent;
+import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Network specifier object used by wifi's {@link android.net.NetworkAgent}.
+ * @hide
+ */
+public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements Parcelable {
+    /**
+     * Security credentials for the currently connected network.
+     */
+    private final WifiConfiguration mWifiConfiguration;
+
+    /**
+     * The UID of the app that requested a specific wifi network using {@link WifiNetworkSpecifier}.
+     *
+     * Will only be filled when the device connects to a wifi network as a result of a
+     * {@link NetworkRequest} with {@link WifiNetworkSpecifier}. Will be set to -1 if the device
+     * auto-connected to a wifi network.
+     */
+    private final int mOriginalRequestorUid;
+
+    public WifiNetworkAgentSpecifier(@NonNull WifiConfiguration wifiConfiguration,
+                                     int originalRequestorUid) {
+        checkNotNull(wifiConfiguration);
+
+        mWifiConfiguration = wifiConfiguration;
+        mOriginalRequestorUid = originalRequestorUid;
+    }
+
+    /**
+     * @hide
+     */
+    public static final Creator<WifiNetworkAgentSpecifier> CREATOR =
+            new Creator<WifiNetworkAgentSpecifier>() {
+                @Override
+                public WifiNetworkAgentSpecifier createFromParcel(@NonNull Parcel in) {
+                    WifiConfiguration wifiConfiguration = in.readParcelable(null);
+                    int originalRequestorUid = in.readInt();
+                    return new WifiNetworkAgentSpecifier(wifiConfiguration, originalRequestorUid);
+                }
+
+                @Override
+                public WifiNetworkAgentSpecifier[] newArray(int size) {
+                    return new WifiNetworkAgentSpecifier[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeParcelable(mWifiConfiguration, flags);
+        dest.writeInt(mOriginalRequestorUid);
+    }
+
+    @Override
+    public boolean satisfiedBy(@Nullable NetworkSpecifier other) {
+        if (this == other) {
+            return true;
+        }
+        // Any generic requests should be satisifed by a specific wifi network.
+        if (other == null || other instanceof MatchAllNetworkSpecifier) {
+            return true;
+        }
+        if (other instanceof WifiNetworkSpecifier) {
+            return satisfiesNetworkSpecifier((WifiNetworkSpecifier) other);
+        }
+        if (other instanceof WifiNetworkAgentSpecifier) {
+            throw new IllegalStateException("WifiNetworkAgentSpecifier instances should never be "
+                    + "compared");
+        }
+        return false;
+    }
+
+    /**
+     * Match {@link WifiNetworkSpecifier} in app's {@link NetworkRequest} with the
+     * {@link WifiNetworkAgentSpecifier} in wifi platform's {@link NetworkAgent}.
+     */
+    public boolean satisfiesNetworkSpecifier(@NonNull WifiNetworkSpecifier ns) {
+        // None of these should be null by construction.
+        // {@link WifiNetworkConfigBuilder} enforces non-null in {@link WifiNetworkSpecifier}.
+        // {@link WifiNetworkFactory} ensures non-null in {@link WifiNetworkAgentSpecifier}.
+        checkNotNull(ns);
+        checkNotNull(ns.ssidPatternMatcher);
+        checkNotNull(ns.bssidPatternMatcher);
+        checkNotNull(ns.wifiConfiguration.allowedKeyManagement);
+        checkNotNull(this.mWifiConfiguration.SSID);
+        checkNotNull(this.mWifiConfiguration.BSSID);
+        checkNotNull(this.mWifiConfiguration.allowedKeyManagement);
+
+        final String ssidWithQuotes = this.mWifiConfiguration.SSID;
+        checkState(ssidWithQuotes.startsWith("\"") && ssidWithQuotes.endsWith("\""));
+        final String ssidWithoutQuotes = ssidWithQuotes.substring(1, ssidWithQuotes.length() - 1);
+        if (!ns.ssidPatternMatcher.match(ssidWithoutQuotes)) {
+            return false;
+        }
+        final MacAddress bssid = MacAddress.fromString(this.mWifiConfiguration.BSSID);
+        final MacAddress matchBaseAddress = ns.bssidPatternMatcher.first;
+        final MacAddress matchMask = ns.bssidPatternMatcher.second;
+        if (!bssid.matches(matchBaseAddress, matchMask))  {
+            return false;
+        }
+        if (!ns.wifiConfiguration.allowedKeyManagement.equals(
+                this.mWifiConfiguration.allowedKeyManagement)) {
+            return false;
+        }
+        if (ns.requestorUid != this.mOriginalRequestorUid) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mWifiConfiguration.SSID,
+                mWifiConfiguration.BSSID,
+                mWifiConfiguration.allowedKeyManagement,
+                mOriginalRequestorUid);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof WifiNetworkAgentSpecifier)) {
+            return false;
+        }
+        WifiNetworkAgentSpecifier lhs = (WifiNetworkAgentSpecifier) obj;
+        return Objects.equals(this.mWifiConfiguration.SSID, lhs.mWifiConfiguration.SSID)
+                && Objects.equals(this.mWifiConfiguration.BSSID, lhs.mWifiConfiguration.BSSID)
+                && Objects.equals(this.mWifiConfiguration.allowedKeyManagement,
+                    lhs.mWifiConfiguration.allowedKeyManagement)
+                && mOriginalRequestorUid == lhs.mOriginalRequestorUid;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("WifiNetworkAgentSpecifier [");
+        sb.append(", WifiConfiguration=").append(
+                mWifiConfiguration == null ? null : mWifiConfiguration.configKey())
+                .append(", mOriginalRequestorUid=").append(mOriginalRequestorUid)
+                .append("]");
+        return sb.toString();
+    }
+
+    @Override
+    public void assertValidFromUid(int requestorUid) {
+        throw new IllegalStateException("WifiNetworkAgentSpecifier should never be used "
+                + "for requests.");
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
new file mode 100644
index 0000000..ae4f405
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
@@ -0,0 +1,511 @@
+/*
+ * 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.net.wifi;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.net.MacAddress;
+import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
+import android.os.PatternMatcher;
+import android.os.Process;
+import android.text.TextUtils;
+import android.util.Pair;
+
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * WifiNetworkConfigBuilder to use for creating Wi-Fi network configuration.
+ * <li>See {@link #buildNetworkSpecifier()} for creating a network specifier to use in
+ * {@link NetworkRequest}.</li>
+ * <li>See {@link #buildNetworkSuggestion()} for creating a network suggestion to use in
+ * {@link WifiManager#addNetworkSuggestions(List, PendingIntent)}.</li>
+ */
+public class WifiNetworkConfigBuilder {
+    private static final String MATCH_ALL_SSID_PATTERN_PATH = ".*";
+    private static final String MATCH_EMPTY_SSID_PATTERN_PATH = "";
+    private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN =
+            new Pair(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS);
+    private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN =
+            new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+    private static final MacAddress MATCH_EXACT_BSSID_PATTERN_MASK =
+            MacAddress.BROADCAST_ADDRESS;
+    private static final int UNASSIGNED_PRIORITY = -1;
+
+    /**
+     * SSID pattern match specified by the app.
+     */
+    private @Nullable PatternMatcher mSsidPatternMatcher;
+    /**
+     * BSSID pattern match specified by the app.
+     * Pair of <BaseAddress, Mask>.
+     */
+    private @Nullable Pair<MacAddress, MacAddress> mBssidPatternMatcher;
+    /**
+     * Pre-shared key for use with WPA-PSK networks.
+     */
+    private @Nullable String mPskPassphrase;
+    /**
+     * The enterprise configuration details specifying the EAP method,
+     * certificates and other settings associated with the EAP.
+     */
+    private @Nullable WifiEnterpriseConfig mEnterpriseConfig;
+    /**
+     * This is a network that does not broadcast its SSID, so an
+     * SSID-specific probe request must be used for scans.
+     */
+    private boolean mIsHiddenSSID;
+    /**
+     * Whether app needs to log in to captive portal to obtain Internet access.
+     */
+    private boolean mIsAppInteractionRequired;
+    /**
+     * Whether user needs to log in to captive portal to obtain Internet access.
+     */
+    private boolean mIsUserInteractionRequired;
+    /**
+     * Whether this network is metered or not.
+     */
+    private boolean mIsMetered;
+    /**
+     * Priority of this network among other network suggestions provided by the app.
+     * The lower the number, the higher the priority (i.e value of 0 = highest priority).
+     */
+    private int mPriority;
+
+    public WifiNetworkConfigBuilder() {
+        mSsidPatternMatcher = null;
+        mBssidPatternMatcher = null;
+        mPskPassphrase = null;
+        mEnterpriseConfig = null;
+        mIsHiddenSSID = false;
+        mIsAppInteractionRequired = false;
+        mIsUserInteractionRequired = false;
+        mIsMetered = false;
+        mPriority = UNASSIGNED_PRIORITY;
+    }
+
+    /**
+     * Set the unicode SSID match pattern to use for filtering networks from scan results.
+     * <p>
+     * <li>Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}. </li>
+     * <li>Overrides any previous value set using {@link #setSsid(String)} or
+     * {@link #setSsidPattern(PatternMatcher)}.</li>
+     *
+     * @param ssidPattern Instance of {@link PatternMatcher} containing the UTF-8 encoded
+     *                    string pattern to use for matching the network's SSID.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setSsidPattern(@NonNull PatternMatcher ssidPattern) {
+        checkNotNull(ssidPattern);
+        mSsidPatternMatcher = ssidPattern;
+        return this;
+    }
+
+    /**
+     * Set the unicode SSID for the network.
+     * <p>
+     * <li>For network requests ({@link NetworkSpecifier}), built using
+     * {@link #buildNetworkSpecifier}, sets the SSID to use for filtering networks from scan
+     * results. Will only match networks whose SSID is identical to the UTF-8 encoding of the
+     * specified value.</li>
+     * <li>For network suggestions ({@link WifiNetworkSuggestion}), built using
+     * {@link #buildNetworkSuggestion()}, sets the SSID for the network.</li>
+     * <li>Overrides any previous value set using {@link #setSsid(String)} or
+     * {@link #setSsidPattern(PatternMatcher)}.</li>
+     *
+     * @param ssid The SSID of the network. It must be valid Unicode.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     * @throws IllegalArgumentException if the SSID is not valid unicode.
+     */
+    public WifiNetworkConfigBuilder setSsid(@NonNull String ssid) {
+        checkNotNull(ssid);
+        final CharsetEncoder unicodeEncoder = StandardCharsets.UTF_8.newEncoder();
+        if (!unicodeEncoder.canEncode(ssid)) {
+            throw new IllegalArgumentException("SSID is not a valid unicode string");
+        }
+        mSsidPatternMatcher = new PatternMatcher(ssid, PatternMatcher.PATTERN_LITERAL);
+        return this;
+    }
+
+    /**
+     * Set the BSSID match pattern to use for filtering networks from scan results.
+     * Will match all networks with BSSID which satisfies the following:
+     * {@code BSSID & mask == baseAddress}.
+     * <p>
+     * <li>Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}. </li>
+     * <li>Overrides any previous value set using {@link #setBssid(MacAddress)} or
+     * {@link #setBssidPattern(MacAddress, MacAddress)}.</li>
+     *
+     * @param baseAddress Base address for BSSID pattern.
+     * @param mask Mask for BSSID pattern.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setBssidPattern(
+            @NonNull MacAddress baseAddress, @NonNull MacAddress mask) {
+        checkNotNull(baseAddress, mask);
+        mBssidPatternMatcher = Pair.create(baseAddress, mask);
+        return this;
+    }
+
+    /**
+     * Set the BSSID to use for filtering networks from scan results. Will only match network whose
+     * BSSID is identical to the specified value.
+     * <p>
+     * <li>Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}. </li>
+     * <li>Overrides any previous value set using {@link #setBssid(MacAddress)} or
+     * {@link #setBssidPattern(MacAddress, MacAddress)}.</li>
+     *
+     * @param bssid BSSID of the network.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setBssid(@NonNull MacAddress bssid) {
+        checkNotNull(bssid);
+        mBssidPatternMatcher = Pair.create(bssid, MATCH_EXACT_BSSID_PATTERN_MASK);
+        return this;
+    }
+
+    /**
+     * Set the ASCII PSK passphrase for this network. Needed for authenticating to
+     * WPA_PSK networks.
+     *
+     * @param pskPassphrase PSK passphrase of the network.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     * @throws IllegalArgumentException if the passphrase is not ASCII encodable.
+     */
+    public WifiNetworkConfigBuilder setPskPassphrase(@NonNull String pskPassphrase) {
+        checkNotNull(pskPassphrase);
+        final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
+        if (!asciiEncoder.canEncode(pskPassphrase)) {
+            throw new IllegalArgumentException("passphrase not ASCII encodable");
+        }
+        mPskPassphrase = pskPassphrase;
+        return this;
+    }
+
+    /**
+     * Set the associated enterprise configuration for this network. Needed for authenticating to
+     * WPA_EAP networks. See {@link WifiEnterpriseConfig} for description.
+     *
+     * @param enterpriseConfig Instance of {@link WifiEnterpriseConfig}.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setEnterpriseConfig(
+            @NonNull WifiEnterpriseConfig enterpriseConfig) {
+        checkNotNull(enterpriseConfig);
+        mEnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
+        return this;
+    }
+
+    /**
+     * Specifies whether this represents a hidden network.
+     * <p>
+     * <li>For network requests (see {@link NetworkSpecifier}), built using
+     * {@link #buildNetworkSpecifier}, setting this disallows the usage of
+     * {@link #setSsidPattern(PatternMatcher)} since hidden networks need to be explicitly
+     * probed for.</li>
+     * <li>If not set, defaults to false (i.e not a hidden network).</li>
+     *
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setIsHiddenSsid() {
+        mIsHiddenSSID = true;
+        return this;
+    }
+
+    /**
+     * Specifies whether the app needs to log in to a captive portal to obtain Internet access.
+     * <p>
+     * This will dictate if the associated pending intent in
+     * {@link WifiManager#addNetworkSuggestions(List, PendingIntent)} will be sent after
+     * successfully connecting to the network.
+     * Use this for captive portal type networks where the app needs to authenticate the user
+     * before the device can access the network.
+     * This setting will be ignored if the {@code PendingIntent} used to add this network
+     * suggestion is null.
+     * <p>
+     * <li>Only allowed for creating network suggestion, i.e {@link #buildNetworkSuggestion()}.</li>
+     * <li>If not set, defaults to false (i.e no app interaction required).</li>
+     *
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setIsAppInteractionRequired() {
+        mIsAppInteractionRequired = true;
+        return this;
+    }
+
+    /**
+     * Specifies whether the user needs to log in to a captive portal to obtain Internet access.
+     * <p>
+     * <li>Only allowed for creating network suggestion, i.e {@link #buildNetworkSuggestion()}.</li>
+     * <li>If not set, defaults to false (i.e no user interaction required).</li>
+     *
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setIsUserInteractionRequired() {
+        mIsUserInteractionRequired = true;
+        return this;
+    }
+
+    /**
+     * Specify the priority of this network among other network suggestions provided by the same app
+     * (priorities have no impact on suggestions by different apps). The lower the number, the
+     * higher the priority (i.e value of 0 = highest priority).
+     * <p>
+     * <li>Only allowed for creating network suggestion, i.e {@link #buildNetworkSuggestion()}.</li>
+     * <li>If not set, defaults to -1 (i.e unassigned priority).</li>
+     *
+     * @param priority Integer number representing the priority among suggestions by the app.
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     * @throws IllegalArgumentException if the priority value is negative.
+     */
+    public WifiNetworkConfigBuilder setPriority(int priority) {
+        if (priority < 0) {
+            throw new IllegalArgumentException("Invalid priority value " + priority);
+        }
+        mPriority = priority;
+        return this;
+    }
+
+    /**
+     * Specifies whether this network is metered.
+     * <p>
+     * <li>Only allowed for creating network suggestion, i.e {@link #buildNetworkSuggestion()}.</li>
+     * <li>If not set, defaults to false (i.e not metered).</li>
+     *
+     * @return Instance of {@link WifiNetworkConfigBuilder} to enable chaining of the builder
+     * method.
+     */
+    public WifiNetworkConfigBuilder setIsMetered() {
+        mIsMetered = true;
+        return this;
+    }
+
+    /**
+     * Set defaults for the various low level credential type fields in the newly created
+     * WifiConfiguration object.
+     *
+     * See {@link com.android.server.wifi.WifiConfigManager#setDefaultsInWifiConfiguration(
+     * WifiConfiguration)}.
+     *
+     * @param configuration provided WifiConfiguration object.
+     */
+    private static void setDefaultsInWifiConfiguration(@NonNull WifiConfiguration configuration) {
+        configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
+        configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
+        configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
+        configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
+    }
+
+    private void setKeyMgmtInWifiConfiguration(@NonNull WifiConfiguration configuration) {
+        if (!TextUtils.isEmpty(mPskPassphrase)) {
+            // WPA_PSK network.
+            configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        } else if (mEnterpriseConfig != null) {
+            // WPA_EAP network
+            configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+            configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+        } else {
+            // Open network
+            configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        }
+    }
+
+    /**
+     * Helper method to build WifiConfiguration object from the builder.
+     * @return Instance of {@link WifiConfiguration}.
+     */
+    private WifiConfiguration buildWifiConfiguration() {
+        final WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        setDefaultsInWifiConfiguration(wifiConfiguration);
+        // WifiConfiguration.SSID needs quotes around unicode SSID.
+        if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) {
+            wifiConfiguration.SSID = "\"" + mSsidPatternMatcher.getPath() + "\"";
+        }
+        setKeyMgmtInWifiConfiguration(wifiConfiguration);
+        // WifiConfiguration.preSharedKey needs quotes around ASCII password.
+        if (mPskPassphrase != null) {
+            wifiConfiguration.preSharedKey = "\"" + mPskPassphrase + "\"";
+        }
+        wifiConfiguration.enterpriseConfig = mEnterpriseConfig;
+        wifiConfiguration.hiddenSSID = mIsHiddenSSID;
+        wifiConfiguration.priority = mPriority;
+        wifiConfiguration.meteredOverride =
+                mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED
+                           : WifiConfiguration.METERED_OVERRIDE_NONE;
+        return wifiConfiguration;
+    }
+
+    private boolean hasSetAnyPattern() {
+        return mSsidPatternMatcher != null || mBssidPatternMatcher != null;
+    }
+
+    private void setMatchAnyPatternIfUnset() {
+        if (mSsidPatternMatcher == null) {
+            mSsidPatternMatcher = new PatternMatcher(MATCH_ALL_SSID_PATTERN_PATH,
+                    PatternMatcher.PATTERN_SIMPLE_GLOB);
+        }
+        if (mBssidPatternMatcher == null) {
+            mBssidPatternMatcher = MATCH_ALL_BSSID_PATTERN;
+        }
+    }
+
+    private boolean hasSetMatchNonePattern() {
+        if (mSsidPatternMatcher.getType() != PatternMatcher.PATTERN_PREFIX
+                && mSsidPatternMatcher.getPath().equals(MATCH_EMPTY_SSID_PATTERN_PATH)) {
+            return true;
+        }
+        if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean hasSetMatchAllPattern() {
+        if ((mSsidPatternMatcher.match(MATCH_EMPTY_SSID_PATTERN_PATH))
+                && mBssidPatternMatcher.equals(MATCH_ALL_BSSID_PATTERN)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Create a specifier object used to request a Wi-Fi network. The generated
+     * {@link NetworkSpecifier} should be used in
+     * {@link NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} when building
+     * the {@link NetworkRequest}.
+     *<p>
+     * Note: Apps can set a combination of network match params:
+     * <li> SSID Pattern using {@link #setSsidPattern(PatternMatcher)} OR Specific SSID using
+     * {@link #setSsid(String)}. </li>
+     * AND/OR
+     * <li> BSSID Pattern using {@link #setBssidPattern(MacAddress, MacAddress)} OR Specific BSSID
+     * using {@link #setBssid(MacAddress)} </li>
+     * to trigger connection to a network that matches the set params.
+     * The system will find the set of networks matching the request and present the user
+     * with a system dialog which will allow the user to select a specific Wi-Fi network to connect
+     * to or to deny the request.
+     *</p>
+     *
+     * For example:
+     * To connect to an open network with a SSID prefix of "test" and a BSSID OUI of "10:03:23":
+     * {@code
+     * final NetworkSpecifier specifier =
+     *      new WifiNetworkConfigBuilder()
+     *      .setSsidPattern(new PatternMatcher("test", PatterMatcher.PATTERN_PREFIX))
+     *      .setBssidPattern(MacAddress.fromString("10:03:23:00:00:00"),
+     *                       MacAddress.fromString("ff:ff:ff:00:00:00"))
+     *      .buildNetworkSpecifier()
+     * final NetworkRequest request =
+     *      new NetworkRequest.Builder()
+     *      .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+     *      .setNetworkSpecifier(specifier)
+     *      .build();
+     * final ConnectivityManager connectivityManager =
+     *      context.getSystemService(Context.CONNECTIVITY_SERVICE);
+     * final NetworkCallback networkCallback = new NetworkCallback() {
+     *      ...
+     *      @Override
+     *      void onAvailable(...) {}
+     *      // etc.
+     * };
+     * connectivityManager.requestNetwork(request, networkCallback);
+     * }
+     *
+     * @return Instance of {@link NetworkSpecifier}.
+     * @throws IllegalStateException on invalid params set.
+     */
+    public NetworkSpecifier buildNetworkSpecifier() {
+        if (!hasSetAnyPattern()) {
+            throw new IllegalStateException("one of setSsidPattern/setSsid/setBssidPattern/setBssid"
+                    + " should be invoked for specifier");
+        }
+        setMatchAnyPatternIfUnset();
+        if (hasSetMatchNonePattern()) {
+            throw new IllegalStateException("cannot set match-none pattern for specifier");
+        }
+        if (hasSetMatchAllPattern()) {
+            throw new IllegalStateException("cannot set match-all pattern for specifier");
+        }
+        if (mIsHiddenSSID && mSsidPatternMatcher.getType() != PatternMatcher.PATTERN_LITERAL) {
+            throw new IllegalStateException("setSsid should also be invoked when "
+                    + "setIsHiddenSsid is invoked for network specifier");
+        }
+        if (mIsAppInteractionRequired || mIsUserInteractionRequired
+                || mPriority != -1 || mIsMetered) {
+            throw new IllegalStateException("none of setIsAppInteractionRequired/"
+                    + "setIsUserInteractionRequired/setPriority/setIsMetered are allowed for "
+                    + "specifier");
+        }
+        if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) {
+            throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can"
+                    + " be invoked for network specifier");
+        }
+
+        return new WifiNetworkSpecifier(
+                mSsidPatternMatcher,
+                mBssidPatternMatcher,
+                buildWifiConfiguration(),
+                Process.myUid());
+    }
+
+    /**
+     * Create a network suggestion object use in
+     * {@link WifiManager#addNetworkSuggestions(List, PendingIntent)}.
+     * See {@link WifiNetworkSuggestion}.
+     *
+     * @return Instance of {@link WifiNetworkSuggestion}.
+     * @throws IllegalStateException on invalid params set.
+     */
+    public WifiNetworkSuggestion buildNetworkSuggestion() {
+        if (mSsidPatternMatcher == null) {
+            throw new IllegalStateException("setSsid should be invoked for suggestion");
+        }
+        if (mSsidPatternMatcher.getType() != PatternMatcher.PATTERN_LITERAL
+                || mBssidPatternMatcher != null) {
+            throw new IllegalStateException("none of setSsidPattern/setBssidPattern/setBssid are"
+                    + " allowed for suggestion");
+        }
+        if (!TextUtils.isEmpty(mPskPassphrase) && mEnterpriseConfig != null) {
+            throw new IllegalStateException("only one of setPreSharedKey or setEnterpriseConfig can"
+                    + "be invoked for suggestion");
+        }
+
+        return new WifiNetworkSuggestion(
+                buildWifiConfiguration(),
+                mIsAppInteractionRequired,
+                mIsUserInteractionRequired,
+                Process.myUid());
+
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
new file mode 100644
index 0000000..4348399
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -0,0 +1,181 @@
+/*
+ * 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.net.wifi;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.net.MacAddress;
+import android.net.MatchAllNetworkSpecifier;
+import android.net.NetworkSpecifier;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PatternMatcher;
+import android.util.Pair;
+
+import java.util.Objects;
+
+/**
+ * Network specifier object used to request a Wi-Fi network. Apps should use the
+ * {@link WifiNetworkConfigBuilder} class to create an instance.
+ * @hide
+ */
+public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+    /**
+     * SSID pattern match specified by the app.
+     */
+    public final PatternMatcher ssidPatternMatcher;
+
+    /**
+     * BSSID pattern match specified by the app.
+     * Pair of <BaseAddress, Mask>.
+     */
+    public final Pair<MacAddress, MacAddress> bssidPatternMatcher;
+
+    /**
+     * Security credentials for the network.
+     * <p>
+     * Note: {@link WifiConfiguration#SSID} & {@link WifiConfiguration#BSSID} fields from
+     * WifiConfiguration are not used. Instead we use the {@link #ssidPatternMatcher} &
+     * {@link #bssidPatternMatcher} fields embedded directly
+     * within {@link WifiNetworkSpecifier}.
+     */
+    public final WifiConfiguration wifiConfiguration;
+
+    /**
+     * The UID of the process initializing this network specifier. Validated by receiver using
+     * checkUidIfNecessary() and is used by satisfiedBy() to determine whether the specifier
+     * matches the offered network.
+     */
+    public final int requestorUid;
+
+    public WifiNetworkSpecifier(@NonNull PatternMatcher ssidPatternMatcher,
+                 @NonNull Pair<MacAddress, MacAddress> bssidPatternMatcher,
+                 @NonNull WifiConfiguration wifiConfiguration,
+                 int requestorUid) {
+        checkNotNull(ssidPatternMatcher);
+        checkNotNull(bssidPatternMatcher);
+        checkNotNull(wifiConfiguration);
+
+        this.ssidPatternMatcher = ssidPatternMatcher;
+        this.bssidPatternMatcher = bssidPatternMatcher;
+        this.wifiConfiguration = wifiConfiguration;
+        this.requestorUid = requestorUid;
+    }
+
+    public static final Creator<WifiNetworkSpecifier> CREATOR =
+            new Creator<WifiNetworkSpecifier>() {
+                @Override
+                public WifiNetworkSpecifier createFromParcel(Parcel in) {
+                    PatternMatcher ssidPatternMatcher = in.readParcelable(/* classLoader */null);
+                    MacAddress baseAddress = in.readParcelable(null);
+                    MacAddress mask = in.readParcelable(null);
+                    Pair<MacAddress, MacAddress> bssidPatternMatcher =
+                            Pair.create(baseAddress, mask);
+                    WifiConfiguration wifiConfiguration = in.readParcelable(null);
+                    int requestorUid = in.readInt();
+                    return new WifiNetworkSpecifier(ssidPatternMatcher, bssidPatternMatcher,
+                            wifiConfiguration, requestorUid);
+                }
+
+                @Override
+                public WifiNetworkSpecifier[] newArray(int size) {
+                    return new WifiNetworkSpecifier[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(ssidPatternMatcher, flags);
+        dest.writeParcelable(bssidPatternMatcher.first, flags);
+        dest.writeParcelable(bssidPatternMatcher.second, flags);
+        dest.writeParcelable(wifiConfiguration, flags);
+        dest.writeInt(requestorUid);
+    }
+
+    @Override
+    public boolean satisfiedBy(NetworkSpecifier other) {
+        if (this == other) {
+            return true;
+        }
+        // Any generic requests should be satisifed by a specific wifi network.
+        if (other == null || other instanceof MatchAllNetworkSpecifier) {
+            return true;
+        }
+        if (other instanceof WifiNetworkAgentSpecifier) {
+            return ((WifiNetworkAgentSpecifier) other).satisfiesNetworkSpecifier(this);
+        }
+        // Specific requests are checked for equality although testing for equality of 2 patterns do
+        // not make much sense!
+        return equals(other);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                ssidPatternMatcher.getPath(),
+                ssidPatternMatcher.getType(),
+                bssidPatternMatcher,
+                wifiConfiguration.allowedKeyManagement,
+                requestorUid);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof WifiNetworkSpecifier)) {
+            return false;
+        }
+        WifiNetworkSpecifier lhs = (WifiNetworkSpecifier) obj;
+        return Objects.equals(this.ssidPatternMatcher.getPath(),
+                    lhs.ssidPatternMatcher.getPath())
+                && Objects.equals(this.ssidPatternMatcher.getType(),
+                    lhs.ssidPatternMatcher.getType())
+                && Objects.equals(this.bssidPatternMatcher,
+                    lhs.bssidPatternMatcher)
+                && Objects.equals(this.wifiConfiguration.allowedKeyManagement,
+                    lhs.wifiConfiguration.allowedKeyManagement)
+                && requestorUid == lhs.requestorUid;
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder()
+                .append("WifiNetworkSpecifierWifiNetworkSpecifier [")
+                .append(", SSID Match pattern=").append(ssidPatternMatcher)
+                .append(", BSSID Match pattern=").append(bssidPatternMatcher)
+                .append(", WifiConfiguration=").append(
+                wifiConfiguration == null ? null : wifiConfiguration.configKey())
+                .append(", requestorUid=").append(requestorUid)
+                .append("]")
+                .toString();
+    }
+
+    @Override
+    public void assertValidFromUid(int requestorUid) {
+        if (this.requestorUid != requestorUid) {
+            throw new SecurityException("mismatched UIDs");
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
new file mode 100644
index 0000000..04b9cb5
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.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.net.wifi;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.app.PendingIntent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The Network Suggestion object is used to provide a Wi-Fi network for consideration when
+ * auto-connecting to networks. Apps cannot directly create this object, they must use
+ * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} to obtain an instance
+ * of this object.
+ *<p>
+ * Apps can provide a list of such networks to the platform using
+ * {@link WifiManager#addNetworkSuggestions(List, PendingIntent)}.
+ */
+public final class WifiNetworkSuggestion implements Parcelable {
+    /**
+     * Network configuration for the provided network.
+     * @hide
+     */
+    public final WifiConfiguration wifiConfiguration;
+
+    /**
+     * Whether app needs to log in to captive portal to obtain Internet access.
+     * This will dictate if the associated pending intent in
+     * {@link WifiManager#addNetworkSuggestions(List, PendingIntent)} needs to be sent after
+     * successfully connecting to the network.
+     * @hide
+     */
+    public final boolean isAppInteractionRequired;
+
+    /**
+     * Whether user needs to log in to captive portal to obtain Internet access.
+     * @hide
+     */
+    public final boolean isUserInteractionRequired;
+
+    /**
+     * The UID of the process initializing this network suggestion.
+     * @hide
+     */
+    public final int suggestorUid;
+
+    /** @hide */
+    public WifiNetworkSuggestion(WifiConfiguration wifiConfiguration,
+                                 boolean isAppInteractionRequired,
+                                 boolean isUserInteractionRequired,
+                                 int suggestorUid) {
+        checkNotNull(wifiConfiguration);
+
+        this.wifiConfiguration = wifiConfiguration;
+        this.isAppInteractionRequired = isAppInteractionRequired;
+        this.isUserInteractionRequired = isUserInteractionRequired;
+        this.suggestorUid = suggestorUid;
+    }
+
+    public static final Creator<WifiNetworkSuggestion> CREATOR =
+            new Creator<WifiNetworkSuggestion>() {
+                @Override
+                public WifiNetworkSuggestion createFromParcel(Parcel in) {
+                    return new WifiNetworkSuggestion(
+                            in.readParcelable(null), // wifiConfiguration
+                            in.readBoolean(), // isAppInteractionRequired
+                            in.readBoolean(), // isUserInteractionRequired
+                            in.readInt() // suggestorUid
+                    );
+                }
+
+                @Override
+                public WifiNetworkSuggestion[] newArray(int size) {
+                    return new WifiNetworkSuggestion[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(wifiConfiguration, flags);
+        dest.writeBoolean(isAppInteractionRequired);
+        dest.writeBoolean(isUserInteractionRequired);
+        dest.writeInt(suggestorUid);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.allowedKeyManagement,
+                suggestorUid);
+    }
+
+    /**
+     * Equals for network suggestions.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof WifiNetworkSuggestion)) {
+            return false;
+        }
+        WifiNetworkSuggestion lhs = (WifiNetworkSuggestion) obj;
+        return Objects.equals(this.wifiConfiguration.SSID, lhs.wifiConfiguration.SSID)
+                && Objects.equals(this.wifiConfiguration.allowedKeyManagement,
+                                  lhs.wifiConfiguration.allowedKeyManagement)
+                && suggestorUid == lhs.suggestorUid;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("WifiNetworkSuggestion [")
+                .append(", WifiConfiguration=").append(wifiConfiguration)
+                .append(", isAppInteractionRequired=").append(isAppInteractionRequired)
+                .append(", isUserInteractionRequired=").append(isUserInteractionRequired)
+                .append(", suggestorUid=").append(suggestorUid)
+                .append("]");
+        return sb.toString();
+    }
+}
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/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index e40b657a..ea41bb3 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -43,6 +43,8 @@
 import android.net.wifi.WifiManager.LocalOnlyHotspotObserver;
 import android.net.wifi.WifiManager.LocalOnlyHotspotReservation;
 import android.net.wifi.WifiManager.LocalOnlyHotspotSubscription;
+import android.net.wifi.WifiManager.NetworkRequestMatchCallback;
+import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback;
 import android.net.wifi.WifiManager.SoftApCallback;
 import android.net.wifi.WifiManager.TrafficStateCallback;
 import android.os.Handler;
@@ -59,6 +61,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+
 /**
  * Unit tests for {@link android.net.wifi.WifiManager}.
  */
@@ -67,16 +71,19 @@
 
     private static final int ERROR_NOT_SET = -1;
     private static final int ERROR_TEST_REASON = 5;
+    private static final int TEST_UID = 14553;
     private static final String TEST_PACKAGE_NAME = "TestPackage";
     private static final String TEST_COUNTRY_CODE = "US";
 
     @Mock Context mContext;
-    @Mock IWifiManager mWifiService;
+    @Mock
+    android.net.wifi.IWifiManager mWifiService;
     @Mock ApplicationInfo mApplicationInfo;
     @Mock WifiConfiguration mApConfig;
     @Mock IBinder mAppBinder;
     @Mock SoftApCallback mSoftApCallback;
     @Mock TrafficStateCallback mTrafficStateCallback;
+    @Mock NetworkRequestMatchCallback mNetworkRequestMatchCallback;
 
     private Handler mHandler;
     private TestLooper mLooper;
@@ -1163,4 +1170,84 @@
         assertEquals(1, altLooper.dispatchAll());
         verify(mTrafficStateCallback).onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT);
     }
+
+    /**
+     * Verify the call to registerNetworkRequestMatchCallback goes to WifiServiceImpl.
+     */
+    @Test
+    public void registerNetworkRequestMatchCallbackCallGoesToWifiServiceImpl()
+            throws Exception {
+        when(mContext.getMainLooper()).thenReturn(mLooper.getLooper());
+        ArgumentCaptor<INetworkRequestMatchCallback.Stub> callbackCaptor =
+                ArgumentCaptor.forClass(INetworkRequestMatchCallback.Stub.class);
+        mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback, null);
+        verify(mWifiService).registerNetworkRequestMatchCallback(
+                any(IBinder.class), callbackCaptor.capture(), anyInt());
+
+        INetworkRequestUserSelectionCallback iUserSelectionCallback =
+                mock(INetworkRequestUserSelectionCallback.class);
+
+        assertEquals(0, mLooper.dispatchAll());
+        callbackCaptor.getValue().onMatch(new ArrayList<WifiConfiguration>());
+        assertEquals(1, mLooper.dispatchAll());
+        verify(mNetworkRequestMatchCallback).onMatch(anyList());
+
+        callbackCaptor.getValue().onUserSelectionConnectSuccess(new WifiConfiguration());
+        assertEquals(1, mLooper.dispatchAll());
+        verify(mNetworkRequestMatchCallback).onUserSelectionConnectSuccess(
+                any(WifiConfiguration.class));
+
+        callbackCaptor.getValue().onUserSelectionConnectFailure(new WifiConfiguration());
+        assertEquals(1, mLooper.dispatchAll());
+        verify(mNetworkRequestMatchCallback).onUserSelectionConnectFailure(
+                any(WifiConfiguration.class));
+    }
+
+    /**
+     * Verify the call to unregisterNetworkRequestMatchCallback goes to WifiServiceImpl.
+     */
+    @Test
+    public void unregisterNetworkRequestMatchCallbackCallGoesToWifiServiceImpl() throws Exception {
+        ArgumentCaptor<Integer> callbackIdentifier = ArgumentCaptor.forClass(Integer.class);
+        mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback, mHandler);
+        verify(mWifiService).registerNetworkRequestMatchCallback(
+                any(IBinder.class), any(INetworkRequestMatchCallback.class),
+                callbackIdentifier.capture());
+
+        mWifiManager.unregisterNetworkRequestMatchCallback(mNetworkRequestMatchCallback);
+        verify(mWifiService).unregisterNetworkRequestMatchCallback(
+                eq((int) callbackIdentifier.getValue()));
+    }
+
+    /**
+     * Verify the call to NetworkRequestUserSelectionCallback goes to
+     * WifiServiceImpl.
+     */
+    @Test
+    public void networkRequestUserSelectionCallbackCallGoesToWifiServiceImpl()
+            throws Exception {
+        when(mContext.getMainLooper()).thenReturn(mLooper.getLooper());
+        ArgumentCaptor<INetworkRequestMatchCallback.Stub> callbackCaptor =
+                ArgumentCaptor.forClass(INetworkRequestMatchCallback.Stub.class);
+        mWifiManager.registerNetworkRequestMatchCallback(mNetworkRequestMatchCallback, null);
+        verify(mWifiService).registerNetworkRequestMatchCallback(
+                any(IBinder.class), callbackCaptor.capture(), anyInt());
+
+        INetworkRequestUserSelectionCallback iUserSelectionCallback =
+                mock(INetworkRequestUserSelectionCallback.class);
+        ArgumentCaptor<NetworkRequestUserSelectionCallback> userSelectionCallbackCaptor =
+                ArgumentCaptor.forClass(NetworkRequestUserSelectionCallback.class);
+        callbackCaptor.getValue().onUserSelectionCallbackRegistration(
+                iUserSelectionCallback);
+        assertEquals(1, mLooper.dispatchAll());
+        verify(mNetworkRequestMatchCallback).onUserSelectionCallbackRegistration(
+                userSelectionCallbackCaptor.capture());
+
+        WifiConfiguration selected = new WifiConfiguration();
+        userSelectionCallbackCaptor.getValue().select(selected);
+        verify(iUserSelectionCallback).select(selected);
+
+        userSelectionCallbackCaptor.getValue().reject();
+        verify(iUserSelectionCallback).reject();
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
new file mode 100644
index 0000000..1b0007c
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
@@ -0,0 +1,453 @@
+/*
+ * 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.net.wifi;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.MacAddress;
+import android.net.MatchAllNetworkSpecifier;
+import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
+import android.os.Parcel;
+import android.os.PatternMatcher;
+import android.support.test.filters.SmallTest;
+import android.util.Pair;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.WifiNetworkAgentSpecifier}.
+ */
+@SmallTest
+public class WifiNetworkAgentSpecifierTest {
+    private static final int TEST_UID = 5;
+    private static final int TEST_UID_1 = 8;
+    private static final String TEST_SSID = "Test123";
+    private static final String TEST_SSID_PATTERN = "Test";
+    private static final String TEST_SSID_1 = "456test";
+    private static final String TEST_BSSID = "12:12:12:aa:0b:c0";
+    private static final String TEST_BSSID_OUI_BASE_ADDRESS = "12:12:12:00:00:00";
+    private static final String TEST_BSSID_OUI_MASK = "ff:ff:ff:00:00:00";
+    private static final String TEST_BSSID_1 = "aa:cc:12:aa:0b:c0";
+    private static final String TEST_PRESHARED_KEY = "\"Test123\"";
+
+    /**
+     * Validate that parcel marshalling/unmarshalling works
+     */
+    @Test
+    public void testWifiNetworkAgentSpecifierParcel() {
+        WifiNetworkAgentSpecifier specifier = createDefaultNetworkAgentSpecifier();
+
+        Parcel parcelW = Parcel.obtain();
+        specifier.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        WifiNetworkAgentSpecifier parcelSpecifier =
+                WifiNetworkAgentSpecifier.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(specifier, parcelSpecifier);
+    }
+
+    /**
+     * Validate that the NetworkAgentSpecifier cannot be used in a {@link NetworkRequest} by apps.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkAgentSpecifierNotUsedInNetworkRequest() {
+        WifiNetworkAgentSpecifier specifier = createDefaultNetworkAgentSpecifier();
+
+        specifier.assertValidFromUid(TEST_UID);
+    }
+
+    /**
+     * Validate NetworkAgentSpecifier equals with itself.
+     * a) Create network agent specifier 1 for WPA_PSK network
+     * b) Create network agent specifier 2 with the same params as specifier 1.
+     * c) Ensure that the specifier 2 equals specifier 1.
+     */
+    @Test
+    public void testWifiNetworkAgentSpecifierEqualsSame() {
+        WifiNetworkAgentSpecifier specifier1 = createDefaultNetworkAgentSpecifier();
+        WifiNetworkAgentSpecifier specifier2 = createDefaultNetworkAgentSpecifier();
+
+        assertTrue(specifier2.equals(specifier1));
+    }
+
+    /**
+     * Validate NetworkAgentSpecifier equals between instances of {@link WifiNetworkAgentSpecifier}.
+     * a) Create network agent specifier 1 for WPA_PSK network
+     * b) Create network agent specifier 2 with different key mgmt params.
+     * c) Ensure that the specifier 2 does not equal specifier 1.
+     */
+    @Test
+    public void testWifiNetworkAgentSpecifierDoesNotEqualsWhenKeyMgmtDifferent() {
+        WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration();
+        WifiNetworkAgentSpecifier specifier1 =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfiguration1,
+                        TEST_UID);
+
+        WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1);
+        wifiConfiguration2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkAgentSpecifier specifier2 =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfiguration2,
+                        TEST_UID);
+
+        assertFalse(specifier2.equals(specifier1));
+    }
+
+    /**
+     * Validate NetworkAgentSpecifier equals between instances of {@link WifiNetworkAgentSpecifier}.
+     * a) Create network agent specifier 1 for WPA_PSK network
+     * b) Create network agent specifier 2 with different SSID.
+     * c) Ensure that the specifier 2 does not equal specifier 1.
+     */
+    @Test
+    public void testWifiNetworkAgentSpecifierDoesNotSatisifyWhenSsidDifferent() {
+        WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration();
+        WifiNetworkAgentSpecifier specifier1 =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfiguration1,
+                        TEST_UID);
+
+        WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1);
+        wifiConfiguration2.SSID = TEST_SSID_1;
+        WifiNetworkAgentSpecifier specifier2 =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfiguration2,
+                        TEST_UID);
+
+        assertFalse(specifier2.equals(specifier1));
+    }
+
+    /**
+     * Validate NetworkAgentSpecifier equals between instances of {@link WifiNetworkAgentSpecifier}.
+     * a) Create network agent specifier 1 for WPA_PSK network
+     * b) Create network agent specifier 2 with different BSSID.
+     * c) Ensure that the specifier 2 does not equal specifier 1.
+     */
+    @Test
+    public void testWifiNetworkAgentSpecifierDoesNotSatisifyWhenBssidDifferent() {
+        WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration();
+        WifiNetworkAgentSpecifier specifier1 =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfiguration1,
+                        TEST_UID);
+
+        WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1);
+        wifiConfiguration2.BSSID = TEST_BSSID_1;
+        WifiNetworkAgentSpecifier specifier2 =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfiguration2,
+                        TEST_UID);
+
+        assertFalse(specifier2.equals(specifier1));
+    }
+
+    /**
+     * Validate NetworkAgentSpecifier matching.
+     * a) Create a network agent specifier for WPA_PSK network
+     * b) Ensure that the specifier matches {@code null} and {@link MatchAllNetworkSpecifier}
+     * specifiers.
+     */
+    @Test
+    public void testWifiNetworkAgentSpecifierSatisifiesNullAndAllMatch() {
+        WifiNetworkAgentSpecifier specifier = createDefaultNetworkAgentSpecifier();
+
+        assertTrue(specifier.satisfiedBy(null));
+        assertTrue(specifier.satisfiedBy(new MatchAllNetworkSpecifier()));
+    }
+
+    /**
+     * Validate NetworkAgentSpecifier matching with itself.
+     * a) Create network agent specifier 1 for WPA_PSK network
+     * b) Create network agent specifier 2 with the same params as specifier 1.
+     * c) Ensure that invoking {@link NetworkSpecifier#satisfiedBy(NetworkSpecifier)} on 2
+     * {@link WifiNetworkAgentSpecifier} throws an exception.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkAgentSpecifierDoesNotSatisifySame() {
+        WifiNetworkAgentSpecifier specifier1 = createDefaultNetworkAgentSpecifier();
+        WifiNetworkAgentSpecifier specifier2 = createDefaultNetworkAgentSpecifier();
+
+        assertTrue(specifier2.satisfiedBy(specifier1));
+    }
+
+    /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
+     * a) Create network agent specifier for WPA_PSK network
+     * b) Create network specifier with matching SSID pattern.
+     * c) Ensure that the agent specifier is satisfied by specifier.
+     */
+    @Test
+    public void
+            testWifiNetworkAgentSpecifierSatisfiesNetworkSpecifierWithSsidPattern() {
+        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier();
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement
+                .set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                wificonfigurationNetworkSpecifier,
+                TEST_UID);
+
+        assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
+        assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+    }
+
+    /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
+     * a) Create network agent specifier for WPA_PSK network
+     * b) Create network specifier with matching BSSID pattern.
+     * c) Ensure that the agent specifier is satisfied by specifier.
+     */
+    @Test
+    public void
+            testWifiNetworkAgentSpecifierSatisfiesNetworkSpecifierWithBssidPattern() {
+        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier();
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK));
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement
+                .set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                wificonfigurationNetworkSpecifier,
+                TEST_UID);
+
+        assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
+        assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+    }
+
+    /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
+     * a) Create network agent specifier for WPA_PSK network
+     * b) Create network specifier with matching SSID & BSSID pattern.
+     * c) Ensure that the agent specifier is satisfied by specifier.
+     */
+    @Test
+    public void
+            testWifiNetworkAgentSpecifierSatisfiesNetworkSpecifierWithSsidAndBssidPattern() {
+        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier();
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK));
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement
+                .set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                wificonfigurationNetworkSpecifier,
+                TEST_UID);
+
+        assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
+        assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+    }
+
+    /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
+     * a) Create network agent specifier for WPA_PSK network
+     * b) Create network specifier with non-matching SSID pattern.
+     * c) Ensure that the agent specifier is not satisfied by specifier.
+     */
+    @Test
+    public void
+            testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithSsidPattern() {
+        WifiConfiguration wifiConfigurationNetworkAgent = createDefaultWifiConfiguration();
+        wifiConfigurationNetworkAgent.SSID = "\"" + TEST_SSID_1 + "\"";
+        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfigurationNetworkAgent,
+                        TEST_UID);
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement
+                .set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                wificonfigurationNetworkSpecifier,
+                TEST_UID);
+
+        assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
+        assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+    }
+
+    /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
+     * a) Create network agent specifier for WPA_PSK network
+     * b) Create network specifier with non-matching BSSID pattern.
+     * c) Ensure that the agent specifier is not satisfied by specifier.
+     */
+    @Test
+    public void
+            testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithBssidPattern() {
+        WifiConfiguration wifiConfigurationNetworkAgent = createDefaultWifiConfiguration();
+        wifiConfigurationNetworkAgent.BSSID = TEST_BSSID_1;
+        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfigurationNetworkAgent,
+                        TEST_UID);
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK));
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement
+                .set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                wificonfigurationNetworkSpecifier,
+                TEST_UID);
+
+        assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
+        assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+    }
+
+    /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
+     * a) Create network agent specifier for WPA_PSK network
+     * b) Create network specifier with non-matching SSID and BSSID pattern.
+     * c) Ensure that the agent specifier is not satisfied by specifier.
+     */
+    @Test
+    public void
+            testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithSsidAndBssidPattern() {
+        WifiConfiguration wifiConfigurationNetworkAgent = createDefaultWifiConfiguration();
+        wifiConfigurationNetworkAgent.BSSID = TEST_BSSID_1;
+        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier =
+                new WifiNetworkAgentSpecifier(
+                        wifiConfigurationNetworkAgent,
+                        TEST_UID);
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK));
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement
+                .set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                wificonfigurationNetworkSpecifier,
+                TEST_UID);
+
+        assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
+        assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+    }
+
+    /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
+     * a) Create network agent specifier for WPA_PSK network
+     * b) Create network specifier with matching SSID and BSSID pattern, but different key mgmt.
+     * c) Ensure that the agent specifier is not satisfied by specifier.
+     */
+    @Test
+    public void
+            testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithDifferentKeyMgmt() {
+        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier();
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK));
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                wificonfigurationNetworkSpecifier,
+                TEST_UID);
+
+        assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
+        assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+    }
+
+    /**
+     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
+     * a) Create network agent specifier for WPA_PSK network
+     * b) Create network specifier with matching SSID and BSSID pattern, but different UID.
+     * c) Ensure that the agent specifier is not satisfied by specifier.
+     */
+    @Test
+    public void
+            testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithDifferentUid() {
+        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier();
+
+        PatternMatcher ssidPattern =
+                new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
+        Pair<MacAddress, MacAddress> bssidPattern =
+                Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK));
+        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
+        wificonfigurationNetworkSpecifier.allowedKeyManagement
+                .set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
+                ssidPattern,
+                bssidPattern,
+                wificonfigurationNetworkSpecifier,
+                TEST_UID_1);
+
+        assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
+        assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
+    }
+
+    private WifiConfiguration createDefaultWifiConfiguration() {
+        WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        wifiConfiguration.SSID = "\"" + TEST_SSID + "\"";
+        wifiConfiguration.BSSID = TEST_BSSID;
+        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY;
+        return wifiConfiguration;
+    }
+
+    private WifiNetworkAgentSpecifier createDefaultNetworkAgentSpecifier() {
+        return new WifiNetworkAgentSpecifier(createDefaultWifiConfiguration(), TEST_UID);
+    }
+
+}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
new file mode 100644
index 0000000..8980ddb
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
@@ -0,0 +1,481 @@
+/*
+ * 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.net.wifi;
+
+import static android.os.PatternMatcher.PATTERN_LITERAL;
+import static android.os.PatternMatcher.PATTERN_PREFIX;
+import static android.os.PatternMatcher.PATTERN_SIMPLE_GLOB;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.MacAddress;
+import android.net.NetworkSpecifier;
+import android.os.PatternMatcher;
+import android.os.Process;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.WifiNetworkConfigBuilder}.
+ */
+@SmallTest
+public class WifiNetworkConfigBuilderTest {
+    private static final String TEST_SSID = "Test123";
+    private static final String TEST_BSSID_OUI_BASE_ADDRESS = "12:12:12:00:00:00";
+    private static final String TEST_BSSID_OUI_MASK = "ff:ff:ff:00:00:00";
+    private static final String TEST_BSSID = "12:12:12:12:12:12";
+    private static final String TEST_PRESHARED_KEY = "Test123";
+
+    /**
+     * Validate correctness of WifiNetworkSpecifier object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for open network with SSID pattern.
+     */
+    @Test
+    public void testWifiNetworkSpecifierBuilderForOpenNetworkWithSsidPattern() {
+        NetworkSpecifier specifier = new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_PREFIX))
+                .buildNetworkSpecifier();
+
+        assertTrue(specifier instanceof WifiNetworkSpecifier);
+        WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier;
+
+        assertEquals(Process.myUid(), wifiNetworkSpecifier.requestorUid);
+        assertEquals(TEST_SSID, wifiNetworkSpecifier.ssidPatternMatcher.getPath());
+        assertEquals(PATTERN_PREFIX, wifiNetworkSpecifier.ssidPatternMatcher.getType());
+        assertEquals(MacAddress.ALL_ZEROS_ADDRESS, wifiNetworkSpecifier.bssidPatternMatcher.first);
+        assertEquals(MacAddress.ALL_ZEROS_ADDRESS, wifiNetworkSpecifier.bssidPatternMatcher.second);
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.NONE));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedProtocols
+                .get(WifiConfiguration.Protocol.RSN));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedAuthAlgorithms
+                .get(WifiConfiguration.AuthAlgorithm.OPEN));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedPairwiseCiphers
+                .get(WifiConfiguration.PairwiseCipher.CCMP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.CCMP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.TKIP));
+    }
+
+    /**
+     * Validate correctness of WifiNetworkSpecifier object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for WPA_PSK network with BSSID
+     * pattern.
+     */
+    @Test
+    public void testWifiNetworkSpecifierBuilderForWpaPskNetworkWithBssidPattern() {
+        NetworkSpecifier specifier = new WifiNetworkConfigBuilder()
+                .setBssidPattern(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK))
+                .setPskPassphrase(TEST_PRESHARED_KEY)
+                .buildNetworkSpecifier();
+
+        assertTrue(specifier instanceof WifiNetworkSpecifier);
+        WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier;
+
+        assertEquals(".*", wifiNetworkSpecifier.ssidPatternMatcher.getPath());
+        assertEquals(PATTERN_SIMPLE_GLOB, wifiNetworkSpecifier.ssidPatternMatcher.getType());
+        assertEquals(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                wifiNetworkSpecifier.bssidPatternMatcher.first);
+        assertEquals(MacAddress.fromString(TEST_BSSID_OUI_MASK),
+                wifiNetworkSpecifier.bssidPatternMatcher.second);
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.WPA_PSK));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedProtocols
+                .get(WifiConfiguration.Protocol.RSN));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedAuthAlgorithms
+                .get(WifiConfiguration.AuthAlgorithm.OPEN));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedPairwiseCiphers
+                .get(WifiConfiguration.PairwiseCipher.CCMP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.CCMP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.TKIP));
+        assertEquals("\"" + TEST_PRESHARED_KEY + "\"",
+                wifiNetworkSpecifier.wifiConfiguration.preSharedKey);
+    }
+
+    /**
+     * Validate correctness of WifiNetworkSpecifier object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} for WPA_EAP network with
+     * SSID and BSSID pattern.
+     */
+    @Test
+    public void testWifiNetworkSpecifierBuilderForEnterpriseHiddenNetworkWithSsidAndBssid() {
+        WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+        enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+        enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.GTC);
+
+        NetworkSpecifier specifier = new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setBssid(MacAddress.fromString(TEST_BSSID))
+                .setEnterpriseConfig(enterpriseConfig)
+                .setIsHiddenSsid()
+                .buildNetworkSpecifier();
+
+        assertTrue(specifier instanceof WifiNetworkSpecifier);
+        WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier;
+
+        assertEquals(TEST_SSID, wifiNetworkSpecifier.ssidPatternMatcher.getPath());
+        assertEquals(PATTERN_LITERAL, wifiNetworkSpecifier.ssidPatternMatcher.getType());
+        assertEquals(MacAddress.fromString(TEST_BSSID),
+                wifiNetworkSpecifier.bssidPatternMatcher.first);
+        assertEquals(MacAddress.BROADCAST_ADDRESS,
+                wifiNetworkSpecifier.bssidPatternMatcher.second);
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.WPA_EAP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.IEEE8021X));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedProtocols
+                .get(WifiConfiguration.Protocol.RSN));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedAuthAlgorithms
+                .get(WifiConfiguration.AuthAlgorithm.OPEN));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedPairwiseCiphers
+                .get(WifiConfiguration.PairwiseCipher.CCMP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.CCMP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers
+                .get(WifiConfiguration.GroupCipher.TKIP));
+        assertTrue(wifiNetworkSpecifier.wifiConfiguration.hiddenSSID);
+        assertEquals(enterpriseConfig.getEapMethod(),
+                wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig.getEapMethod());
+        assertEquals(enterpriseConfig.getPhase2Method(),
+                wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig.getPhase2Method());
+    }
+
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#setSsid(String)} throws an exception
+     * when the string is not Unicode.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testSetSsidWithNonUnicodeString() {
+        new WifiNetworkConfigBuilder()
+                .setSsid("\ud800")
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} throws an exception
+     * when the string is not ASCII encodable.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testSetPskPassphraseWithNonAsciiString() {
+        new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setPskPassphrase("salvē")
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when neither SSID nor BSSID patterns were set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithNoSsidAndBssidPattern() {
+        new WifiNetworkConfigBuilder().buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when match-all SSID pattern is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMatchAllSsidPattern1() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB))
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when match-all SSID pattern is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMatchAllSsidPattern2() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(".*", PatternMatcher.PATTERN_ADVANCED_GLOB))
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when match-all SSID pattern is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMatchAllSsidPattern3() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher("", PatternMatcher.PATTERN_PREFIX))
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when match-all BSSID pattern is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMatchAllBssidPattern() {
+        new WifiNetworkConfigBuilder()
+                .setBssidPattern(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS)
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when match-none SSID pattern is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher("", PatternMatcher.PATTERN_LITERAL))
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when match-none BSSID pattern is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern() {
+        new WifiNetworkConfigBuilder()
+                .setBssidPattern(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS)
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when SSID pattern is set for hidden network.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithBssidMatchPatternForHiddenNetwork() {
+        new WifiNetworkConfigBuilder()
+                .setBssidPattern(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                        MacAddress.fromString(TEST_BSSID_OUI_MASK))
+                .setIsHiddenSsid()
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when both {@link WifiNetworkConfigBuilder#setPskPassphrase(String)} and
+     * {@link WifiNetworkConfigBuilder#setEnterpriseConfig(WifiEnterpriseConfig)} are invoked.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithBothPskPassphraseAndEnterpriseConfig() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+                .setPskPassphrase(TEST_PRESHARED_KEY)
+                .setEnterpriseConfig(new WifiEnterpriseConfig())
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when SSID pattern is set for hidden network.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithSsidMatchPatternForHiddenNetwork() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PatternMatcher.PATTERN_PREFIX))
+                .setIsHiddenSsid()
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setIsAppInteractionRequired()} is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithRequiredAppInteraction() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+                .setIsAppInteractionRequired()
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setIsUserInteractionRequired()} is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithRequiredUserInteraction() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+                .setIsUserInteractionRequired()
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setPriority(int)} is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithSetPriority() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+                .setPriority(4)
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setIsMetered()} is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMetered() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_LITERAL))
+                .setIsMetered()
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for Open network which requires
+     * app interaction.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForOpenNetworkWithReqAppInteraction() {
+        WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setIsAppInteractionRequired()
+                .buildNetworkSuggestion();
+
+        assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+        assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.NONE));
+        assertTrue(suggestion.isAppInteractionRequired);
+        assertFalse(suggestion.isUserInteractionRequired);
+        assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE,
+                suggestion.wifiConfiguration.meteredOverride);
+        assertEquals(-1, suggestion.wifiConfiguration.priority);
+    }
+
+    /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for WPA_EAP network which requires
+     * app interaction and has a priority of zero set.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForWpaEapNetworkWithPriorityAndReqAppInteraction() {
+        WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setPskPassphrase(TEST_PRESHARED_KEY)
+                .setIsAppInteractionRequired()
+                .setPriority(0)
+                .buildNetworkSuggestion();
+
+        assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+        assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.WPA_PSK));
+        assertEquals("\"" + TEST_PRESHARED_KEY + "\"",
+                suggestion.wifiConfiguration.preSharedKey);
+        assertTrue(suggestion.isAppInteractionRequired);
+        assertFalse(suggestion.isUserInteractionRequired);
+        assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE,
+                suggestion.wifiConfiguration.meteredOverride);
+        assertEquals(0, suggestion.wifiConfiguration.priority);
+    }
+
+    /**
+     * Validate correctness of WifiNetworkSuggestion object created by
+     * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for WPA_PSK network which requires
+     * user interaction and is metered.
+     */
+    @Test
+    public void testWifiNetworkSuggestionBuilderForWpaPskNetworkWithMeteredAndReqUserInteraction() {
+        WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setPskPassphrase(TEST_PRESHARED_KEY)
+                .setIsUserInteractionRequired()
+                .setIsMetered()
+                .buildNetworkSuggestion();
+
+        assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+        assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
+                .get(WifiConfiguration.KeyMgmt.WPA_PSK));
+        assertEquals("\"" + TEST_PRESHARED_KEY + "\"",
+                suggestion.wifiConfiguration.preSharedKey);
+        assertFalse(suggestion.isAppInteractionRequired);
+        assertTrue(suggestion.isUserInteractionRequired);
+        assertEquals(WifiConfiguration.METERED_OVERRIDE_METERED,
+                suggestion.wifiConfiguration.meteredOverride);
+        assertEquals(-1, suggestion.wifiConfiguration.priority);
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setSsidPattern(PatternMatcher)} is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSuggestionBuilderWithSsidPattern() {
+        new WifiNetworkConfigBuilder()
+                .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_PREFIX))
+                .buildNetworkSuggestion();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSuggestionBuilderWithBssidPattern() {
+        new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setBssidPattern(MacAddress.fromString(TEST_BSSID),
+                        MacAddress.fromString(TEST_BSSID))
+                .buildNetworkSuggestion();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setBssidPattern(MacAddress, MacAddress)} is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSuggestionBuilderWithBssid() {
+        new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setBssid(MacAddress.fromString(TEST_BSSID))
+                .buildNetworkSuggestion();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setSsid(String)} is not set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSuggestionBuilderWithNoSsid() {
+        new WifiNetworkConfigBuilder()
+                .buildNetworkSuggestion();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#setPriority(int)} throws an exception
+     * when the value is negative.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testWifiNetworkSuggestionBuilderWithInvalidPriority() {
+        new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setPriority(-1)
+                .buildNetworkSuggestion();
+    }
+}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
new file mode 100644
index 0000000..856f0c7
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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.net.wifi;
+
+import static android.os.PatternMatcher.PATTERN_LITERAL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.MacAddress;
+import android.net.MatchAllNetworkSpecifier;
+import android.os.Parcel;
+import android.os.PatternMatcher;
+import android.support.test.filters.SmallTest;
+import android.util.Pair;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.WifiNetworkSpecifier}.
+ */
+@SmallTest
+public class WifiNetworkSpecifierTest {
+    private static final int TEST_UID = 5;
+    private static final String TEST_SSID = "Test123";
+    private static final String TEST_BSSID_OUI_BASE_ADDRESS = "12:12:12:00:00:00";
+    private static final String TEST_BSSID_OUI_MASK = "ff:ff:ff:00:00:00";
+    private static final String TEST_PRESHARED_KEY = "\"Test123\"";
+
+    /**
+     * Validate that parcel marshalling/unmarshalling works
+     */
+    @Test
+    public void testWifiNetworkSpecifierParcel() {
+        WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY;
+        WifiNetworkSpecifier specifier =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration,
+                        TEST_UID);
+
+        Parcel parcelW = Parcel.obtain();
+        specifier.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        WifiNetworkSpecifier parcelSpecifier =
+                WifiNetworkSpecifier.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(specifier, parcelSpecifier);
+    }
+
+    /**
+     * Validate NetworkSpecifier matching.
+     * a) Create a network specifier for WPA_PSK network
+     * b) Ensure that the specifier matches {@code null} and {@link MatchAllNetworkSpecifier}
+     * specifiers.
+     */
+    @Test
+    public void testWifiNetworkSpecifierSatisfiesNullAndAllMatch() {
+        WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY;
+        WifiNetworkSpecifier specifier =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration,
+                        TEST_UID);
+
+        assertTrue(specifier.satisfiedBy(null));
+        assertTrue(specifier.satisfiedBy(new MatchAllNetworkSpecifier()));
+    }
+
+    /**
+     * Validate NetworkSpecifier matching.
+     * a) Create network specifier 1 for WPA_PSK network
+     * b) Create network specifier 2 with the same params as specifier 1.
+     * c) Ensure that the specifier 2 is satisfied by specifier 1.
+     */
+    @Test
+    public void testWifiNetworkSpecifierSatisfiesSame() {
+        WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY;
+
+        WifiNetworkSpecifier specifier1 =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration,
+                        TEST_UID);
+
+        WifiNetworkSpecifier specifier2 =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration,
+                        TEST_UID);
+
+        assertTrue(specifier2.satisfiedBy(specifier1));
+    }
+
+    /**
+     * Validate NetworkSpecifier matching.
+     * a) Create network specifier 1 for WPA_PSK network
+     * b) Create network specifier 2 with different key mgmt params.
+     * c) Ensure that the specifier 2 is not satisfied by specifier 1.
+     */
+    @Test
+    public void testWifiNetworkSpecifierDoesNotSatisfyWhenKeyMgmtDifferent() {
+        WifiConfiguration wifiConfiguration1 = new WifiConfiguration();
+        wifiConfiguration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        wifiConfiguration1.preSharedKey = TEST_PRESHARED_KEY;
+
+        WifiNetworkSpecifier specifier1 =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration1,
+                        TEST_UID);
+
+        WifiConfiguration wifiConfiguration2 = new WifiConfiguration();
+        wifiConfiguration2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkSpecifier specifier2 =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration2,
+                        TEST_UID);
+
+        assertFalse(specifier2.satisfiedBy(specifier1));
+    }
+
+    /**
+     * Validate NetworkSpecifier matching.
+     * a) Create network specifier 1 for WPA_PSK network
+     * b) Create network specifier 2 with different SSID pattern.
+     * c) Ensure that the specifier 2 is not satisfied by specifier 1.
+     */
+    @Test
+    public void testWifiNetworkSpecifierDoesNotSatisfyWhenSsidDifferent() {
+        WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY;
+
+        WifiNetworkSpecifier specifier1 =
+                new WifiNetworkSpecifier(new PatternMatcher("", PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration,
+                        TEST_UID);
+
+        WifiNetworkSpecifier specifier2 =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration,
+                        TEST_UID);
+
+        assertFalse(specifier2.satisfiedBy(specifier1));
+    }
+
+    /**
+     * Validate NetworkSpecifier matching.
+     * a) Create network specifier 1 for WPA_PSK network
+     * b) Create network specifier 2 with different BSSID pattern.
+     * c) Ensure that the specifier 2 is not satisfied by specifier 1.
+     */
+    @Test
+    public void testWifiNetworkSpecifierDoesNotSatisfyWhenBssidDifferent() {
+        WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY;
+
+        WifiNetworkSpecifier specifier1 =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+                        wifiConfiguration,
+                        TEST_UID);
+
+        WifiNetworkSpecifier specifier2 =
+                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+                        Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS),
+                        wifiConfiguration,
+                        TEST_UID);
+
+        assertFalse(specifier2.satisfiedBy(specifier1));
+    }
+}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
new file mode 100644
index 0000000..6bab60d
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.net.wifi;
+
+import static org.junit.Assert.*;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.WifiNetworkSuggestion}.
+ */
+@SmallTest
+public class WifiNetworkSuggestionTest {
+    private static final String TEST_SSID = "\"Test123\"";
+    private static final String TEST_SSID_1 = "\"Test1234\"";
+
+    /**
+     * Check that parcel marshalling/unmarshalling works
+     */
+    @Test
+    public void testWifiNetworkSuggestionParcel() {
+        WifiConfiguration configuration = new WifiConfiguration();
+        configuration.SSID = TEST_SSID;
+        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkSuggestion suggestion =
+                new WifiNetworkSuggestion(configuration, false, true, 0);
+
+        Parcel parcelW = Parcel.obtain();
+        suggestion.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        WifiNetworkSuggestion parcelSuggestion =
+                WifiNetworkSuggestion.CREATOR.createFromParcel(parcelR);
+
+        // Two suggestion objects are considered equal if they point to the same network (i.e same
+        // SSID + keyMgmt + same UID). |isAppInteractionRequired| & |isUserInteractionRequired| are
+        // not considered for equality and hence needs to be checked for explicitly below.
+        assertEquals(suggestion, parcelSuggestion);
+        assertEquals(suggestion.isAppInteractionRequired,
+                parcelSuggestion.isAppInteractionRequired);
+        assertEquals(suggestion.isUserInteractionRequired,
+                parcelSuggestion.isUserInteractionRequired);
+    }
+
+    /**
+     * Check NetworkSuggestion equals returns {@code true} for 2 network suggestions with the same
+     * SSID, key mgmt and UID.
+     */
+    @Test
+    public void testWifiNetworkSuggestionEqualsSame() {
+        WifiConfiguration configuration = new WifiConfiguration();
+        configuration.SSID = TEST_SSID;
+        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSuggestion suggestion =
+                new WifiNetworkSuggestion(configuration, true, false, 0);
+
+        WifiConfiguration configuration1 = new WifiConfiguration();
+        configuration1.SSID = TEST_SSID;
+        configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSuggestion suggestion1 =
+                new WifiNetworkSuggestion(configuration1, false, true, 0);
+
+        assertEquals(suggestion, suggestion1);
+    }
+
+    /**
+     * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
+     * key mgmt and UID, but different SSID.
+     */
+    @Test
+    public void testWifiNetworkSuggestionEqualsFailsWhenSsidIsDifferent() {
+        WifiConfiguration configuration = new WifiConfiguration();
+        configuration.SSID = TEST_SSID;
+        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkSuggestion suggestion =
+                new WifiNetworkSuggestion(configuration, false, false, 0);
+
+        WifiConfiguration configuration1 = new WifiConfiguration();
+        configuration1.SSID = TEST_SSID_1;
+        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkSuggestion suggestion1 =
+                new WifiNetworkSuggestion(configuration1, false, false, 0);
+
+        assertNotEquals(suggestion, suggestion1);
+    }
+
+    /**
+     * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
+     * SSID and UID, but different key mgmt.
+     */
+    @Test
+    public void testWifiNetworkSuggestionEqualsFailsWhenKeyMgmtIsDifferent() {
+        WifiConfiguration configuration = new WifiConfiguration();
+        configuration.SSID = TEST_SSID;
+        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkSuggestion suggestion =
+                new WifiNetworkSuggestion(configuration, false, false, 0);
+
+        WifiConfiguration configuration1 = new WifiConfiguration();
+        configuration1.SSID = TEST_SSID;
+        configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+        WifiNetworkSuggestion suggestion1 =
+                new WifiNetworkSuggestion(configuration1, false, false, 0);
+
+        assertNotEquals(suggestion, suggestion1);
+    }
+
+    /**
+     * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
+     * SSID and key mgmt, but different UID.
+     */
+    @Test
+    public void testWifiNetworkSuggestionEqualsFailsWhenUidIsDifferent() {
+        WifiConfiguration configuration = new WifiConfiguration();
+        configuration.SSID = TEST_SSID;
+        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkSuggestion suggestion =
+                new WifiNetworkSuggestion(configuration, false, false, 0);
+
+        WifiNetworkSuggestion suggestion1 =
+                new WifiNetworkSuggestion(configuration, false, false, 1);
+
+        assertNotEquals(suggestion, suggestion1);
+    }
+}
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);
+    }
+
 }