Merge "Use MediaParceledListSlice in ControllerCallbackLink"
diff --git a/api/current.txt b/api/current.txt
index efd805c..30599a5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3941,7 +3941,8 @@
     method public boolean isBackgroundRestricted();
     method @Deprecated public boolean isInLockTaskMode();
     method public boolean isLowRamDevice();
-    method public static boolean isRunningInTestHarness();
+    method @Deprecated public static boolean isRunningInTestHarness();
+    method public static boolean isRunningInUserTestHarness();
     method public static boolean isUserAMonkey();
     method @RequiresPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES) public void killBackgroundProcesses(String);
     method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int);
@@ -11652,6 +11653,7 @@
     field public static final String FEATURE_SCREEN_LANDSCAPE = "android.hardware.screen.landscape";
     field public static final String FEATURE_SCREEN_PORTRAIT = "android.hardware.screen.portrait";
     field public static final String FEATURE_SECURELY_REMOVES_USERS = "android.software.securely_removes_users";
+    field public static final String FEATURE_SECURE_LOCK_SCREEN = "android.software.secure_lock_screen";
     field public static final String FEATURE_SENSOR_ACCELEROMETER = "android.hardware.sensor.accelerometer";
     field public static final String FEATURE_SENSOR_AMBIENT_TEMPERATURE = "android.hardware.sensor.ambient_temperature";
     field public static final String FEATURE_SENSOR_BAROMETER = "android.hardware.sensor.barometer";
@@ -26591,12 +26593,9 @@
     field public static final int SUCCESS = 0; // 0x0
   }
 
-  public static final class AudioEffect.Descriptor implements android.os.Parcelable {
+  public static class AudioEffect.Descriptor {
     ctor public AudioEffect.Descriptor();
     ctor public AudioEffect.Descriptor(String, String, String, String, String);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.media.audiofx.AudioEffect.Descriptor> CREATOR;
     field public String connectMode;
     field public String implementor;
     field public String name;
@@ -28578,6 +28577,28 @@
     field public int serverAddress;
   }
 
+  public final class DnsResolver {
+    method public static android.net.DnsResolver getInstance();
+    method public void query(@Nullable android.net.Network, @NonNull byte[], int, @NonNull android.os.Handler, @NonNull android.net.DnsResolver.RawAnswerListener) throws android.system.ErrnoException;
+    method public void query(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull android.os.Handler, @NonNull android.net.DnsResolver.RawAnswerListener) throws android.system.ErrnoException;
+    method public void query(@Nullable android.net.Network, @NonNull String, int, @NonNull android.os.Handler, @NonNull android.net.DnsResolver.InetAddressAnswerListener) throws android.system.ErrnoException;
+    field public static final int CLASS_IN = 1; // 0x1
+    field public static final int FLAG_EMPTY = 0; // 0x0
+    field public static final int FLAG_NO_CACHE_LOOKUP = 4; // 0x4
+    field public static final int FLAG_NO_CACHE_STORE = 2; // 0x2
+    field public static final int FLAG_NO_RETRY = 1; // 0x1
+    field public static final int TYPE_A = 1; // 0x1
+    field public static final int TYPE_AAAA = 28; // 0x1c
+  }
+
+  public static interface DnsResolver.InetAddressAnswerListener {
+    method public void onAnswer(@NonNull java.util.List<java.net.InetAddress>);
+  }
+
+  public static interface DnsResolver.RawAnswerListener {
+    method public void onAnswer(@Nullable byte[]);
+  }
+
   public class InetAddresses {
     method public static boolean isNumericAddress(String);
     method public static java.net.InetAddress parseNumericAddress(String);
@@ -43613,6 +43634,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getSelfManagedPhoneAccounts();
     method public android.telecom.PhoneAccountHandle getSimCallManager();
     method public String getSystemDialerPackage();
+    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telecom.PhoneAccountHandle getUserSelectedOutgoingPhoneAccount();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String, android.telecom.PhoneAccountHandle);
@@ -49691,6 +49713,7 @@
   }
 
   public class Surface implements android.os.Parcelable {
+    ctor public Surface(android.view.SurfaceControl);
     ctor public Surface(android.graphics.SurfaceTexture);
     method public int describeContents();
     method public boolean isValid();
@@ -49713,6 +49736,38 @@
     ctor public Surface.OutOfResourcesException(String);
   }
 
+  public final class SurfaceControl implements android.os.Parcelable {
+    method public int describeContents();
+    method public boolean isValid();
+    method public void readFromParcel(android.os.Parcel);
+    method public void release();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.SurfaceControl> CREATOR;
+  }
+
+  public static class SurfaceControl.Builder {
+    ctor public SurfaceControl.Builder();
+    method public android.view.SurfaceControl build();
+    method public android.view.SurfaceControl.Builder setBufferSize(@IntRange(from=0) int, @IntRange(from=0) int);
+    method @NonNull public android.view.SurfaceControl.Builder setFormat(int);
+    method public android.view.SurfaceControl.Builder setName(String);
+    method @NonNull public android.view.SurfaceControl.Builder setOpaque(boolean);
+    method @NonNull public android.view.SurfaceControl.Builder setParent(@Nullable android.view.SurfaceControl);
+  }
+
+  public static class SurfaceControl.Transaction implements java.io.Closeable {
+    ctor public SurfaceControl.Transaction();
+    method public void apply();
+    method public void close();
+    method @NonNull public android.view.SurfaceControl.Transaction merge(@NonNull android.view.SurfaceControl.Transaction);
+    method @NonNull public android.view.SurfaceControl.Transaction reparent(@NonNull android.view.SurfaceControl, @Nullable android.view.SurfaceControl);
+    method @NonNull public android.view.SurfaceControl.Transaction setAlpha(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0, to=1.0) float);
+    method @NonNull public android.view.SurfaceControl.Transaction setBufferSize(@NonNull android.view.SurfaceControl, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int);
+    method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int);
+    method @NonNull public android.view.SurfaceControl.Transaction setVisibility(@NonNull android.view.SurfaceControl, boolean);
+  }
+
   public interface SurfaceHolder {
     method public void addCallback(android.view.SurfaceHolder.Callback);
     method public android.view.Surface getSurface();
@@ -49757,6 +49812,7 @@
     ctor public SurfaceView(android.content.Context, android.util.AttributeSet, int, int);
     method public boolean gatherTransparentRegion(android.graphics.Region);
     method public android.view.SurfaceHolder getHolder();
+    method public android.view.SurfaceControl getSurfaceControl();
     method public void setSecure(boolean);
     method public void setZOrderMediaOverlay(boolean);
     method public void setZOrderOnTop(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index 2088c14..cdd2d80 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1136,19 +1136,46 @@
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean enableNoAutoConnect();
     method public boolean isBleScanAlwaysAvailable();
     method public boolean isLeEnabled();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean registerMetadataListener(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothAdapter.MetadataListener, android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean unregisterMetadataListener(android.bluetooth.BluetoothDevice);
     field public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
     field public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
   }
 
+  public abstract class BluetoothAdapter.MetadataListener {
+    ctor public BluetoothAdapter.MetadataListener();
+    method public void onMetadataChanged(android.bluetooth.BluetoothDevice, int, String);
+  }
+
   public final class BluetoothDevice implements android.os.Parcelable {
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelBondProcess();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public String getMetadata(int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean removeBond();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, String);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int);
     field public static final int ACCESS_ALLOWED = 1; // 0x1
     field public static final int ACCESS_REJECTED = 2; // 0x2
     field public static final int ACCESS_UNKNOWN = 0; // 0x0
+    field public static final int METADATA_COMPANION_APP = 4; // 0x4
+    field public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; // 0x10
+    field public static final int METADATA_HARDWARE_VERSION = 3; // 0x3
+    field public static final int METADATA_IS_UNTHETHERED_HEADSET = 6; // 0x6
+    field public static final int METADATA_MAIN_ICON = 5; // 0x5
+    field public static final int METADATA_MANUFACTURER_NAME = 0; // 0x0
+    field public static final int METADATA_MAX_LENGTH = 2048; // 0x800
+    field public static final int METADATA_MODEL_NAME = 1; // 0x1
+    field public static final int METADATA_SOFTWARE_VERSION = 2; // 0x2
+    field public static final int METADATA_UNTHETHERED_CASE_BATTERY = 12; // 0xc
+    field public static final int METADATA_UNTHETHERED_CASE_CHARGING = 15; // 0xf
+    field public static final int METADATA_UNTHETHERED_CASE_ICON = 9; // 0x9
+    field public static final int METADATA_UNTHETHERED_LEFT_BATTERY = 10; // 0xa
+    field public static final int METADATA_UNTHETHERED_LEFT_CHARGING = 13; // 0xd
+    field public static final int METADATA_UNTHETHERED_LEFT_ICON = 7; // 0x7
+    field public static final int METADATA_UNTHETHERED_RIGHT_BATTERY = 11; // 0xb
+    field public static final int METADATA_UNTHETHERED_RIGHT_CHARGING = 14; // 0xe
+    field public static final int METADATA_UNTHETHERED_RIGHT_ICON = 8; // 0x8
   }
 
   public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
@@ -6164,12 +6191,15 @@
     method public abstract int onSwitchToSubscription(int, @Nullable String, boolean);
     method public abstract int onUpdateSubscriptionNickname(int, String, String);
     field public static final String ACTION_BIND_CARRIER_PROVISIONING_SERVICE = "android.service.euicc.action.BIND_CARRIER_PROVISIONING_SERVICE";
+    field public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED = "android.service.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
     field public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
     field public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
+    field public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED = "android.service.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
     field @Deprecated public static final String ACTION_RESOLVE_CONFIRMATION_CODE = "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
     field public static final String ACTION_RESOLVE_DEACTIVATE_SIM = "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM";
     field public static final String ACTION_RESOLVE_NO_PRIVILEGES = "android.service.euicc.action.RESOLVE_NO_PRIVILEGES";
     field public static final String ACTION_RESOLVE_RESOLVABLE_ERRORS = "android.service.euicc.action.RESOLVE_RESOLVABLE_ERRORS";
+    field public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED = "android.service.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
     field public static final String CATEGORY_EUICC_UI = "android.service.euicc.category.EUICC_UI";
     field public static final String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService";
     field public static final String EXTRA_RESOLUTION_ALLOW_POLICY_RULES = "android.service.euicc.extra.RESOLUTION_ALLOW_POLICY_RULES";
@@ -6747,6 +6777,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
     method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.WRITE_SECURE_SETTINGS}) public boolean setDefaultDialer(String);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(android.telecom.PhoneAccountHandle);
     field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
     field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
     field public static final String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
@@ -7912,9 +7943,12 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public int getOtaStatus();
+    field public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
     field @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public static final String ACTION_OTA_STATUS_CHANGED = "android.telephony.euicc.action.OTA_STATUS_CHANGED";
     field public static final String ACTION_PROFILE_SELECTION = "android.telephony.euicc.action.PROFILE_SELECTION";
     field public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
+    field public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
+    field public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
     field public static final int EUICC_ACTIVATION_TYPE_BACKUP = 2; // 0x2
     field public static final int EUICC_ACTIVATION_TYPE_DEFAULT = 1; // 0x1
     field public static final int EUICC_ACTIVATION_TYPE_TRANSFER = 3; // 0x3
@@ -7925,7 +7959,10 @@
     field public static final int EUICC_OTA_SUCCEEDED = 3; // 0x3
     field public static final String EXTRA_ACTIVATION_TYPE = "android.telephony.euicc.extra.ACTIVATION_TYPE";
     field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS";
+    field public static final String EXTRA_ENABLE_SUBSCRIPTION = "android.telephony.euicc.extra.ENABLE_SUBSCRIPTION";
     field public static final String EXTRA_FORCE_PROVISION = "android.telephony.euicc.extra.FORCE_PROVISION";
+    field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.euicc.extra.SUBSCRIPTION_ID";
+    field public static final String EXTRA_SUBSCRIPTION_NICKNAME = "android.telephony.euicc.extra.SUBSCRIPTION_NICKNAME";
   }
 
   @IntDef(prefix={"EUICC_OTA_"}, value={android.telephony.euicc.EuiccManager.EUICC_OTA_IN_PROGRESS, android.telephony.euicc.EuiccManager.EUICC_OTA_FAILED, android.telephony.euicc.EuiccManager.EUICC_OTA_SUCCEEDED, android.telephony.euicc.EuiccManager.EUICC_OTA_NOT_NEEDED, android.telephony.euicc.EuiccManager.EUICC_OTA_STATUS_UNAVAILABLE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccManager.OtaStatus {
diff --git a/api/test-current.txt b/api/test-current.txt
index d089831..eca7a7a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -772,6 +772,11 @@
     field public static final java.util.UUID EFFECT_TYPE_NULL;
   }
 
+  public static class AudioEffect.Descriptor {
+    ctor public AudioEffect.Descriptor(android.os.Parcel);
+    method public void writeToParcel(android.os.Parcel);
+  }
+
   public static interface AudioEffect.OnParameterChangeListener {
     method public void onParameterChange(android.media.audiofx.AudioEffect, int, byte[], byte[]);
   }
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 4174ad7..1b7fbfe 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -23,8 +23,10 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.telecom.Log;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
+import android.text.TextUtils;
 
 import com.android.internal.os.BaseCommand;
 import com.android.internal.telecom.ITelecomService;
@@ -45,6 +47,8 @@
     private static final String COMMAND_SET_PHONE_ACCOUNT_ENABLED = "set-phone-account-enabled";
     private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled";
     private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account";
+    private static final String COMMAND_SET_USER_SELECTED_OUTGOING_PHONE_ACCOUNT =
+            "set-user-selected-outgoing-phone-account";
     private static final String COMMAND_REGISTER_SIM_PHONE_ACCOUNT = "register-sim-phone-account";
     private static final String COMMAND_SET_TEST_CALL_REDIRECTION_APP = "set-test-call-redirection-app";
     private static final String COMMAND_SET_TEST_CALL_SCREENING_APP = "set-test-call-screening-app";
@@ -70,6 +74,8 @@
                 + "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n"
                 + "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n"
                 + "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n"
+                + "usage: telecom set-user-selected-outgoing-phone-account <COMPONENT> <ID> "
+                + "<USER_SN>\n"
                 + "usage: telecom set-test-call-redirection-app <PACKAGE>\n"
                 + "usage: telecom set-test-call-screening-app <PACKAGE>\n"
                 + "usage: telecom set-test-auto-mode-app <PACKAGE>\n"
@@ -104,16 +110,18 @@
         mTelecomService = ITelecomService.Stub.asInterface(
                 ServiceManager.getService(Context.TELECOM_SERVICE));
         if (mTelecomService == null) {
+            Log.w(this, "onRun: Can't access telecom manager.");
             showError("Error: Could not access the Telecom Manager. Is the system running?");
             return;
         }
         mUserManager = IUserManager.Stub
                 .asInterface(ServiceManager.getService(Context.USER_SERVICE));
         if (mUserManager == null) {
+            Log.w(this, "onRun: Can't access user manager.");
             showError("Error: Could not access the User Manager. Is the system running?");
             return;
         }
-
+        Log.i(this, "onRun: parsing command.");
         String command = nextArgRequired();
         switch (command) {
             case COMMAND_SET_PHONE_ACCOUNT_ENABLED:
@@ -143,6 +151,9 @@
             case COMMAND_REGISTER_SIM_PHONE_ACCOUNT:
                 runRegisterSimPhoneAccount();
                 break;
+            case COMMAND_SET_USER_SELECTED_OUTGOING_PHONE_ACCOUNT:
+                runSetUserSelectedOutgoingPhoneAccount();
+                break;
             case COMMAND_UNREGISTER_PHONE_ACCOUNT:
                 runUnregisterPhoneAccount();
                 break;
@@ -159,6 +170,7 @@
                 runWaitOnHandler();
                 break;
             default:
+                Log.w(this, "onRun: unknown command: %s", command);
                 throw new IllegalArgumentException ("unknown command '" + command + "'");
         }
     }
@@ -227,6 +239,13 @@
         mTelecomService.setTestPhoneAcctSuggestionComponent(componentName);
     }
 
+    private void runSetUserSelectedOutgoingPhoneAccount() throws RemoteException {
+        Log.i(this, "runSetUserSelectedOutgoingPhoneAccount");
+        final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
+        mTelecomService.setUserSelectedOutgoingPhoneAccount(handle);
+        System.out.println("Success - " + handle + " set as default outgoing account.");
+    }
+
     private void runUnregisterPhoneAccount() throws RemoteException {
         final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
         mTelecomService.unregisterPhoneAccount(handle);
@@ -256,7 +275,10 @@
 
     }
 
-    private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException{
+    private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException {
+        if (TextUtils.isEmpty(mArgs.peekNextArg())) {
+            return null;
+        }
         final ComponentName component = parseComponentName(nextArgRequired());
         final String accountId = nextArgRequired();
         final String userSnInStr = nextArgRequired();
@@ -265,6 +287,7 @@
             final int userSn = Integer.parseInt(userSnInStr);
             userHandle = UserHandle.of(mUserManager.getUserHandle(userSn));
         } catch (NumberFormatException ex) {
+            Log.w(this, "getPhoneAccountHandleFromArgs - invalid user %s", userSnInStr);
             throw new IllegalArgumentException ("Invalid user serial number " + userSnInStr);
         }
         return new PhoneAccountHandle(component, accountId, userHandle);
@@ -277,4 +300,4 @@
         }
         return cn;
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e0b8d78..1045b7a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3529,12 +3529,32 @@
 
     /**
      * Returns "true" if device is running in a test harness.
+     *
+     * @deprecated this method is false for all user builds. Users looking to check if their device
+     * is running in a device farm should see {@link #isRunningInUserTestHarness()}.
      */
+    @Deprecated
     public static boolean isRunningInTestHarness() {
         return SystemProperties.getBoolean("ro.test_harness", false);
     }
 
     /**
+     * Returns "true" if the device is running in Test Harness Mode.
+     *
+     * <p>Test Harness Mode is a feature that allows devices to run without human interaction in a
+     * device farm/testing harness (such as Firebase Test Lab). You should check this method if you
+     * want your app to behave differently when running in a test harness to skip setup screens that
+     * would impede UI testing. e.g. a keyboard application that has a full screen setup page for
+     * the first time it is launched.
+     *
+     * <p>Note that you should <em>not</em> use this to determine whether or not your app is running
+     * an instrumentation test, as it is not set for a standard device running a test.
+     */
+    public static boolean isRunningInUserTestHarness() {
+        return SystemProperties.getBoolean("persist.sys.test_harness", false);
+    }
+
+    /**
      * Unsupported compiled sdk warning should always be shown for the intput activity
      * even in cases where the system would normally not show the warning. E.g. when running in a
      * test harness.
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index ab8f234..4d3711a 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -328,7 +328,7 @@
                 }
             } else {
                 mTmpTransaction.reparent(mRootSurfaceControl,
-                        mSurfaceView.getSurfaceControl().getHandle()).apply();
+                        mSurfaceView.getSurfaceControl()).apply();
             }
 
             if (mVirtualDisplay != null) {
@@ -390,7 +390,7 @@
                 .build();
 
         try {
-            wm.reparentDisplayContent(displayId, mRootSurfaceControl.getHandle());
+            wm.reparentDisplayContent(displayId, mRootSurfaceControl);
             wm.dontOverrideDisplayInfo(displayId);
             if (mSingleTaskInstance) {
                 mActivityTaskManager.setDisplayToSingleTaskInstance(displayId);
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 1ae0f52..75c9054 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -19,6 +19,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -124,6 +125,7 @@
      *
      * @return the intent for launching the activity or null if no password is required.
      **/
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public Intent createConfirmDeviceCredentialIntent(CharSequence title, CharSequence description) {
         if (!isDeviceSecure()) return null;
         Intent intent = new Intent(ACTION_CONFIRM_DEVICE_CREDENTIAL);
@@ -176,6 +178,7 @@
      * @throws IllegalStateException if the device has already been provisioned
      * @hide
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     @SystemApi
     public Intent createConfirmFactoryResetCredentialIntent(
             CharSequence title, CharSequence description, CharSequence alternateButtonLabel) {
@@ -231,6 +234,7 @@
      * secure notifications cannot be shown if {@code false}
      * @hide
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     @RequiresPermission(Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)
     @SystemApi
     public void setPrivateNotificationsAllowed(boolean allow) {
@@ -249,6 +253,7 @@
      * By default, private notifications are allowed.
      * @hide
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     @RequiresPermission(Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)
     @SystemApi
     public boolean getPrivateNotificationsAllowed() {
diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java
index 5b87de4..5f1a94c 100644
--- a/core/java/android/app/VrManager.java
+++ b/core/java/android/app/VrManager.java
@@ -215,19 +215,12 @@
     }
 
     /**
-     * Start VR Input method for the packageName in {@link ComponentName}.
-     * This method notifies InputMethodManagerService to use VR IME instead of
-     * regular phone IME.
-     * @param componentName ComponentName of a VR InputMethod that should be set as selected
-     * input by InputMethodManagerService.
+     * This method is not implemented.
+     *
+     * @param componentName not used
      */
     @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
     public void setVrInputMethod(ComponentName componentName) {
-        try {
-            mService.setVrInputMethod(componentName);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
     }
 
     /**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 86db99bf..7cc953c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2513,6 +2513,9 @@
      * requested quality constant (between the policy set here, the user's preference, and any other
      * considerations) is the one that is in effect.
      * <p>
+     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     * <p>
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
      * not, a security exception will be thrown.
@@ -2548,6 +2551,9 @@
      * returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
      * restrictions on the parent profile.
      *
+     * <p>Note: on devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature,
+     * the password is always treated as empty.
+     *
      * @param admin The name of the admin component to check, or {@code null} to aggregate
      * all admins.
      */
@@ -2580,6 +2586,9 @@
      * {@link #PASSWORD_QUALITY_ALPHANUMERIC}, or {@link #PASSWORD_QUALITY_COMPLEX} with
      * {@link #setPasswordQuality}.
      * <p>
+     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     * <p>
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
      * not, a security exception will be thrown.
@@ -2609,11 +2618,13 @@
      * restrictions on this user and its participating profiles. Restrictions on profiles that have
      * a separate challenge are not taken into account.
      *
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     *
      * <p>This method can be called on the {@link DevicePolicyManager} instance
      * returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
      * restrictions on the parent profile.
      *
-     * user and its profiles or a particular one.
      * @param admin The name of the admin component to check, or {@code null} to aggregate
      * all admins.
      */
@@ -2644,6 +2655,9 @@
      * setting this value. This constraint is only imposed if the administrator has also requested
      * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The default value is 0.
      * <p>
+     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     * <p>
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
      * not, a security exception will be thrown.
@@ -2678,6 +2692,9 @@
      * and only applies when the password quality is
      * {@link #PASSWORD_QUALITY_COMPLEX}.
      *
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     *
      * <p>This method can be called on the {@link DevicePolicyManager} instance
      * returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
      * restrictions on the parent profile.
@@ -2714,6 +2731,9 @@
      * setting this value. This constraint is only imposed if the administrator has also requested
      * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The default value is 0.
      * <p>
+     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     * <p>
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
      * not, a security exception will be thrown.
@@ -2748,6 +2768,9 @@
      * and only applies when the password quality is
      * {@link #PASSWORD_QUALITY_COMPLEX}.
      *
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     *
      * <p>This method can be called on the {@link DevicePolicyManager} instance
      * returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
      * restrictions on the parent profile.
@@ -2784,6 +2807,9 @@
      * only imposed if the administrator has also requested {@link #PASSWORD_QUALITY_COMPLEX} with
      * {@link #setPasswordQuality}. The default value is 1.
      * <p>
+     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     * <p>
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
      * not, a security exception will be thrown.
@@ -2818,6 +2844,9 @@
      * and only applies when the password quality is
      * {@link #PASSWORD_QUALITY_COMPLEX}.
      *
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     *
      * <p>This method can be called on the {@link DevicePolicyManager} instance
      * returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
      * restrictions on the parent profile.
@@ -2853,6 +2882,9 @@
      * setting this value. This constraint is only imposed if the administrator has also requested
      * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The default value is 1.
      * <p>
+     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     * <p>
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
      * not, a security exception will be thrown.
@@ -2887,6 +2919,9 @@
      * and only applies when the password quality is
      * {@link #PASSWORD_QUALITY_COMPLEX}.
      *
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     *
      * <p>This method can be called on the {@link DevicePolicyManager} instance
      * returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
      * restrictions on the parent profile.
@@ -2922,6 +2957,9 @@
      * only imposed if the administrator has also requested {@link #PASSWORD_QUALITY_COMPLEX} with
      * {@link #setPasswordQuality}. The default value is 1.
      * <p>
+     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     * <p>
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
      * not, a security exception will be thrown.
@@ -2955,6 +2993,9 @@
      * and only applies when the password quality is
      * {@link #PASSWORD_QUALITY_COMPLEX}.
      *
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     *
      * <p>This method can be called on the {@link DevicePolicyManager} instance
      * returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
      * restrictions on the parent profile.
@@ -2990,6 +3031,9 @@
      * setting this value. This constraint is only imposed if the administrator has also requested
      * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The default value is 0.
      * <p>
+     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     * <p>
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
      * not, a security exception will be thrown.
@@ -3024,6 +3068,9 @@
      * and only applies when the password quality is
      * {@link #PASSWORD_QUALITY_COMPLEX}.
      *
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     *
      * <p>This method can be called on the {@link DevicePolicyManager} instance
      * returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
      * restrictions on the parent profile.
@@ -3060,6 +3107,9 @@
      * , {@link #PASSWORD_QUALITY_NUMERIC_COMPLEX} {@link #PASSWORD_QUALITY_ALPHABETIC}, or
      * {@link #PASSWORD_QUALITY_ALPHANUMERIC} with {@link #setPasswordQuality}.
      * <p>
+     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     * <p>
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
      * not, a security exception will be thrown.
@@ -3112,6 +3162,7 @@
      * @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
      *             does not use {@link DeviceAdminInfo#USES_POLICY_EXPIRE_PASSWORD}
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public void setPasswordExpirationTimeout(@NonNull ComponentName admin, long timeout) {
         if (mService != null) {
             try {
@@ -3136,6 +3187,7 @@
      * @param admin The name of the admin component to check, or {@code null} to aggregate all admins.
      * @return The timeout for the given admin or the minimum of all timeouts
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public long getPasswordExpirationTimeout(@Nullable ComponentName admin) {
         if (mService != null) {
             try {
@@ -3160,6 +3212,7 @@
      * @param admin The name of the admin component to check, or {@code null} to aggregate all admins.
      * @return The password expiration time, in milliseconds since epoch.
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public long getPasswordExpiration(@Nullable ComponentName admin) {
         if (mService != null) {
             try {
@@ -3184,12 +3237,14 @@
      * all admins.
      * @return The length of the password history
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public int getPasswordHistoryLength(@Nullable ComponentName admin) {
         return getPasswordHistoryLength(admin, myUserId());
     }
 
     /** @hide per-user version */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public int getPasswordHistoryLength(@Nullable ComponentName admin, int userHandle) {
         if (mService != null) {
             try {
@@ -3204,10 +3259,16 @@
     /**
      * Return the maximum password length that the device supports for a
      * particular password quality.
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always empty.
      * @param quality The quality being interrogated.
      * @return Returns the maximum length that the user can enter.
      */
     public int getPasswordMaximumLength(int quality) {
+        PackageManager pm = mContext.getPackageManager();
+        if (!pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ID_ATTESTATION)) {
+            return 0;
+        }
         // Kind-of arbitrary.
         return 16;
     }
@@ -3218,6 +3279,10 @@
      * user and its participating profiles. Restrictions on profiles that have a separate challenge
      * are not taken into account. The user must be unlocked in order to perform the check.
      * <p>
+     * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty - i.e. this method will always return false on such
+     * devices, provided any password requirements were set.
+     * <p>
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
      * not, a security exception will be thrown.
@@ -3250,6 +3315,9 @@
      * explicitly querying the parent profile screen lock complexity via {@link
      * #getParentProfileInstance}.
      *
+     * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+     * password is always treated as empty.
+     *
      * @throws IllegalStateException if the user is not unlocked.
      * @throws SecurityException if the calling application does not have the permission
      *                           {@link permission#GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY}
@@ -3329,6 +3397,7 @@
      * @throws SecurityException if the calling application does not own an active administrator
      *             that uses {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN}
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public int getCurrentFailedPasswordAttempts() {
         return getCurrentFailedPasswordAttempts(myUserId());
     }
@@ -3396,6 +3465,7 @@
      *             both {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} and
      *             {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}.
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public void setMaximumFailedPasswordsForWipe(@NonNull ComponentName admin, int num) {
         if (mService != null) {
             try {
@@ -3419,12 +3489,14 @@
      * @param admin The name of the admin component to check, or {@code null} to aggregate
      * all admins.
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public int getMaximumFailedPasswordsForWipe(@Nullable ComponentName admin) {
         return getMaximumFailedPasswordsForWipe(admin, myUserId());
     }
 
     /** @hide per-user version */
     @UnsupportedAppUsage
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public int getMaximumFailedPasswordsForWipe(@Nullable ComponentName admin, int userHandle) {
         if (mService != null) {
             try {
@@ -3444,6 +3516,7 @@
      * user passed in.
      * @hide Used only by Keyguard
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public int getProfileWithMinimumFailedPasswordsForWipe(int userHandle) {
         if (mService != null) {
             try {
@@ -3514,6 +3587,7 @@
      *             that uses {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD}
      * @throws IllegalStateException if the calling user is locked or has a managed profile.
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public boolean resetPassword(String password, int flags) {
         throwIfParentInstance("resetPassword");
         if (mService != null) {
@@ -3557,6 +3631,7 @@
      * @throws SecurityException if admin is not a device or profile owner.
      * @throws IllegalArgumentException if the supplied token is invalid.
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public boolean setResetPasswordToken(ComponentName admin, byte[] token) {
         throwIfParentInstance("setResetPasswordToken");
         if (mService != null) {
@@ -3576,6 +3651,7 @@
      * @return true if the operation is successful, false otherwise.
      * @throws SecurityException if admin is not a device or profile owner.
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public boolean clearResetPasswordToken(ComponentName admin) {
         throwIfParentInstance("clearResetPasswordToken");
         if (mService != null) {
@@ -3596,6 +3672,7 @@
      * @throws SecurityException if admin is not a device or profile owner.
      * @throws IllegalStateException if no token has been set.
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public boolean isResetPasswordTokenActive(ComponentName admin) {
         throwIfParentInstance("isResetPasswordTokenActive");
         if (mService != null) {
@@ -3637,6 +3714,7 @@
      * @throws SecurityException if admin is not a device or profile owner.
      * @throws IllegalStateException if the provided token is not valid.
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public boolean resetPasswordWithToken(@NonNull ComponentName admin, String password,
             byte[] token, int flags) {
         throwIfParentInstance("resetPassword");
@@ -3742,6 +3820,7 @@
      *
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public void setRequiredStrongAuthTimeout(@NonNull ComponentName admin,
             long timeoutMs) {
         if (mService != null) {
@@ -3766,12 +3845,14 @@
      *         across all participating admins.
      * @return The timeout in milliseconds or 0 if not configured for the provided admin.
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public long getRequiredStrongAuthTimeout(@Nullable ComponentName admin) {
         return getRequiredStrongAuthTimeout(admin, myUserId());
     }
 
     /** @hide per-user version */
     @UnsupportedAppUsage
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public long getRequiredStrongAuthTimeout(@Nullable ComponentName admin, @UserIdInt int userId) {
         if (mService != null) {
             try {
@@ -5350,6 +5431,7 @@
      * @hide
      */
     @UnsupportedAppUsage
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public void setActivePasswordState(PasswordMetrics metrics, int userHandle) {
         if (mService != null) {
             try {
@@ -5363,6 +5445,7 @@
     /**
      * @hide
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public void reportPasswordChanged(@UserIdInt int userId) {
         if (mService != null) {
             try {
@@ -5377,6 +5460,7 @@
      * @hide
      */
     @UnsupportedAppUsage
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public void reportFailedPasswordAttempt(int userHandle) {
         if (mService != null) {
             try {
@@ -5391,6 +5475,7 @@
      * @hide
      */
     @UnsupportedAppUsage
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public void reportSuccessfulPasswordAttempt(int userHandle) {
         if (mService != null) {
             try {
@@ -5404,6 +5489,7 @@
     /**
      * @hide
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public void reportFailedBiometricAttempt(int userHandle) {
         if (mService != null) {
             try {
@@ -5417,6 +5503,7 @@
     /**
      * @hide
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public void reportSuccessfulBiometricAttempt(int userHandle) {
         if (mService != null) {
             try {
@@ -6383,6 +6470,7 @@
      * @throws SecurityException if {@code admin} is not an active administrator or does not use
      *             {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_FEATURES}
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public void setTrustAgentConfiguration(@NonNull ComponentName admin,
             @NonNull ComponentName target, PersistableBundle configuration) {
         if (mService != null) {
@@ -6412,6 +6500,7 @@
      * @param agent Which component to get enabled features for.
      * @return configuration for the given trust agent.
      */
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public @Nullable List<PersistableBundle> getTrustAgentConfiguration(
             @Nullable ComponentName admin, @NonNull ComponentName agent) {
         return getTrustAgentConfiguration(admin, agent, myUserId());
@@ -6419,6 +6508,7 @@
 
     /** @hide per-user version */
     @UnsupportedAppUsage
+    @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
     public @Nullable List<PersistableBundle> getTrustAgentConfiguration(
             @Nullable ComponentName admin, @NonNull ComponentName agent, int userHandle) {
         if (mService != null) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 1945b2f..97bc079 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -36,6 +36,7 @@
 import android.content.Context;
 import android.os.BatteryStats;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
@@ -648,6 +649,32 @@
 
     private final Object mLock = new Object();
     private final Map<LeScanCallback, ScanCallback> mLeScanClients;
+    private static final Map<BluetoothDevice, List<Pair<MetadataListener, Handler>>>
+                sMetadataListeners = new HashMap<>();
+
+    /**
+     * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener
+     * implementation.
+     */
+    private static final IBluetoothMetadataListener sBluetoothMetadataListener =
+            new IBluetoothMetadataListener.Stub() {
+        @Override
+        public void onMetadataChanged(BluetoothDevice device, int key, String value) {
+            synchronized (sMetadataListeners) {
+                if (sMetadataListeners.containsKey(device)) {
+                    List<Pair<MetadataListener, Handler>> list = sMetadataListeners.get(device);
+                    for (Pair<MetadataListener, Handler> pair : list) {
+                        MetadataListener listener = pair.first;
+                        Handler handler = pair.second;
+                        handler.post(() -> {
+                            listener.onMetadataChanged(device, key, value);
+                        });
+                    }
+                }
+            }
+            return;
+        }
+    };
 
     /**
      * Get a handle to the default local Bluetooth adapter.
@@ -2607,6 +2634,16 @@
                             }
                         }
                     }
+                    synchronized (sMetadataListeners) {
+                        sMetadataListeners.forEach((device, pair) -> {
+                            try {
+                                mService.registerMetadataListener(sBluetoothMetadataListener,
+                                        device);
+                            } catch (RemoteException e) {
+                                Log.e(TAG, "Failed to register metadata listener", e);
+                            }
+                        });
+                    }
                 }
 
                 public void onBluetoothServiceDown() {
@@ -3090,4 +3127,142 @@
                     + "listenUsingInsecureL2capChannel");
         return listenUsingInsecureL2capChannel();
     }
+
+    /**
+     * Register a {@link #MetadataListener} to receive update about metadata
+     * changes for this {@link BluetoothDevice}.
+     * Registration must be done when Bluetooth is ON and will last until
+     * {@link #unregisterMetadataListener(BluetoothDevice)} is called, even when Bluetooth
+     * restarted in the middle.
+     * All input parameters should not be null or {@link NullPointerException} will be triggered.
+     * The same {@link BluetoothDevice} and {@link #MetadataListener} pair can only be registered
+     * once, double registration would cause {@link IllegalArgumentException}.
+     *
+     * @param device {@link BluetoothDevice} that will be registered
+     * @param listener {@link #MetadataListener} that will receive asynchronous callbacks
+     * @param handler the handler for listener callback
+     * @return true on success, false on error
+     * @throws NullPointerException If one of {@code listener}, {@code device} or {@code handler}
+     * is null.
+     * @throws IllegalArgumentException The same {@link #MetadataListener} and
+     * {@link BluetoothDevice} are registered twice.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean registerMetadataListener(BluetoothDevice device, MetadataListener listener,
+            Handler handler) {
+        if (DBG) Log.d(TAG, "registerMetdataListener()");
+
+        final IBluetooth service = mService;
+        if (service == null) {
+            Log.e(TAG, "Bluetooth is not enabled. Cannot register metadata listener");
+            return false;
+        }
+        if (listener == null) {
+            throw new NullPointerException("listener is null");
+        }
+        if (device == null) {
+            throw new NullPointerException("device is null");
+        }
+        if (handler == null) {
+            throw new NullPointerException("handler is null");
+        }
+
+        synchronized (sMetadataListeners) {
+            List<Pair<MetadataListener, Handler>> listenerList = sMetadataListeners.get(device);
+            if (listenerList == null) {
+                // Create new listener/handler list for registeration
+                listenerList = new ArrayList<>();
+                sMetadataListeners.put(device, listenerList);
+            } else {
+                // Check whether this device was already registed by the lisenter
+                if (listenerList.stream().anyMatch((pair) -> (pair.first.equals(listener)))) {
+                    throw new IllegalArgumentException("listener was already regestered"
+                            + " for the device");
+                }
+            }
+
+            Pair<MetadataListener, Handler> listenerPair = new Pair(listener, handler);
+            listenerList.add(listenerPair);
+
+            boolean ret = false;
+            try {
+                ret = service.registerMetadataListener(sBluetoothMetadataListener, device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "registerMetadataListener fail", e);
+            } finally {
+                if (!ret) {
+                    // Remove listener registered earlier when fail.
+                    listenerList.remove(listenerPair);
+                    if (listenerList.isEmpty()) {
+                        // Remove the device if its listener list is empty
+                        sMetadataListeners.remove(device);
+                    }
+                }
+            }
+            return ret;
+        }
+    }
+
+    /**
+     * Unregister all {@link MetadataListener} from this {@link BluetoothDevice}.
+     * Unregistration can be done when Bluetooth is either ON or OFF.
+     * {@link #registerMetadataListener(MetadataListener, BluetoothDevice, Handler)} must
+     * be called before unregisteration.
+     * Unregistering a device that is not regestered would cause {@link IllegalArgumentException}.
+     *
+     * @param device {@link BluetoothDevice} that will be unregistered. it
+     * should not be null or {@link NullPointerException} will be triggered.
+     * @return true on success, false on error
+     * @throws NullPointerException If {@code device} is null.
+     * @throws IllegalArgumentException If {@code device} has not been registered before.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean unregisterMetadataListener(BluetoothDevice device) {
+        if (DBG) Log.d(TAG, "unregisterMetdataListener()");
+        if (device == null) {
+            throw new NullPointerException("device is null");
+        }
+
+        synchronized (sMetadataListeners) {
+            if (sMetadataListeners.containsKey(device)) {
+                sMetadataListeners.remove(device);
+            } else {
+                throw new IllegalArgumentException("device was not registered");
+            }
+
+            final IBluetooth service = mService;
+            if (service == null) {
+                // Bluetooth is OFF, do nothing to Bluetooth service.
+                return true;
+            }
+            try {
+                return service.unregisterMetadataListener(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "unregisterMetadataListener fail", e);
+                return false;
+            }
+        }
+    }
+
+    /**
+     * This abstract class is used to implement {@link BluetoothAdapter} metadata listener.
+     * @hide
+     */
+    @SystemApi
+    public abstract class MetadataListener {
+        /**
+         * Callback triggered if the metadata of {@link BluetoothDevice} registered in
+         * {@link #registerMetadataListener}.
+         *
+         * @param device changed {@link BluetoothDevice}.
+         * @param key changed metadata key, one of BluetoothDevice.METADATA_*.
+         * @param value the new value of metadata.
+         */
+        public void onMetadataChanged(BluetoothDevice device, int key, String value) {
+        }
+    }
 }
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 235dc5c..17cf702 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -341,6 +341,137 @@
             "android.bluetooth.device.action.SDP_RECORD";
 
     /**
+     * Maximum length of a metadata entry, this is to avoid exploding Bluetooth
+     * disk usage
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_MAX_LENGTH = 2048;
+
+    /**
+     * Manufacturer name of this Bluetooth device
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_MANUFACTURER_NAME = 0;
+
+    /**
+     * Model name of this Bluetooth device
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_MODEL_NAME = 1;
+
+    /**
+     * Software version of this Bluetooth device
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_SOFTWARE_VERSION = 2;
+
+    /**
+     * Hardware version of this Bluetooth device
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_HARDWARE_VERSION = 3;
+
+    /**
+     * Package name of the companion app, if any
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_COMPANION_APP = 4;
+
+    /**
+     * URI to the main icon shown on the settings UI
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_MAIN_ICON = 5;
+
+    /**
+     * Whether this device is an untethered headset with left, right and case
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_IS_UNTHETHERED_HEADSET = 6;
+
+    /**
+     * URI to icon of the left headset
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_UNTHETHERED_LEFT_ICON = 7;
+
+    /**
+     * URI to icon of the right headset
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_UNTHETHERED_RIGHT_ICON = 8;
+
+    /**
+     * URI to icon of the headset charging case
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_UNTHETHERED_CASE_ICON = 9;
+
+    /**
+     * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
+     * is invalid, of the left headset
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_UNTHETHERED_LEFT_BATTERY = 10;
+
+    /**
+     * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
+     * is invalid, of the right headset
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_UNTHETHERED_RIGHT_BATTERY = 11;
+
+    /**
+     * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
+     * is invalid, of the headset charging case
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_UNTHETHERED_CASE_BATTERY = 12;
+
+    /**
+     * Whether the left headset is charging
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_UNTHETHERED_LEFT_CHARGING = 13;
+
+    /**
+     * Whether the right headset is charging
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_UNTHETHERED_RIGHT_CHARGING = 14;
+
+    /**
+     * Whether the headset charging case is charging
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_UNTHETHERED_CASE_CHARGING = 15;
+
+    /**
+     * URI to the enhanced settings UI slice, null or empty String means
+     * the UI does not exist
+     * @hide
+     */
+    @SystemApi
+    public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16;
+
+    /**
      * Broadcast Action: This intent is used to broadcast the {@link UUID}
      * wrapped as a {@link android.os.ParcelUuid} of the remote device after it
      * has been fetched. This intent is sent only when the UUIDs of the remote
@@ -2026,4 +2157,61 @@
         Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createInsecureL2capChannel");
         return createInsecureL2capChannel(psm);
     }
+
+    /**
+     * Set a keyed metadata of this {@link BluetoothDevice} to a
+     * {@link String} value.
+     * Only bonded devices's metadata will be persisted across Bluetooth
+     * restart.
+     * Metadata will be removed when the device's bond state is moved to
+     * {@link #BOND_NONE}.
+     *
+     * @param key must be within the list of BluetoothDevice.METADATA_*
+     * @param value the string data to set for key. Must be less than
+     * {@link BluetoothAdapter#METADATA_MAX_LENGTH} characters in length
+     * @return true on success, false on error
+     * @hide
+    */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean setMetadata(int key, String value) {
+        final IBluetooth service = sService;
+        if (service == null) {
+            Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata");
+            return false;
+        }
+        if (value.length() > METADATA_MAX_LENGTH) {
+            throw new IllegalArgumentException("value length is " + value.length()
+                    + ", should not over " + METADATA_MAX_LENGTH);
+        }
+        try {
+            return service.setMetadata(this, key, value);
+        } catch (RemoteException e) {
+            Log.e(TAG, "setMetadata fail", e);
+            return false;
+        }
+    }
+
+    /**
+     * Get a keyed metadata for this {@link BluetoothDevice} as {@link String}
+     *
+     * @param key must be within the list of BluetoothDevice.METADATA_*
+     * @return Metadata of the key as string, null on error or not found
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public String getMetadata(int key) {
+        final IBluetooth service = sService;
+        if (service == null) {
+            Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata");
+            return null;
+        }
+        try {
+            return service.getMetadata(this, key);
+        } catch (RemoteException e) {
+            Log.e(TAG, "getMetadata fail", e);
+            return null;
+        }
+    }
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b845673..4efcd30 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2060,6 +2060,14 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device has a secure implementation of keyguard, meaning the
+     * device supports PIN, pattern and password as defined in Android CDD
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SECURE_LOCK_SCREEN = "android.software.secure_lock_screen";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device includes an accelerometer.
      */
     @SdkConstant(SdkConstantType.FEATURE)
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index c7320b0..c9a4c82 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -794,6 +794,12 @@
             "android.content.pm.extra.ENABLE_ROLLBACK_INSTALL_FLAGS";
 
     /**
+     * Extra field name for the set of installed users for a given rollback package.
+     */
+    public static final String EXTRA_ENABLE_ROLLBACK_INSTALLED_USERS =
+            "android.content.pm.extra.ENABLE_ROLLBACK_INSTALLED_USERS";
+
+    /**
      * Used as the {@code enableRollbackCode} argument for
      * {@link PackageManagerInternal#setEnableRollbackCode} to indicate that
      * enabling rollback succeeded.
@@ -827,4 +833,10 @@
      * Ask the package manager to compile layouts in the given package.
      */
     public abstract boolean compileLayouts(String packageName);
+
+    /*
+     * Inform the package manager that the pending package install identified by
+     * {@code token} can be completed.
+     */
+    public abstract void finishPackageInstall(int token, boolean didLaunch);
 }
diff --git a/core/java/android/content/rollback/IRollbackManager.aidl b/core/java/android/content/rollback/IRollbackManager.aidl
index 7f557cd..420bcb6 100644
--- a/core/java/android/content/rollback/IRollbackManager.aidl
+++ b/core/java/android/content/rollback/IRollbackManager.aidl
@@ -33,6 +33,12 @@
     void executeRollback(in RollbackInfo rollback, String callerPackageName,
             in IntentSender statusReceiver);
 
+    // Exposed for use from the system server only. Callback from the package
+    // manager during the install flow when user data can be restored for a given
+    // package.
+    void restoreUserData(String packageName, int userId, int appId, long ceDataInode,
+            String seInfo, int token);
+
     // Exposed for test purposes only.
     void reloadPersistedData();
 
diff --git a/core/java/android/net/DnsPacket.java b/core/java/android/net/DnsPacket.java
new file mode 100644
index 0000000..458fb34
--- /dev/null
+++ b/core/java/android/net/DnsPacket.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+
+import com.android.internal.util.BitUtils;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.text.DecimalFormat;
+import java.text.FieldPosition;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringJoiner;
+
+/**
+ * Defines basic data for DNS protocol based on RFC 1035.
+ * Subclasses create the specific format used in DNS packet.
+ *
+ * @hide
+ */
+public abstract class DnsPacket {
+    public class DnsHeader {
+        private static final String TAG = "DnsHeader";
+        public final int id;
+        public final int flags;
+        public final int rcode;
+        private final int[] mSectionCount;
+
+        /**
+         * Create a new DnsHeader from a positioned ByteBuffer.
+         *
+         * The ByteBuffer must be in network byte order (which is the default).
+         * Reads the passed ByteBuffer from its current position and decodes a DNS header.
+         * When this constructor returns, the reading position of the ByteBuffer has been
+         * advanced to the end of the DNS header record.
+         * This is meant to chain with other methods reading a DNS response in sequence.
+         *
+         */
+        DnsHeader(@NonNull ByteBuffer buf) throws BufferUnderflowException {
+            id = BitUtils.uint16(buf.getShort());
+            flags = BitUtils.uint16(buf.getShort());
+            rcode = flags & 0xF;
+            mSectionCount = new int[NUM_SECTIONS];
+            for (int i = 0; i < NUM_SECTIONS; ++i) {
+                mSectionCount[i] = BitUtils.uint16(buf.getShort());
+            }
+        }
+
+        /**
+         * Get section count by section type.
+         */
+        public int getSectionCount(int sectionType) {
+            return mSectionCount[sectionType];
+        }
+    }
+
+    public class DnsSection {
+        private static final int MAXNAMESIZE = 255;
+        private static final int MAXLABELSIZE = 63;
+        private static final int MAXLABELCOUNT = 128;
+        private static final int NAME_NORMAL = 0;
+        private static final int NAME_COMPRESSION = 0xC0;
+        private final DecimalFormat byteFormat = new DecimalFormat();
+        private final FieldPosition pos = new FieldPosition(0);
+
+        private static final String TAG = "DnsSection";
+
+        public final String dName;
+        public final int nsType;
+        public final int nsClass;
+        public final long ttl;
+        private final byte[] mRR;
+
+        /**
+         * Create a new DnsSection from a positioned ByteBuffer.
+         *
+         * The ByteBuffer must be in network byte order (which is the default).
+         * Reads the passed ByteBuffer from its current position and decodes a DNS section.
+         * When this constructor returns, the reading position of the ByteBuffer has been
+         * advanced to the end of the DNS header record.
+         * This is meant to chain with other methods reading a DNS response in sequence.
+         *
+         */
+        DnsSection(int sectionType, @NonNull ByteBuffer buf)
+                throws BufferUnderflowException, ParseException {
+            dName = parseName(buf, 0 /* Parse depth */);
+            if (dName.length() > MAXNAMESIZE) {
+                throw new ParseException("Parse name fail, name size is too long");
+            }
+            nsType = BitUtils.uint16(buf.getShort());
+            nsClass = BitUtils.uint16(buf.getShort());
+
+            if (sectionType != QDSECTION) {
+                ttl = BitUtils.uint32(buf.getInt());
+                final int length = BitUtils.uint16(buf.getShort());
+                mRR = new byte[length];
+                buf.get(mRR);
+            } else {
+                ttl = 0;
+                mRR = null;
+            }
+        }
+
+        /**
+         * Get a copy of rr.
+         */
+        @Nullable public byte[] getRR() {
+            return (mRR == null) ? null : mRR.clone();
+        }
+
+        /**
+         * Convert label from {@code byte[]} to {@code String}
+         *
+         * It follows the same converting rule as native layer.
+         * (See ns_name.c in libc)
+         *
+         */
+        private String labelToString(@NonNull byte[] label) {
+            final StringBuffer sb = new StringBuffer();
+            for (int i = 0; i < label.length; ++i) {
+                int b = BitUtils.uint8(label[i]);
+                // Control characters and non-ASCII characters.
+                if (b <= 0x20 || b >= 0x7f) {
+                    sb.append('\\');
+                    byteFormat.format(b, sb, pos);
+                } else if (b == '"' || b == '.' || b == ';' || b == '\\'
+                        || b == '(' || b == ')' || b == '@' || b == '$') {
+                    sb.append('\\');
+                    sb.append((char) b);
+                } else {
+                    sb.append((char) b);
+                }
+            }
+            return sb.toString();
+        }
+
+        private String parseName(@NonNull ByteBuffer buf, int depth) throws
+                BufferUnderflowException, ParseException {
+            if (depth > MAXLABELCOUNT) throw new ParseException("Parse name fails, too many labels");
+            final int len = BitUtils.uint8(buf.get());
+            final int mask = len & NAME_COMPRESSION;
+            if (0 == len) {
+                return "";
+            } else if (mask != NAME_NORMAL && mask != NAME_COMPRESSION) {
+                throw new ParseException("Parse name fail, bad label type");
+            } else if (mask == NAME_COMPRESSION) {
+                // Name compression based on RFC 1035 - 4.1.4 Message compression
+                final int offset = ((len & ~NAME_COMPRESSION) << 8) + BitUtils.uint8(buf.get());
+                final int oldPos = buf.position();
+                if (offset >= oldPos - 2) {
+                    throw new ParseException("Parse compression name fail, invalid compression");
+                }
+                buf.position(offset);
+                final String pointed = parseName(buf, depth + 1);
+                buf.position(oldPos);
+                return pointed;
+            } else {
+                final byte[] label = new byte[len];
+                buf.get(label);
+                final String head = labelToString(label);
+                if (head.length() > MAXLABELSIZE) {
+                    throw new ParseException("Parse name fail, invalid label length");
+                }
+                final String tail = parseName(buf, depth + 1);
+                return TextUtils.isEmpty(tail) ? head : head + "." + tail;
+            }
+        }
+    }
+
+    public static final int QDSECTION = 0;
+    public static final int ANSECTION = 1;
+    public static final int NSSECTION = 2;
+    public static final int ARSECTION = 3;
+    private static final int NUM_SECTIONS = ARSECTION + 1;
+
+    private static final String TAG = DnsPacket.class.getSimpleName();
+
+    protected final DnsHeader mHeader;
+    protected final List<DnsSection>[] mSections;
+
+    public static class ParseException extends Exception {
+        public ParseException(String msg) {
+            super(msg);
+        }
+
+        public ParseException(String msg, Throwable cause) {
+            super(msg, cause);
+        }
+    }
+
+    protected DnsPacket(@NonNull byte[] data) throws ParseException {
+        if (null == data) throw new ParseException("Parse header failed, null input data");
+        final ByteBuffer buffer;
+        try {
+            buffer = ByteBuffer.wrap(data);
+            mHeader = new DnsHeader(buffer);
+        } catch (BufferUnderflowException e) {
+            throw new ParseException("Parse Header fail, bad input data", e);
+        }
+
+        mSections = new ArrayList[NUM_SECTIONS];
+
+        for (int i = 0; i < NUM_SECTIONS; ++i) {
+            final int count = mHeader.getSectionCount(i);
+            if (count > 0) {
+                mSections[i] = new ArrayList(count);
+            }
+            for (int j = 0; j < count; ++j) {
+                try {
+                    mSections[i].add(new DnsSection(i, buffer));
+                } catch (BufferUnderflowException e) {
+                    throw new ParseException("Parse section fail", e);
+                }
+            }
+        }
+    }
+}
diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java
new file mode 100644
index 0000000..6d54264
--- /dev/null
+++ b/core/java/android/net/DnsResolver.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static android.net.NetworkUtils.resNetworkQuery;
+import static android.net.NetworkUtils.resNetworkResult;
+import static android.net.NetworkUtils.resNetworkSend;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.MessageQueue;
+import android.system.ErrnoException;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+
+/**
+ * Dns resolver class for asynchronous dns querying
+ *
+ */
+public final class DnsResolver {
+    private static final String TAG = "DnsResolver";
+    private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
+    private static final int MAXPACKET = 8 * 1024;
+
+    @IntDef(prefix = { "CLASS_" }, value = {
+            CLASS_IN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface QueryClass {}
+    public static final int CLASS_IN = 1;
+
+    @IntDef(prefix = { "TYPE_" },  value = {
+            TYPE_A,
+            TYPE_AAAA
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface QueryType {}
+    public static final int TYPE_A = 1;
+    public static final int TYPE_AAAA = 28;
+
+    @IntDef(prefix = { "FLAG_" }, value = {
+            FLAG_EMPTY,
+            FLAG_NO_RETRY,
+            FLAG_NO_CACHE_STORE,
+            FLAG_NO_CACHE_LOOKUP
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface QueryFlag {}
+    public static final int FLAG_EMPTY = 0;
+    public static final int FLAG_NO_RETRY = 1 << 0;
+    public static final int FLAG_NO_CACHE_STORE = 1 << 1;
+    public static final int FLAG_NO_CACHE_LOOKUP = 1 << 2;
+
+    private static final int DNS_RAW_RESPONSE = 1;
+
+    private static final int NETID_UNSET = 0;
+
+    private static final DnsResolver sInstance = new DnsResolver();
+
+    /**
+     * listener for receiving raw answers
+     */
+    public interface RawAnswerListener {
+        /**
+         * {@code byte[]} is {@code null} if query timed out
+         */
+        void onAnswer(@Nullable byte[] answer);
+    }
+
+    /**
+     * listener for receiving parsed answers
+     */
+    public interface InetAddressAnswerListener {
+        /**
+         * Will be called exactly once with all the answers to the query.
+         * size of addresses will be zero if no available answer could be parsed.
+         */
+        void onAnswer(@NonNull List<InetAddress> addresses);
+    }
+
+    /**
+     * Get instance for DnsResolver
+     */
+    public static DnsResolver getInstance() {
+        return sInstance;
+    }
+
+    private DnsResolver() {}
+
+    /**
+     * Pass in a blob and corresponding setting,
+     * get a blob back asynchronously with the entire raw answer.
+     *
+     * @param network {@link Network} specifying which network for querying.
+     *         {@code null} for query on default network.
+     * @param query blob message
+     * @param flags flags as a combination of the FLAGS_* constants
+     * @param handler {@link Handler} to specify the thread
+     *         upon which the {@link RawAnswerListener} will be invoked.
+     * @param listener a {@link RawAnswerListener} which will be called to notify the caller
+     *         of the result of dns query.
+     */
+    public void query(@Nullable Network network, @NonNull byte[] query, @QueryFlag int flags,
+            @NonNull Handler handler, @NonNull RawAnswerListener listener) throws ErrnoException {
+        final FileDescriptor queryfd = resNetworkSend((network != null
+                ? network.netId : NETID_UNSET), query, query.length, flags);
+        registerFDListener(handler.getLooper().getQueue(), queryfd,
+                answerbuf -> listener.onAnswer(answerbuf));
+    }
+
+    /**
+     * Pass in a domain name and corresponding setting,
+     * get a blob back asynchronously with the entire raw answer.
+     *
+     * @param network {@link Network} specifying which network for querying.
+     *         {@code null} for query on default network.
+     * @param domain domain name for querying
+     * @param nsClass dns class as one of the CLASS_* constants
+     * @param nsType dns resource record (RR) type as one of the TYPE_* constants
+     * @param flags flags as a combination of the FLAGS_* constants
+     * @param handler {@link Handler} to specify the thread
+     *         upon which the {@link RawAnswerListener} will be invoked.
+     * @param listener a {@link RawAnswerListener} which will be called to notify the caller
+     *         of the result of dns query.
+     */
+    public void query(@Nullable Network network, @NonNull String domain, @QueryClass int nsClass,
+            @QueryType int nsType, @QueryFlag int flags,
+            @NonNull Handler handler, @NonNull RawAnswerListener listener) throws ErrnoException {
+        final FileDescriptor queryfd = resNetworkQuery((network != null
+                ? network.netId : NETID_UNSET), domain, nsClass, nsType, flags);
+        registerFDListener(handler.getLooper().getQueue(), queryfd,
+                answerbuf -> listener.onAnswer(answerbuf));
+    }
+
+    /**
+     * Pass in a domain name and corresponding setting,
+     * get back a set of InetAddresses asynchronously.
+     *
+     * @param network {@link Network} specifying which network for querying.
+     *         {@code null} for query on default network.
+     * @param domain domain name for querying
+     * @param flags flags as a combination of the FLAGS_* constants
+     * @param handler {@link Handler} to specify the thread
+     *         upon which the {@link InetAddressAnswerListener} will be invoked.
+     * @param listener an {@link InetAddressAnswerListener} which will be called to
+     *         notify the caller of the result of dns query.
+     *
+     */
+    public void query(@Nullable Network network, @NonNull String domain, @QueryFlag int flags,
+            @NonNull Handler handler, @NonNull InetAddressAnswerListener listener)
+            throws ErrnoException {
+        final FileDescriptor v4fd = resNetworkQuery((network != null
+                ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_A, flags);
+        final FileDescriptor v6fd = resNetworkQuery((network != null
+                ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_AAAA, flags);
+
+        final InetAddressAnswerAccumulator accmulator =
+                new InetAddressAnswerAccumulator(2, listener);
+        final Consumer<byte[]> consumer = answerbuf ->
+                accmulator.accumulate(parseAnswers(answerbuf));
+
+        registerFDListener(handler.getLooper().getQueue(), v4fd, consumer);
+        registerFDListener(handler.getLooper().getQueue(), v6fd, consumer);
+    }
+
+    private void registerFDListener(@NonNull MessageQueue queue,
+            @NonNull FileDescriptor queryfd, @NonNull Consumer<byte[]> answerConsumer) {
+        queue.addOnFileDescriptorEventListener(
+                queryfd,
+                FD_EVENTS,
+                (fd, events) -> {
+                    byte[] answerbuf = null;
+                    try {
+                    // TODO: Implement result function in Java side instead of using JNI
+                    //       Because JNI method close fd prior than unregistering fd on
+                    //       event listener.
+                        answerbuf = resNetworkResult(fd);
+                    } catch (ErrnoException e) {
+                        Log.e(TAG, "resNetworkResult:" + e.toString());
+                    }
+                    answerConsumer.accept(answerbuf);
+
+                    // Unregister this fd listener
+                    return 0;
+                });
+    }
+
+    private class DnsAddressAnswer extends DnsPacket {
+        private static final String TAG = "DnsResolver.DnsAddressAnswer";
+        private static final boolean DBG = false;
+
+        private final int mQueryType;
+
+        DnsAddressAnswer(@NonNull byte[] data) throws ParseException {
+            super(data);
+            if ((mHeader.flags & (1 << 15)) == 0) {
+                throw new ParseException("Not an answer packet");
+            }
+            if (mHeader.rcode != 0) {
+                throw new ParseException("Response error, rcode:" + mHeader.rcode);
+            }
+            if (mHeader.getSectionCount(ANSECTION) == 0) {
+                throw new ParseException("No available answer");
+            }
+            if (mHeader.getSectionCount(QDSECTION) == 0) {
+                throw new ParseException("No question found");
+            }
+            // Assume only one question per answer packet. (RFC1035)
+            mQueryType = mSections[QDSECTION].get(0).nsType;
+        }
+
+        public @NonNull List<InetAddress> getAddresses() {
+            final List<InetAddress> results = new ArrayList<InetAddress>();
+            for (final DnsSection ansSec : mSections[ANSECTION]) {
+                // Only support A and AAAA, also ignore answers if query type != answer type.
+                int nsType = ansSec.nsType;
+                if (nsType != mQueryType || (nsType != TYPE_A && nsType != TYPE_AAAA)) {
+                    continue;
+                }
+                try {
+                    results.add(InetAddress.getByAddress(ansSec.getRR()));
+                } catch (UnknownHostException e) {
+                    if (DBG) {
+                        Log.w(TAG, "rr to address fail");
+                    }
+                }
+            }
+            return results;
+        }
+    }
+
+    private @Nullable List<InetAddress> parseAnswers(@Nullable byte[] data) {
+        try {
+            return (data == null) ? null : new DnsAddressAnswer(data).getAddresses();
+        } catch (DnsPacket.ParseException e) {
+            Log.e(TAG, "Parse answer fail " + e.getMessage());
+            return null;
+        }
+    }
+
+    private class InetAddressAnswerAccumulator {
+        private final List<InetAddress> mAllAnswers;
+        private final InetAddressAnswerListener mAnswerListener;
+        private final int mTargetAnswerCount;
+        private int mReceivedAnswerCount = 0;
+
+        InetAddressAnswerAccumulator(int size, @NonNull InetAddressAnswerListener listener) {
+            mTargetAnswerCount = size;
+            mAllAnswers = new ArrayList<>();
+            mAnswerListener = listener;
+        }
+
+        public void accumulate(@Nullable List<InetAddress> answer) {
+            if (null != answer) {
+                mAllAnswers.addAll(answer);
+            }
+            if (++mReceivedAnswerCount == mTargetAnswerCount) {
+                mAnswerListener.onAnswer(mAllAnswers);
+            }
+        }
+    }
+}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 4eab49c..c996d01 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -39,6 +39,8 @@
 import java.util.Locale;
 import java.util.TreeSet;
 
+import android.system.ErrnoException;
+
 /**
  * Native methods for managing network interfaces.
  *
@@ -138,6 +140,32 @@
     public native static boolean queryUserAccess(int uid, int netId);
 
     /**
+     * DNS resolver series jni method.
+     * Issue the query {@code msg} on the network designated by {@code netId}.
+     * {@code flags} is an additional config to control actual querying behavior.
+     * @return a file descriptor to watch for read events
+     */
+    public static native FileDescriptor resNetworkSend(
+            int netId, byte[] msg, int msglen, int flags) throws ErrnoException;
+
+    /**
+     * DNS resolver series jni method.
+     * Look up the {@code nsClass} {@code nsType} Resource Record (RR) associated
+     * with Domain Name {@code dname} on the network designated by {@code netId}.
+     * {@code flags} is an additional config to control actual querying behavior.
+     * @return a file descriptor to watch for read events
+     */
+    public static native FileDescriptor resNetworkQuery(
+            int netId, String dname, int nsClass, int nsType, int flags) throws ErrnoException;
+
+    /**
+     * DNS resolver series jni method.
+     * Read a result for the query associated with the {@code fd}.
+     * @return a byte array containing blob answer
+     */
+    public static native byte[] resNetworkResult(FileDescriptor fd) throws ErrnoException;
+
+    /**
      * Add an entry into the ARP cache.
      */
     public static void addArpEntry(Inet4Address ipv4Addr, MacAddress ethAddr, String ifname,
diff --git a/core/java/android/net/ipmemorystore/NetworkAttributes.java b/core/java/android/net/ipmemorystore/NetworkAttributes.java
index 5397b57..6a9eae0 100644
--- a/core/java/android/net/ipmemorystore/NetworkAttributes.java
+++ b/core/java/android/net/ipmemorystore/NetworkAttributes.java
@@ -252,6 +252,12 @@
         }
     }
 
+    /** @hide */
+    public boolean isEmpty() {
+        return (null == assignedV4Address) && (null == groupHint)
+                && (null == dnsAddresses) && (null == mtu);
+    }
+
     @Override
     public boolean equals(@Nullable final Object o) {
         if (!(o instanceof NetworkAttributes)) return false;
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index ca39051..51c3c4c 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -72,6 +72,7 @@
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Comparator;
 import java.util.Objects;
 import java.util.concurrent.Executor;
@@ -831,6 +832,16 @@
         return false;
     }
 
+    /** {@hide} */
+    public static boolean contains(Collection<File> dirs, File file) {
+        for (File dir : dirs) {
+            if (contains(dir, file)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Test if a file lives under the given directory, either as a direct child
      * or a distant grandchild.
diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl
index 8271701..fe17c6b 100644
--- a/core/java/android/os/IDeviceIdleController.aidl
+++ b/core/java/android/os/IDeviceIdleController.aidl
@@ -45,4 +45,6 @@
     void exitIdle(String reason);
     boolean registerMaintenanceActivityListener(IMaintenanceActivityListener listener);
     void unregisterMaintenanceActivityListener(IMaintenanceActivityListener listener);
+    int setPreIdleTimeoutMode(int Mode);
+    void resetPreIdleTimeoutMode();
 }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 0942d97..4ce760f 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1724,6 +1724,25 @@
             = "android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED";
 
     /**
+     * Constant for PreIdleTimeout normal mode (default mode, not short nor extend timeout) .
+     * @hide
+     */
+    public static final int PRE_IDLE_TIMEOUT_MODE_NORMAL = 0;
+
+    /**
+     * Constant for PreIdleTimeout long mode (extend timeout to keep in inactive mode
+     * longer).
+     * @hide
+     */
+    public static final int PRE_IDLE_TIMEOUT_MODE_LONG = 1;
+
+    /**
+     * Constant for PreIdleTimeout short mode (short timeout to go to doze mode quickly)
+     * @hide
+     */
+    public static final int PRE_IDLE_TIMEOUT_MODE_SHORT = 2;
+
+    /**
      * A wake lock is a mechanism to indicate that your application needs
      * to have the device stay on.
      * <p>
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index df1a713..714a061 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -35,6 +35,7 @@
 
 import java.io.CharArrayWriter;
 import java.io.File;
+import java.util.Locale;
 
 /**
  * Information about a shared/external storage volume for a specific user.
@@ -263,6 +264,11 @@
         return mFsUuid;
     }
 
+    /** {@hide} */
+    public @Nullable String getNormalizedUuid() {
+        return mFsUuid != null ? mFsUuid.toLowerCase(Locale.US) : null;
+    }
+
     /**
      * Parse and return volume UUID as FAT volume ID, or return -1 if unable to
      * parse or UUID is unknown.
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 8c3aa17..5d310e1 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -42,6 +42,7 @@
 import java.io.CharArrayWriter;
 import java.io.File;
 import java.util.Comparator;
+import java.util.Locale;
 import java.util.Objects;
 
 /**
@@ -254,6 +255,10 @@
         return fsUuid;
     }
 
+    public @Nullable String getNormalizedFsUuid() {
+        return fsUuid != null ? fsUuid.toLowerCase(Locale.US) : null;
+    }
+
     @UnsupportedAppUsage
     public int getMountUserId() {
         return mountUserId;
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 3a49986..487198b 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -70,6 +70,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -1224,7 +1226,7 @@
                 if (sv.isPrimary()) {
                     return VOLUME_EXTERNAL;
                 } else {
-                    return checkArgumentVolumeName(sv.getUuid());
+                    return checkArgumentVolumeName(sv.getNormalizedUuid());
                 }
             }
             throw new IllegalStateException("Unknown volume at " + path);
@@ -2919,7 +2921,7 @@
                 if (vi.isPrimary()) {
                     volumeNames.add(VOLUME_EXTERNAL);
                 } else {
-                    volumeNames.add(vi.getFsUuid());
+                    volumeNames.add(vi.getNormalizedFsUuid());
                 }
             }
         }
@@ -2953,8 +2955,7 @@
         // When not one of the well-known values above, it must be a hex UUID
         for (int i = 0; i < volumeName.length(); i++) {
             final char c = volumeName.charAt(i);
-            if (('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')
-                    || ('0' <= c && c <= '9') || (c == '-')) {
+            if (('a' <= c && c <= 'f') || ('0' <= c && c <= '9') || (c == '-')) {
                 continue;
             } else {
                 throw new IllegalArgumentException("Invalid volume name: " + volumeName);
@@ -2963,23 +2964,26 @@
         return volumeName;
     }
 
-    /** {@hide} */
+    /**
+     * Return path where the given volume is mounted. Not valid for
+     * {@link #VOLUME_INTERNAL}.
+     *
+     * @hide
+     */
     public static @NonNull File getVolumePath(@NonNull String volumeName)
             throws FileNotFoundException {
         if (TextUtils.isEmpty(volumeName)) {
             throw new IllegalArgumentException();
         }
 
-        if (VOLUME_INTERNAL.equals(volumeName)) {
-            return Environment.getDataDirectory();
-        } else if (VOLUME_EXTERNAL.equals(volumeName)) {
+        if (VOLUME_EXTERNAL.equals(volumeName)) {
             return Environment.getExternalStorageDirectory();
         }
 
         final StorageManager sm = AppGlobals.getInitialApplication()
                 .getSystemService(StorageManager.class);
         for (VolumeInfo vi : sm.getVolumes()) {
-            if (Objects.equals(vi.getFsUuid(), volumeName)) {
+            if (Objects.equals(vi.getNormalizedFsUuid(), volumeName)) {
                 final File path = vi.getPathForUser(UserHandle.myUserId());
                 if (path != null) {
                     return path;
@@ -2992,6 +2996,33 @@
     }
 
     /**
+     * Return paths that should be scanned for the given volume.
+     *
+     * @hide
+     */
+    public static @NonNull Collection<File> getVolumeScanPaths(@NonNull String volumeName)
+            throws FileNotFoundException {
+        if (TextUtils.isEmpty(volumeName)) {
+            throw new IllegalArgumentException();
+        }
+
+        final ArrayList<File> res = new ArrayList<>();
+        if (VOLUME_INTERNAL.equals(volumeName)) {
+            res.add(new File(Environment.getRootDirectory(), "media"));
+            res.add(new File(Environment.getOemDirectory(), "media"));
+            res.add(new File(Environment.getProductDirectory(), "media"));
+        } else {
+            res.add(getVolumePath(volumeName));
+            final UserManager um = AppGlobals.getInitialApplication()
+                    .getSystemService(UserManager.class);
+            if (VOLUME_EXTERNAL.equals(volumeName) && um.isDemoUser()) {
+                res.add(Environment.getDataPreloadsMediaDirectory());
+            }
+        }
+        return res;
+    }
+
+    /**
      * Uri for querying the state of the media scanner.
      */
     public static Uri getMediaScannerUri() {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 707778f..643c473 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11244,8 +11244,6 @@
          * service_max_inactivity               (long)
          * service_bg_start_timeout             (long)
          * process_start_async                  (boolean)
-         * use_mem_aware_service_restarts       (boolean)
-         * service_restart_delay_duration       (long)
          * </pre>
          *
          * <p>
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index 4dc10cd..ffb524d 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -103,10 +103,23 @@
      */
     public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS =
             "android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
+
     /** @see android.telephony.euicc.EuiccManager#ACTION_PROVISION_EMBEDDED_SUBSCRIPTION */
     public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION =
             "android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
 
+    /** @see android.telephony.euicc.EuiccManager#ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED */
+    public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED =
+            "android.service.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
+
+    /** @see android.telephony.euicc.EuiccManager#ACTION_DELETE_SUBSCRIPTION_PRIVILEGED */
+    public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED =
+            "android.service.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
+
+    /** @see android.telephony.euicc.EuiccManager#ACTION_RENAME_SUBSCRIPTION_PRIVILEGED */
+    public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED =
+            "android.service.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
+
     // LUI resolution actions. These are called by the platform to resolve errors in situations that
     // require user interaction.
     // TODO(b/33075886): Define extras for any input parameters to these dialogs once they are
diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl
index f7acfc5..b0269e3 100644
--- a/core/java/android/service/vr/IVrManager.aidl
+++ b/core/java/android/service/vr/IVrManager.aidl
@@ -110,13 +110,5 @@
      * @param standy True if the device is entering standby, false if it's exiting standby.
      */
     void setStandbyEnabled(boolean standby);
-
-    /**
-     * Start VR Input method for the given packageName in {@param componentName}.
-     * This method notifies InputMethodManagerService to use VR IME instead of
-     * regular phone IME.
-     */
-    void setVrInputMethod(in ComponentName componentName);
-
 }
 
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 33b3ff4f..7d9ec70 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -237,6 +237,16 @@
     private static final int LONG_PRESS = 2;
     private static final int TAP = 3;
 
+    /**
+     * If a MotionEvent has CLASSIFICATION_AMBIGUOUS_GESTURE set, then certain actions, such as
+     * scrolling, will be inhibited. However, to account for the possibility of incorrect
+     * classification, the default scrolling will only be inhibited if the gesture moves beyond
+     * (default touch slop * AMBIGUOUS_GESTURE_MULTIPLIER). Likewise, the default long press
+     * timeout will be increased for some situations where the default behaviour
+     * is to cancel it.
+     */
+    private static final int AMBIGUOUS_GESTURE_MULTIPLIER = 2;
+
     private final Handler mHandler;
     @UnsupportedAppUsage
     private final OnGestureListener mListener;
@@ -292,27 +302,27 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-            case SHOW_PRESS:
-                mListener.onShowPress(mCurrentDownEvent);
-                break;
-                
-            case LONG_PRESS:
-                dispatchLongPress();
-                break;
-                
-            case TAP:
-                // If the user's finger is still down, do not count it as a tap
-                if (mDoubleTapListener != null) {
-                    if (!mStillDown) {
-                        mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
-                    } else {
-                        mDeferConfirmSingleTap = true;
-                    }
-                }
-                break;
+                case SHOW_PRESS:
+                    mListener.onShowPress(mCurrentDownEvent);
+                    break;
 
-            default:
-                throw new RuntimeException("Unknown message " + msg); //never
+                case LONG_PRESS:
+                    dispatchLongPress();
+                    break;
+
+                case TAP:
+                    // If the user's finger is still down, do not count it as a tap
+                    if (mDoubleTapListener != null) {
+                        if (!mStillDown) {
+                            mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
+                        } else {
+                            mDeferConfirmSingleTap = true;
+                        }
+                    }
+                    break;
+
+                default:
+                    throw new RuntimeException("Unknown message " + msg); //never
             }
         }
     }
@@ -427,7 +437,7 @@
         if (context == null) {
             //noinspection deprecation
             touchSlop = ViewConfiguration.getTouchSlop();
-            doubleTapTouchSlop = touchSlop; // Hack rather than adding a hiden method for this
+            doubleTapTouchSlop = touchSlop; // Hack rather than adding a hidden method for this
             doubleTapSlop = ViewConfiguration.getDoubleTapSlop();
             //noinspection deprecation
             mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
@@ -605,6 +615,10 @@
                 if (mInLongPress || mInContextClick) {
                     break;
                 }
+
+                final int motionClassification = ev.getClassification();
+                final boolean hasPendingLongPress = mHandler.hasMessages(LONG_PRESS);
+
                 final float scrollX = mLastFocusX - focusX;
                 final float scrollY = mLastFocusY - focusY;
                 if (mIsDoubleTapping) {
@@ -615,6 +629,31 @@
                     final int deltaY = (int) (focusY - mDownFocusY);
                     int distance = (deltaX * deltaX) + (deltaY * deltaY);
                     int slopSquare = isGeneratedGesture ? 0 : mTouchSlopSquare;
+
+                    final boolean ambiguousGesture =
+                            motionClassification == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE;
+                    final boolean shouldInhibitDefaultAction =
+                            hasPendingLongPress && ambiguousGesture;
+                    if (shouldInhibitDefaultAction) {
+                        // Inhibit default long press
+                        if (distance > slopSquare) {
+                            // The default action here is to remove long press. But if the touch
+                            // slop below gets increased, and we never exceed the modified touch
+                            // slop while still receiving AMBIGUOUS_GESTURE, we risk that *nothing*
+                            // will happen in response to user input. To prevent this,
+                            // reschedule long press with a modified timeout.
+                            mHandler.removeMessages(LONG_PRESS);
+                            final int longPressTimeout = ViewConfiguration.getLongPressTimeout();
+                            mHandler.sendEmptyMessageAtTime(LONG_PRESS, ev.getDownTime()
+                                    + longPressTimeout * AMBIGUOUS_GESTURE_MULTIPLIER);
+                        }
+                        // Inhibit default scroll. If a gesture is ambiguous, we prevent scroll
+                        // until the gesture is resolved.
+                        // However, for safety, simply increase the touch slop in case the
+                        // classification is erroneous. Since the value is squared, multiply twice.
+                        slopSquare *= AMBIGUOUS_GESTURE_MULTIPLIER * AMBIGUOUS_GESTURE_MULTIPLIER;
+                    }
+
                     if (distance > slopSquare) {
                         handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
                         mLastFocusX = focusX;
@@ -633,6 +672,12 @@
                     mLastFocusX = focusX;
                     mLastFocusY = focusY;
                 }
+                final boolean deepPress =
+                        motionClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS;
+                if (deepPress && hasPendingLongPress) {
+                    mHandler.removeMessages(LONG_PRESS);
+                    mHandler.sendEmptyMessage(LONG_PRESS);
+                }
                 break;
 
             case MotionEvent.ACTION_UP:
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 330d72f..42ac880 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -51,6 +51,7 @@
 import android.view.AppTransitionAnimationSpec;
 import android.view.WindowContentFrameStats;
 import android.view.WindowManager;
+import android.view.SurfaceControl;
 
 /**
  * System private interface to the window manager.
@@ -555,8 +556,8 @@
      * display content info to any SurfaceControl, as this would be a security issue.
      *
      * @param displayId The id of the display.
-     * @param surfaceControlHandle The SurfaceControl handle that the top level layers for the
+     * @param surfaceControlHandle The SurfaceControl that the top level layers for the
      *        display should be re-parented to.
      */
-    void reparentDisplayContent(int displayId, in IBinder surfaceControlHandle);
+    void reparentDisplayContent(int displayId, in SurfaceControl sc);
 }
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index f3cb376..7fcb2af 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -185,6 +185,18 @@
     }
 
     /**
+     * Create a Surface assosciated with a given {@link SurfaceControl}. Buffers submitted to this
+     * surface will be displayed by the system compositor according to the parameters
+     * specified by the control. Multiple surfaces may be constructed from one SurfaceControl,
+     * but only one can be connected (e.g. have an active EGL context) at a time.
+     *
+     * @param from The SurfaceControl to assosciate this Surface with
+     */
+    public Surface(SurfaceControl from) {
+        copyFrom(from);
+    }
+
+    /**
      * Create Surface from a {@link SurfaceTexture}.
      *
      * Images drawn to the Surface will be made available to the {@link
@@ -494,7 +506,6 @@
      * in to it.
      *
      * @param other {@link SurfaceControl} to copy from.
-     *
      * @hide
      */
     @UnsupportedAppUsage
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5e98236..863b717 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -27,6 +27,10 @@
 import static android.view.SurfaceControlProto.HASH_CODE;
 import static android.view.SurfaceControlProto.NAME;
 
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.Size;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.Bitmap;
@@ -58,10 +62,16 @@
 import java.io.Closeable;
 
 /**
- * SurfaceControl
- * @hide
+ * Handle to an on-screen Surface managed by the system compositor. The SurfaceControl is
+ * a combination of a buffer source, and metadata about how to display the buffers.
+ * By constructing a {@link Surface} from this SurfaceControl you can submit buffers to be
+ * composited. Using {@link SurfaceControl.Transaction} you can manipulate various
+ * properties of how the buffer will be displayed on-screen. SurfaceControl's are
+ * arranged into a scene-graph like hierarchy, and as such any SurfaceControl may have
+ * a parent. Geometric properties like transform, crop, and Z-ordering will be inherited
+ * from the parent, as if the child were content in the parents buffer stream.
  */
-public class SurfaceControl implements Parcelable {
+public final class SurfaceControl implements Parcelable {
     private static final String TAG = "SurfaceControl";
 
     private static native long nativeCreate(SurfaceSession session, String name,
@@ -103,6 +113,8 @@
             float dtdy, float dsdy);
     private static native void nativeSetColorTransform(long transactionObj, long nativeObject,
             float[] matrix, float[] translation);
+    private static native void nativeSetGeometry(long transactionObj, long nativeObject,
+            Rect sourceCrop, Rect dest, long orientation);
     private static native void nativeSetColor(long transactionObj, long nativeObject, float[] color);
     private static native void nativeSetFlags(long transactionObj, long nativeObject,
             int flags, int mask);
@@ -156,7 +168,7 @@
     private static native void nativeReparentChildren(long transactionObj, long nativeObject,
             IBinder handle);
     private static native void nativeReparent(long transactionObj, long nativeObject,
-            IBinder parentHandle);
+            long newParentNativeObject);
     private static native void nativeSeverChildren(long transactionObj, long nativeObject);
     private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject,
             int scalingMode);
@@ -331,8 +343,7 @@
      */
     public static final int BUILT_IN_DISPLAY_ID_HDMI = 1;
 
-    /* Display power modes * /
-
+    // Display power modes.
     /**
      * Display power mode off: used while blanking the screen.
      * Use only with {@link SurfaceControl#setDisplayPowerMode}.
@@ -403,7 +414,6 @@
 
     /**
      * Builder class for {@link SurfaceControl} objects.
-     * @hide
      */
     public static class Builder {
         private SurfaceSession mSession;
@@ -427,8 +437,14 @@
         }
 
         /**
-         * Construct a new {@link SurfaceControl} with the set parameters.
-         * @hide
+         * Begin building a SurfaceControl.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Construct a new {@link SurfaceControl} with the set parameters. The builder
+         * remains valid.
          */
         public SurfaceControl build() {
             if (mWidth < 0 || mHeight < 0) {
@@ -447,7 +463,6 @@
          * Set a debugging-name for the SurfaceControl.
          *
          * @param name A name to identify the Surface in debugging.
-         * @hide
          */
         public Builder setName(String name) {
             mName = name;
@@ -459,9 +474,9 @@
          *
          * @param width The buffer width in pixels.
          * @param height The buffer height in pixels.
-         * @hide
          */
-        public Builder setBufferSize(int width, int height) {
+        public Builder setBufferSize(@IntRange(from = 0) int width,
+                @IntRange(from = 0) int height) {
             if (width < 0 || height < 0) {
                 throw new IllegalArgumentException(
                         "width and height must be positive");
@@ -474,8 +489,8 @@
         /**
          * Set the pixel format of the controlled surface's buffers, using constants from
          * {@link android.graphics.PixelFormat}.
-         * @hide
          */
+        @NonNull
         public Builder setFormat(@PixelFormat.Format int format) {
             mFormat = format;
             return this;
@@ -490,6 +505,7 @@
          * @param protectedContent Whether to require a protected sink.
          * @hide
          */
+        @NonNull
         public Builder setProtected(boolean protectedContent) {
             if (protectedContent) {
                 mFlags |= PROTECTED_APP;
@@ -506,6 +522,7 @@
          * not a complete prevention of readback as {@link #setProtected}.
          * @hide
          */
+        @NonNull
         public Builder setSecure(boolean secure) {
             if (secure) {
                 mFlags |= SECURE;
@@ -537,8 +554,8 @@
          * If the underlying buffer lacks an alpha channel, it is as if setOpaque(true)
          * were set automatically.
          * @param opaque Whether the Surface is OPAQUE.
-         * @hide
          */
+        @NonNull
         public Builder setOpaque(boolean opaque) {
             if (opaque) {
                 mFlags |= OPAQUE;
@@ -556,9 +573,9 @@
          * of the parent.
          *
          * @param parent The parent control.
-         * @hide
          */
-        public Builder setParent(SurfaceControl parent) {
+        @NonNull
+        public Builder setParent(@Nullable SurfaceControl parent) {
             mParent = parent;
             return this;
         }
@@ -673,9 +690,6 @@
     private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
             SurfaceControl parent, int windowType, int ownerUid)
                     throws OutOfResourcesException, IllegalArgumentException {
-        if (session == null) {
-            throw new IllegalArgumentException("session must not be null");
-        }
         if (name == null) {
             throw new IllegalArgumentException("name must not be null");
         }
@@ -729,9 +743,6 @@
         mCloseGuard.open("release");
     }
 
-    /**
-     * @hide
-     */
     public void readFromParcel(Parcel in) {
         if (in == null) {
             throw new IllegalArgumentException("source must not be null");
@@ -748,17 +759,11 @@
         assignNativeObject(object);
     }
 
-    /**
-     * @hide
-     */
     @Override
     public int describeContents() {
         return 0;
     }
 
-    /**
-     * @hide
-     */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mName);
@@ -791,9 +796,6 @@
         proto.end(token);
     }
 
-    /**
-     * @hide
-     */
     public static final Creator<SurfaceControl> CREATOR
             = new Creator<SurfaceControl>() {
         public SurfaceControl createFromParcel(Parcel in) {
@@ -823,10 +825,12 @@
     }
 
     /**
-     * Release the local reference to the server-side surface.
-     * Always call release() when you're done with a Surface.
-     * This will make the surface invalid.
-     * @hide
+     * Release the local reference to the server-side surface. The surface
+     * may continue to exist on-screen as long as its parent continues
+     * to exist. To explicitly remove a surface from the screen use
+     * {@link Transaction#reparent} with a null-parent.
+     *
+     * Always call release() when you're done with a SurfaceControl.
      */
     public void release() {
         if (mNativeObject != 0) {
@@ -866,7 +870,10 @@
     }
 
     /**
-     * @hide
+     * Check whether this instance points to a valid layer with the system-compositor. For
+     * example this may be false if construction failed, or the layer was released.
+     *
+     * @return Whether this SurfaceControl is valid.
      */
     public boolean isValid() {
         return mNativeObject != 0;
@@ -962,9 +969,9 @@
     /**
      * @hide
      */
-    public void reparent(IBinder newParentHandle) {
+    public void reparent(SurfaceControl newParent) {
         synchronized(SurfaceControl.class) {
-            sGlobalTransaction.reparent(this, newParentHandle);
+            sGlobalTransaction.reparent(this, newParent);
         }
     }
 
@@ -1270,9 +1277,6 @@
         }
     }
 
-    /**
-     * @hide
-     */
     @Override
     public String toString() {
         return "Surface(name=" + mName + ")/@0x" +
@@ -1286,6 +1290,7 @@
 
     /**
      * Describes the properties of a physical display known to surface flinger.
+     * @hide
      */
     public static final class PhysicalDisplayInfo {
         /**
@@ -1777,9 +1782,12 @@
     }
 
     /**
-     * @hide
+     * An atomic set of changes to a set of SurfaceControl.
      */
     public static class Transaction implements Closeable {
+        /**
+         * @hide
+         */
         public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
                 Transaction.class.getClassLoader(),
                 nativeGetNativeTransactionFinalizer(), 512);
@@ -1789,9 +1797,12 @@
         Runnable mFreeNativeResources;
 
         /**
-         * @hide
+         * Open a new transaction object. The transaction may be filed with commands to
+         * manipulate {@link SurfaceControl} instances, and then applied atomically with
+         * {@link #apply}. Eventually the user should invoke {@link #close}, when the object
+         * is no longer required. Note however that re-using a transaction after a call to apply
+         * is allowed as a convenience.
          */
-        @UnsupportedAppUsage
         public Transaction() {
             mNativeObject = nativeCreateTransaction();
             mFreeNativeResources
@@ -1801,9 +1812,7 @@
         /**
          * Apply the transaction, clearing it's state, and making it usable
          * as a new transaction.
-         * @hide
          */
-        @UnsupportedAppUsage
         public void apply() {
             apply(false);
         }
@@ -1811,7 +1820,6 @@
         /**
          * Close the transaction, if the transaction was not already applied this will cancel the
          * transaction.
-         * @hide
          */
         @Override
         public void close() {
@@ -1841,6 +1849,27 @@
         }
 
         /**
+         * Toggle the visibility of a given Layer and it's sub-tree.
+         *
+         * @param sc The SurfaceControl for which to set the visibility
+         * @param visible The new visibility
+         * @return This transaction object.
+         */
+        @NonNull
+        public Transaction setVisibility(@NonNull SurfaceControl sc, boolean visible) {
+            sc.checkNotReleased();
+            if (visible) {
+                return show(sc);
+            } else {
+                return hide(sc);
+            }
+        }
+
+        /**
+         * Request that a given surface and it's sub-tree be shown.
+         *
+         * @param sc The surface to show.
+         * @return This transaction.
          * @hide
          */
         @UnsupportedAppUsage
@@ -1851,6 +1880,10 @@
         }
 
         /**
+         * Request that a given surface and it's sub-tree be hidden.
+         *
+         * @param sc The surface to hidden.
+         * @return This transaction.
          * @hide
          */
         @UnsupportedAppUsage
@@ -1871,10 +1904,17 @@
         }
 
         /**
-         * @hide
+         * Set the default buffer size for the SurfaceControl, if there is an
+         * {@link Surface} assosciated with the control, then
+         * this will be the default size for buffers dequeued from it.
+         * @param sc The surface to set the buffer size for.
+         * @param w The default width
+         * @param h The default height
+         * @return This Transaction
          */
-        @UnsupportedAppUsage
-        public Transaction setBufferSize(SurfaceControl sc, int w, int h) {
+        @NonNull
+        public Transaction setBufferSize(@NonNull SurfaceControl sc,
+                @IntRange(from = 0) int w, @IntRange(from = 0) int h) {
             sc.checkNotReleased();
             mResizedSurfaces.put(sc, new Point(w, h));
             nativeSetSize(mNativeObject, sc.mNativeObject, w, h);
@@ -1882,10 +1922,17 @@
         }
 
         /**
-         * @hide
+         * Set the Z-order for a given SurfaceControl, relative to it's siblings.
+         * If two siblings share the same Z order the ordering is undefined. Surfaces
+         * with a negative Z will be placed below the parent surface.
+         *
+         * @param sc The SurfaceControl to set the Z order on
+         * @param z The Z-order
+         * @return This Transaction.
          */
-        @UnsupportedAppUsage
-        public Transaction setLayer(SurfaceControl sc, int z) {
+        @NonNull
+        public Transaction setLayer(@NonNull SurfaceControl sc,
+                @IntRange(from = Integer.MIN_VALUE, to = Integer.MAX_VALUE) int z) {
             sc.checkNotReleased();
             nativeSetLayer(mNativeObject, sc.mNativeObject, z);
             return this;
@@ -1912,10 +1959,15 @@
         }
 
         /**
-         * @hide
+         * Set the alpha for a given surface. If the alpha is non-zero the SurfaceControl
+         * will be blended with the Surfaces under it according to the specified ratio.
+         *
+         * @param sc The given SurfaceControl.
+         * @param alpha The alpha to set.
          */
-        @UnsupportedAppUsage
-        public Transaction setAlpha(SurfaceControl sc, float alpha) {
+        @NonNull
+        public Transaction setAlpha(@NonNull SurfaceControl sc,
+                @FloatRange(from = 0.0, to = 1.0) float alpha) {
             sc.checkNotReleased();
             nativeSetAlpha(mNativeObject, sc.mNativeObject, alpha);
             return this;
@@ -1947,6 +1999,25 @@
         }
 
         /**
+         * Specify how the buffer assosciated with this Surface is mapped in to the
+         * parent coordinate space. The source frame will be scaled to fit the destination
+         * frame, after being rotated according to the orientation parameter.
+         *
+         * @param sc The SurfaceControl to specify the geometry of
+         * @param sourceCrop The source rectangle in buffer space. Or null for the entire buffer.
+         * @param destFrame The destination rectangle in parent space. Or null for the source frame.
+         * @param orientation The buffer rotation
+         * @return This transaction object.
+         */
+        @NonNull
+        public Transaction setGeometry(@NonNull SurfaceControl sc, @Nullable Rect sourceCrop,
+                @Nullable Rect destFrame, @Surface.Rotation int orientation) {
+            sc.checkNotReleased();
+            nativeSetGeometry(mNativeObject, sc.mNativeObject, sourceCrop, destFrame, orientation);
+            return this;
+        }
+
+        /**
          * @hide
          */
         @UnsupportedAppUsage
@@ -2023,20 +2094,20 @@
             return this;
         }
 
-        @UnsupportedAppUsage
         /**
          * @hide
          */
+        @UnsupportedAppUsage
         public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
             sc.checkNotReleased();
             nativeSetLayerStack(mNativeObject, sc.mNativeObject, layerStack);
             return this;
         }
 
-        @UnsupportedAppUsage
         /**
          * @hide
          */
+        @UnsupportedAppUsage
         public Transaction deferTransactionUntil(SurfaceControl sc, IBinder handle,
                 long frameNumber) {
             if (frameNumber < 0) {
@@ -2047,10 +2118,10 @@
             return this;
         }
 
-        @UnsupportedAppUsage
         /**
          * @hide
          */
+        @UnsupportedAppUsage
         public Transaction deferTransactionUntilSurface(SurfaceControl sc, Surface barrierSurface,
                 long frameNumber) {
             if (frameNumber < 0) {
@@ -2071,13 +2142,25 @@
             return this;
         }
 
-        /** Re-parents a specific child layer to a new parent 
-         * @hide
+        /**
+         * Re-parents a given layer to a new parent. Children inherit transform (position, scaling)
+         * crop, visibility, and Z-ordering from their parents, as if the children were pixels within the
+         * parent Surface.
+         *
+         * @param sc The SurfaceControl to reparent
+         * @param newParent The new parent for the given control.
+         * @return This Transaction
          */
-        public Transaction reparent(SurfaceControl sc, IBinder newParentHandle) {
+        @NonNull
+        public Transaction reparent(@NonNull SurfaceControl sc,
+                @Nullable SurfaceControl newParent) {
             sc.checkNotReleased();
-            nativeReparent(mNativeObject, sc.mNativeObject,
-                    newParentHandle);
+            long otherObject = 0;
+            if (newParent != null) {
+                newParent.checkNotReleased();
+                otherObject = newParent.mNativeObject;
+            }
+            nativeReparent(mNativeObject, sc.mNativeObject, otherObject);
             return this;
         }
 
@@ -2245,9 +2328,12 @@
         /**
          * Merge the other transaction into this transaction, clearing the
          * other transaction as if it had been applied.
-         * @hide
+         *
+         * @param other The transaction to merge in to this one.
+         * @return This transaction.
          */
-        public Transaction merge(Transaction other) {
+        @NonNull
+        public Transaction merge(@NonNull Transaction other) {
             mResizedSurfaces.putAll(other.mResizedSurfaces);
             other.mResizedSurfaces.clear();
             nativeMergeTransaction(mNativeObject, other.mNativeObject);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 61fb00d..45e6c50 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -120,10 +120,11 @@
     final Rect mScreenRect = new Rect();
     SurfaceSession mSurfaceSession;
 
-    SurfaceControlWithBackground mSurfaceControl;
+    SurfaceControl mSurfaceControl;
     // In the case of format changes we switch out the surface in-place
     // we need to preserve the old one until the new one has drawn.
     SurfaceControl mDeferredDestroySurfaceControl;
+    SurfaceControl mBackgroundControl;
     final Rect mTmpRect = new Rect();
     final Configuration mConfiguration = new Configuration();
 
@@ -487,6 +488,29 @@
         }
     }
 
+    private void updateBackgroundVisibilityInTransaction() {
+        if (mBackgroundControl == null) {
+            return;
+        }
+        if ((mSurfaceFlags & PixelFormat.OPAQUE) == 0) {
+            mBackgroundControl.show();
+            mBackgroundControl.setLayer(Integer.MIN_VALUE);
+        } else {
+            mBackgroundControl.hide();
+        }
+    }
+
+    private void releaseSurfaces() {
+        if (mSurfaceControl != null) {
+            mSurfaceControl.destroy();
+            mSurfaceControl = null;
+        }
+        if (mBackgroundControl != null) {
+            mBackgroundControl.destroy();
+            mBackgroundControl = null;
+        }
+    }
+
     /** @hide */
     protected void updateSurface() {
         if (!mHaveFrame) {
@@ -553,14 +577,21 @@
                     updateOpaqueFlag();
                     final String name = "SurfaceView - " + viewRoot.getTitle().toString();
 
-                    mSurfaceControl = new SurfaceControlWithBackground(
-                            name,
-                            (mSurfaceFlags & SurfaceControl.OPAQUE) != 0,
-                            new SurfaceControl.Builder(mSurfaceSession)
-                                    .setBufferSize(mSurfaceWidth, mSurfaceHeight)
-                                    .setFormat(mFormat)
-                                    .setParent(viewRoot.getSurfaceControl())
-                                    .setFlags(mSurfaceFlags));
+                    mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
+                        .setName(name)
+                        .setOpaque((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
+                        .setBufferSize(mSurfaceWidth, mSurfaceHeight)
+                        .setFormat(mFormat)
+                        .setParent(viewRoot.getSurfaceControl())
+                        .setFlags(mSurfaceFlags)
+                        .build();
+                    mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
+                        .setName("Background for -" + name)
+                        .setOpaque(true)
+                        .setColorLayer(true)
+                        .setParent(mSurfaceControl)
+                        .build();
+
                 } else if (mSurfaceControl == null) {
                     return;
                 }
@@ -577,11 +608,13 @@
                     SurfaceControl.openTransaction();
                     try {
                         mSurfaceControl.setLayer(mSubLayer);
+
                         if (mViewVisibility) {
                             mSurfaceControl.show();
                         } else {
                             mSurfaceControl.hide();
                         }
+                        updateBackgroundVisibilityInTransaction();
 
                         // While creating the surface, we will set it's initial
                         // geometry. Outside of that though, we should generally
@@ -724,8 +757,7 @@
                     if (mSurfaceControl != null && !mSurfaceCreated) {
                         mSurface.release();
 
-                        mSurfaceControl.destroy();
-                        mSurfaceControl = null;
+                        releaseSurfaces();
                     }
                 }
             } catch (Exception ex) {
@@ -823,7 +855,6 @@
         final ViewRootImpl viewRoot = getViewRootImpl();
 
         applySurfaceTransforms(mSurfaceControl, position, frameNumber);
-        applySurfaceTransforms(mSurfaceControl.mBackgroundControl, position, frameNumber);
 
         applyChildSurfaceTransaction_renderWorker(mRtTransaction, viewRoot.mSurface,
                 frameNumber);
@@ -950,7 +981,19 @@
      * @hide
      */
     public void setResizeBackgroundColor(int bgColor) {
-        mSurfaceControl.setBackgroundColor(bgColor);
+        if (mBackgroundControl == null) {
+            return;
+        }
+
+        final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f,
+                Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f };
+
+        SurfaceControl.openTransaction();
+        try {
+            mBackgroundControl.setColor(colorComponents);
+        } finally {
+            SurfaceControl.closeTransaction();
+        }
     }
 
     @UnsupportedAppUsage
@@ -1128,154 +1171,12 @@
     };
 
     /**
-     * @hide
+     * Return a SurfaceControl which can be used for parenting Surfaces to
+     * this SurfaceView.
+     *
+     * @return The SurfaceControl for this SurfaceView.
      */
     public SurfaceControl getSurfaceControl() {
         return mSurfaceControl;
     }
-
-    class SurfaceControlWithBackground extends SurfaceControl {
-        SurfaceControl mBackgroundControl;
-        private boolean mOpaque = true;
-        public boolean mVisible = false;
-
-        public SurfaceControlWithBackground(String name, boolean opaque, SurfaceControl.Builder b)
-                       throws Exception {
-            super(b.setName(name).build());
-
-            mBackgroundControl = b.setName("Background for -" + name)
-                    .setFormat(OPAQUE)
-                    // Unset the buffer size of the background color layer.
-                    .setBufferSize(0, 0)
-                    .setColorLayer(true)
-                    .build();
-            mOpaque = opaque;
-        }
-
-        @Override
-        public void setAlpha(float alpha) {
-            super.setAlpha(alpha);
-            mBackgroundControl.setAlpha(alpha);
-        }
-
-        @Override
-        public void setLayer(int zorder) {
-            super.setLayer(zorder);
-            // -3 is below all other child layers as SurfaceView never goes below -2
-            mBackgroundControl.setLayer(-3);
-        }
-
-        @Override
-        public void setPosition(float x, float y) {
-            super.setPosition(x, y);
-            mBackgroundControl.setPosition(x, y);
-        }
-
-        @Override
-        public void setBufferSize(int w, int h) {
-            super.setBufferSize(w, h);
-            // The background surface is a color layer so we do not set a size.
-        }
-
-        @Override
-        public void setWindowCrop(Rect crop) {
-            super.setWindowCrop(crop);
-            mBackgroundControl.setWindowCrop(crop);
-        }
-
-        @Override
-        public void setWindowCrop(int width, int height) {
-            super.setWindowCrop(width, height);
-            mBackgroundControl.setWindowCrop(width, height);
-        }
-
-        @Override
-        public void setLayerStack(int layerStack) {
-            super.setLayerStack(layerStack);
-            mBackgroundControl.setLayerStack(layerStack);
-        }
-
-        @Override
-        public void setOpaque(boolean isOpaque) {
-            super.setOpaque(isOpaque);
-            mOpaque = isOpaque;
-            updateBackgroundVisibility();
-        }
-
-        @Override
-        public void setSecure(boolean isSecure) {
-            super.setSecure(isSecure);
-        }
-
-        @Override
-        public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
-            super.setMatrix(dsdx, dtdx, dsdy, dtdy);
-            mBackgroundControl.setMatrix(dsdx, dtdx, dsdy, dtdy);
-        }
-
-        @Override
-        public void hide() {
-            super.hide();
-            mVisible = false;
-            updateBackgroundVisibility();
-        }
-
-        @Override
-        public void show() {
-            super.show();
-            mVisible = true;
-            updateBackgroundVisibility();
-        }
-
-        @Override
-        public void destroy() {
-            super.destroy();
-            mBackgroundControl.destroy();
-         }
-
-        @Override
-        public void release() {
-            super.release();
-            mBackgroundControl.release();
-        }
-
-        @Override
-        public void setTransparentRegionHint(Region region) {
-            super.setTransparentRegionHint(region);
-            mBackgroundControl.setTransparentRegionHint(region);
-        }
-
-        @Override
-        public void deferTransactionUntil(IBinder handle, long frame) {
-            super.deferTransactionUntil(handle, frame);
-            mBackgroundControl.deferTransactionUntil(handle, frame);
-        }
-
-        @Override
-        public void deferTransactionUntil(Surface barrier, long frame) {
-            super.deferTransactionUntil(barrier, frame);
-            mBackgroundControl.deferTransactionUntil(barrier, frame);
-        }
-
-        /** Set the color to fill the background with. */
-        private void setBackgroundColor(int bgColor) {
-            final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f,
-                    Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f };
-
-            SurfaceControl.openTransaction();
-            try {
-                mBackgroundControl.setColor(colorComponents);
-            } finally {
-                SurfaceControl.closeTransaction();
-            }
-        }
-
-        void updateBackgroundVisibility() {
-            if (mOpaque && mVisible) {
-                mBackgroundControl.show();
-            } else {
-                mBackgroundControl.hide();
-            }
-        }
-    }
 }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 86c5f18..10b99ec 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -922,19 +922,6 @@
         }
     }
 
-    /**
-     * Returns a list of VR InputMethod currently installed.
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
-    public List<InputMethodInfo> getVrInputMethodList() {
-        try {
-            return mService.getVrInputMethodList();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
     public List<InputMethodInfo> getEnabledInputMethodList() {
         try {
             return mService.getEnabledInputMethodList();
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 356d178..8194a92 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -35,7 +35,6 @@
 
     // TODO: Use ParceledListSlice instead
     List<InputMethodInfo> getInputMethodList();
-    List<InputMethodInfo> getVrInputMethodList();
     // TODO: Use ParceledListSlice instead
     List<InputMethodInfo> getEnabledInputMethodList();
     List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in String imiId,
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index d5dc703..8d3c482 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -34,6 +34,7 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.os.AsyncTask;
 import android.os.Handler;
@@ -176,6 +177,7 @@
     private UserManager mUserManager;
     private final Handler mHandler;
     private final SparseLongArray mLockoutDeadlines = new SparseLongArray();
+    private Boolean mHasSecureLockScreen;
 
     /**
      * Use {@link TrustManager#isTrustUsuallyManaged(int)}.
@@ -706,6 +708,10 @@
      * @param userId the user whose pattern is to be saved.
      */
     public void saveLockPattern(List<LockPatternView.Cell> pattern, String savedPattern, int userId) {
+        if (!hasSecureLockScreen()) {
+            throw new UnsupportedOperationException(
+                    "This operation requires the lock screen feature.");
+        }
         if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
             throw new IllegalArgumentException("pattern must not be null and at least "
                     + MIN_LOCK_PATTERN_SIZE + " dots long.");
@@ -801,6 +807,10 @@
 
     /** Update the encryption password if it is enabled **/
     private void updateEncryptionPassword(final int type, final String password) {
+        if (!hasSecureLockScreen()) {
+            throw new UnsupportedOperationException(
+                    "This operation requires the lock screen feature.");
+        }
         if (!isDeviceEncryptionEnabled()) {
             return;
         }
@@ -835,6 +845,10 @@
      */
     public void saveLockPassword(String password, String savedPassword, int requestedQuality,
             int userHandle) {
+        if (!hasSecureLockScreen()) {
+            throw new UnsupportedOperationException(
+                    "This operation requires the lock screen feature.");
+        }
         if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) {
             throw new IllegalArgumentException("password must not be null and at least "
                     + "of length " + MIN_LOCK_PASSWORD_SIZE);
@@ -1621,6 +1635,10 @@
      */
     public boolean setLockCredentialWithToken(String credential, int type, int requestedQuality,
             long tokenHandle, byte[] token, int userId) {
+        if (!hasSecureLockScreen()) {
+            throw new UnsupportedOperationException(
+                    "This operation requires the lock screen feature.");
+        }
         LockSettingsInternal localService = getLockSettingsInternal();
         if (type != CREDENTIAL_TYPE_NONE) {
             if (TextUtils.isEmpty(credential) || credential.length() < MIN_LOCK_PASSWORD_SIZE) {
@@ -1854,6 +1872,17 @@
         return getLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 0, UserHandle.USER_SYSTEM) != 0;
     }
 
+    /**
+     * Return true if the device supports the lock screen feature, false otherwise.
+     */
+    public boolean hasSecureLockScreen() {
+        if (mHasSecureLockScreen == null) {
+            mHasSecureLockScreen = Boolean.valueOf(mContext.getPackageManager()
+                    .hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN));
+        }
+        return mHasSecureLockScreen.booleanValue();
+    }
+
     public static boolean userOwnsFrpCredential(Context context, UserInfo info) {
         return info != null && info.isPrimary() && info.isAdmin() && frpCredentialEnabled(context);
     }
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 9b138eb..7eddcfe 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -16,8 +16,11 @@
 
 #define LOG_TAG "NetUtils"
 
+#include <vector>
+
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
 #include "NetdClient.h"
 #include <utils/misc.h>
 #include <android_runtime/AndroidRuntime.h>
@@ -55,6 +58,31 @@
 static const uint32_t kUDPDstPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, dest);
 static const uint16_t kDhcpClientPort = 68;
 
+constexpr int MAXPACKETSIZE = 8 * 1024;
+// FrameworkListener limits the size of commands to 1024 bytes. TODO: fix this.
+constexpr int MAXCMDSIZE = 1024;
+
+static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
+    ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
+    if (detailMessage.get() == NULL) {
+        // Not really much we can do here. We're probably dead in the water,
+        // but let's try to stumble on...
+        env->ExceptionClear();
+    }
+    static jclass errnoExceptionClass =
+            MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException"));
+
+    static jmethodID errnoExceptionCtor =
+            GetMethodIDOrDie(env, errnoExceptionClass,
+            "<init>", "(Ljava/lang/String;I)V");
+
+    jobject exception = env->NewObject(errnoExceptionClass,
+                                       errnoExceptionCtor,
+                                       detailMessage.get(),
+                                       error);
+    env->Throw(reinterpret_cast<jthrowable>(exception));
+}
+
 static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd)
 {
     struct sock_filter filter_code[] = {
@@ -372,6 +400,63 @@
     }
 }
 
+static jobject android_net_utils_resNetworkQuery(JNIEnv *env, jobject thiz, jint netId,
+        jstring dname, jint ns_class, jint ns_type, jint flags) {
+    const jsize javaCharsCount = env->GetStringLength(dname);
+    const jsize byteCountUTF8 = env->GetStringUTFLength(dname);
+
+    // Only allow dname which could be simply formatted to UTF8.
+    // In native layer, res_mkquery would re-format the input char array to packet.
+    std::vector<char> queryname(byteCountUTF8 + 1, 0);
+
+    env->GetStringUTFRegion(dname, 0, javaCharsCount, queryname.data());
+    int fd = resNetworkQuery(netId, queryname.data(), ns_class, ns_type, flags);
+
+    if (fd < 0) {
+        throwErrnoException(env, "resNetworkQuery", -fd);
+        return nullptr;
+    }
+
+    return jniCreateFileDescriptor(env, fd);
+}
+
+static jobject android_net_utils_resNetworkSend(JNIEnv *env, jobject thiz, jint netId,
+        jbyteArray msg, jint msgLen, jint flags) {
+    uint8_t data[MAXCMDSIZE];
+
+    checkLenAndCopy(env, msg, msgLen, data);
+    int fd = resNetworkSend(netId, data, msgLen, flags);
+
+    if (fd < 0) {
+        throwErrnoException(env, "resNetworkSend", -fd);
+        return nullptr;
+    }
+
+    return jniCreateFileDescriptor(env, fd);
+}
+
+static jbyteArray android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) {
+    int fd = jniGetFDFromFileDescriptor(env, javaFd);
+    int rcode;
+    std::vector<uint8_t> buf(MAXPACKETSIZE, 0);
+
+    int res = resNetworkResult(fd, &rcode, buf.data(), MAXPACKETSIZE);
+    if (res < 0) {
+        throwErrnoException(env, "resNetworkResult", -res);
+        return nullptr;
+    }
+
+    jbyteArray answer = env->NewByteArray(res);
+    if (answer == nullptr) {
+        throwErrnoException(env, "resNetworkResult", ENOMEM);
+        return nullptr;
+    } else {
+        env->SetByteArrayRegion(answer, 0, res,
+                reinterpret_cast<jbyte*>(buf.data()));
+    }
+
+    return answer;
+}
 
 // ----------------------------------------------------------------------------
 
@@ -391,6 +476,9 @@
     { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter },
     { "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachControlPacketFilter },
     { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket },
+    { "resNetworkSend", "(I[BII)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkSend },
+    { "resNetworkQuery", "(ILjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery },
+    { "resNetworkResult", "(Ljava/io/FileDescriptor;)[B", (void*) android_net_utils_resNetworkResult },
 };
 
 int register_android_net_NetworkUtils(JNIEnv* env)
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 897427f..0453195 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -126,7 +126,12 @@
         jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
         jint windowType, jint ownerUid) {
     ScopedUtfChars name(env, nameStr);
-    sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
+    sp<SurfaceComposerClient> client;
+    if (sessionObj != NULL) {
+        client = android_view_SurfaceSession_getClient(env, sessionObj);
+    } else {
+        client = SurfaceComposerClient::getDefault();
+    }
     SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
     sp<SurfaceControl> surface;
     status_t err = client->createSurfaceChecked(
@@ -277,6 +282,21 @@
     transaction->setPosition(ctrl, x, y);
 }
 
+static void nativeSetGeometry(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
+        jobject sourceObj, jobject dstObj, jlong orientation) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+
+    Rect source, dst;
+    if (sourceObj != NULL) {
+        source = rectFromObj(env, sourceObj);
+    }
+    if (dstObj != NULL) {
+        dst = rectFromObj(env, dstObj);
+    }
+    transaction->setGeometry(ctrl, source, dst, orientation);
+}
+
 static void nativeSetGeometryAppliesWithResize(JNIEnv* env, jclass clazz,
 jlong transactionObj,
         jlong nativeObject) {
@@ -868,13 +888,13 @@
 
 static void nativeReparent(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject,
-        jobject newParentObject) {
+        jlong newParentObject) {
     auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    sp<IBinder> parentHandle = ibinderForJavaObject(env, newParentObject);
+    auto newParent = reinterpret_cast<SurfaceControl *>(newParentObject);
 
     {
         auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
-        transaction->reparent(ctrl, parentHandle);
+        transaction->reparent(ctrl, newParent != NULL ? newParent->getHandle() : NULL);
     }
 }
 
@@ -1063,7 +1083,7 @@
             (void*)nativeDeferTransactionUntilSurface },
     {"nativeReparentChildren", "(JJLandroid/os/IBinder;)V",
             (void*)nativeReparentChildren } ,
-    {"nativeReparent", "(JJLandroid/os/IBinder;)V",
+    {"nativeReparent", "(JJJ)V",
             (void*)nativeReparent },
     {"nativeSeverChildren", "(JJ)V",
             (void*)nativeSeverChildren } ,
@@ -1087,6 +1107,8 @@
     {"nativeGetDisplayedContentSample",
             "(Landroid/os/IBinder;JJ)Landroid/hardware/display/DisplayedContentSample;",
             (void*)nativeGetDisplayedContentSample },
+    {"nativeSetGeometry", "(JJLandroid/graphics/Rect;Landroid/graphics/Rect;J)V",
+            (void*)nativeSetGeometry }
 };
 
 int register_android_view_SurfaceControl(JNIEnv* env)
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7028008..2e7184b 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -493,10 +493,7 @@
   // Apply system or app filter based on uid.
   if (uid >= AID_APP_START) {
     if (is_child_zygote) {
-      // set_app_zygote_seccomp_filter();
-      // TODO(b/111434506) install the filter; for now, install the app filter
-      // which is more restrictive.
-      set_app_seccomp_filter();
+      set_app_zygote_seccomp_filter();
     } else {
       set_app_seccomp_filter();
     }
@@ -1645,14 +1642,10 @@
     return;
   }
 
-  // TODO(b/111434506) install the filter
-
-  /*
   bool installed = install_setuidgid_seccomp_filter(uidGidMin, uidGidMax);
   if (!installed) {
       RuntimeAbort(env, __LINE__, "Could not install setuid/setgid seccomp filter.");
   }
-  */
 }
 
 /**
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 7f3ea7a..e68f9db 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -221,6 +221,8 @@
     optional bool use_heartbeats = 23;
 
     message TimeController {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         // Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
         // ready now.
         optional bool skip_not_ready_jobs = 1;
@@ -228,6 +230,8 @@
     optional TimeController time_controller = 25;
 
     message QuotaController {
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
         // How much time each app will have to run jobs within their standby bucket window.
         optional int64 allowed_time_per_period_ms = 1;
         // How much time the package should have before transitioning from out-of-quota to in-quota.
@@ -251,6 +255,21 @@
         optional int64 rare_window_size_ms = 6;
         // The maximum amount of time an app can have its jobs running within a 24 hour window.
         optional int64 max_execution_time_ms = 7;
+        // The maximum number of jobs an app can run within this particular standby bucket's
+        // window size.
+        optional int32 max_job_count_active = 8;
+        // The maximum number of jobs an app can run within this particular standby bucket's
+        // window size.
+        optional int32 max_job_count_working = 9;
+        // The maximum number of jobs an app can run within this particular standby bucket's
+        // window size.
+        optional int32 max_job_count_frequent = 10;
+        // The maximum number of jobs an app can run within this particular standby bucket's
+        // window size.
+        optional int32 max_job_count_rare = 11;
+        // The maximum number of jobs that should be allowed to run in the past
+        // {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS}.
+        optional int32 max_job_count_per_allowed_time = 12;
     }
     optional QuotaController quota_controller = 24;
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ea0c8e2..96b8dc2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1748,6 +1748,10 @@
     <permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED"
         android:protectionLevel="signature" />
 
+    <!-- @hide Allows the device to be reset, clearing all data and enables Test Harness Mode. -->
+    <permission android:name="android.permission.ENABLE_TEST_HARNESS_MODE"
+        android:protectionLevel="signature" />
+
     <!-- ================================== -->
     <!-- Permissions for accessing accounts -->
     <!-- ================================== -->
diff --git a/core/tests/coretests/src/com/android/server/wm/test/filters/CoreTestsFilter.java b/core/tests/coretests/src/com/android/server/wm/test/filters/CoreTestsFilter.java
new file mode 100644
index 0000000..1a81c2c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/server/wm/test/filters/CoreTestsFilter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.test.filters;
+
+import android.os.Bundle;
+
+import com.android.test.filters.SelectTest;
+
+/**
+ * JUnit test filter that select Window Manager Service related tests from FrameworksCoreTests.
+ *
+ * <p>Use this filter when running FrameworksCoreTests as
+ * <pre>
+ * adb shell am instrument -w \
+ *     -e filter com.android.server.wm.test.filters.CoreTestsFilter  \
+ *     -e selectTest_verbose true \
+ *     com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
+ */
+public final class CoreTestsFilter extends SelectTest {
+
+    private static final String[] SELECTED_CORE_TESTS = {
+            "android.app.servertransaction.", // all tests under the package.
+            "android.view.DisplayCutoutTest",
+            "android.view.InsetsControllerTest",
+            "android.view.InsetsSourceTest",
+            "android.view.InsetsSourceConsumerTest",
+            "android.view.InsetsStateTest",
+    };
+
+    public CoreTestsFilter(Bundle testArgs) {
+        super(addSelectTest(testArgs, SELECTED_CORE_TESTS));
+    }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
index 03cf3eb..9913531 100644
--- a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
@@ -21,6 +21,7 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
@@ -67,6 +68,7 @@
         // TODO(b/63758238): stop spying the class under test
         mLockPatternUtils = spy(new LockPatternUtils(context));
         when(mLockPatternUtils.getLockSettings()).thenReturn(ils);
+        doReturn(true).when(mLockPatternUtils).hasSecureLockScreen();
 
         final UserInfo userInfo = Mockito.mock(UserInfo.class);
         when(userInfo.isDemo()).thenReturn(isDemoUser);
diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
index 52771e4..1d763ce 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -356,11 +356,11 @@
         dest.writeInt(mDeviceSource);
         dest.writeInt(mClientEffects.length);
         for (int i = 0; i < mClientEffects.length; i++) {
-            mClientEffects[i].writeToParcel(dest, 0);
+            mClientEffects[i].writeToParcel(dest);
         }
         dest.writeInt(mDeviceEffects.length);
         for (int i = 0; i < mDeviceEffects.length; i++) {
-            mDeviceEffects[i].writeToParcel(dest, 0);
+            mDeviceEffects[i].writeToParcel(dest);
         }
     }
 
@@ -375,13 +375,13 @@
         mClientPortId = in.readInt();
         mClientSilenced = in.readBoolean();
         mDeviceSource = in.readInt();
-        mClientEffects = AudioEffect.Descriptor.CREATOR.newArray(in.readInt());
+        mClientEffects = new AudioEffect.Descriptor[in.readInt()];
         for (int i = 0; i < mClientEffects.length; i++) {
-            mClientEffects[i] = AudioEffect.Descriptor.CREATOR.createFromParcel(in);
+            mClientEffects[i] = new AudioEffect.Descriptor(in);
         }
-        mDeviceEffects = AudioEffect.Descriptor.CREATOR.newArray(in.readInt());
-        for (int i = 0; i < mClientEffects.length; i++) {
-            mDeviceEffects[i] = AudioEffect.Descriptor.CREATOR.createFromParcel(in);
+        mDeviceEffects = new AudioEffect.Descriptor[in.readInt()];
+        for (int i = 0; i < mDeviceEffects.length; i++) {
+            mDeviceEffects[i] = new AudioEffect.Descriptor(in);
         }
     }
 
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index 814bc72..887b447 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -45,7 +45,7 @@
 
 /**
  * Allows an app to interact with an active {@link MediaSession2} or a
- * MediaSession2Service which would provide {@link MediaSession2}. Media buttons and other
+ * {@link MediaSession2Service} which would provide {@link MediaSession2}. Media buttons and other
  * commands can be sent to the session.
  * <p>
  * This API is not generally intended for third party application developers.
@@ -53,7 +53,6 @@
  * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
  * for consistent behavior across all devices.
  */
-// TODO: use @link for MediaSession2Service
 public class MediaController2 implements AutoCloseable {
     static final String TAG = "MediaController2";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -325,7 +324,7 @@
                     MediaController2.this, command, args);
             if (resultReceiver != null) {
                 if (result == null) {
-                    throw new RuntimeException("onSessionCommand shouldn't return null");
+                    resultReceiver.send(Session2Command.RESULT_INFO_SKIPPED, null);
                 } else {
                     resultReceiver.send(result.getResultCode(), result.getResultData());
                 }
@@ -442,8 +441,8 @@
          * @param controller the controller for this event
          * @param command the session command
          * @param args optional arguments
-         * @return the result for the session command. A runtime exception will be thrown if null
-         *         is returned.
+         * @return the result for the session command. If {@code null}, RESULT_INFO_SKIPPED
+         *         will be sent to the session.
          */
         @Nullable
         public Session2Command.Result onSessionCommand(@NonNull MediaController2 controller,
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 76ef27a..fdd07fd 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -397,7 +397,7 @@
                     MediaSession2.this, controllerInfo, command, args);
             if (resultReceiver != null) {
                 if (result == null) {
-                    throw new RuntimeException("onSessionCommand shouldn't return null");
+                    resultReceiver.send(Session2Command.RESULT_INFO_SKIPPED, null);
                 } else {
                     resultReceiver.send(result.getResultCode(), result.getResultData());
                 }
@@ -753,8 +753,8 @@
          * @param controller controller information
          * @param command the session command
          * @param args optional arguments
-         * @return the result for the session command. A runtime exception will be thrown if null
-         *         is returned.
+         * @return the result for the session command. If {@code null}, RESULT_INFO_SKIPPED
+         *         will be sent to the session.
          */
         @Nullable
         public Session2Command.Result onSessionCommand(@NonNull MediaSession2 session,
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 3b51c82..325420b 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -21,18 +21,14 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.media.session.MediaSession;
 import android.media.session.MediaSessionLegacyHelper;
 import android.media.session.PlaybackState;
-import android.media.session.MediaSession;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.Looper;
-import android.os.Message;
 import android.os.SystemClock;
 import android.util.Log;
 
-import java.lang.IllegalArgumentException;
-
 /**
  * RemoteControlClient enables exposing information meant to be consumed by remote controls
  * capable of displaying metadata, artwork and media transport control buttons.
@@ -682,7 +678,7 @@
 
                 // USE_SESSIONS
                 if (mSession != null) {
-                    int pbState = PlaybackState.getStateFromRccState(state);
+                    int pbState = getStateFromRccState(state);
                     long position = hasPosition ? mPlaybackPositionMs
                             : PlaybackState.PLAYBACK_POSITION_UNKNOWN;
 
@@ -718,8 +714,7 @@
             // USE_SESSIONS
             if (mSession != null) {
                 PlaybackState.Builder bob = new PlaybackState.Builder(mSessionPlaybackState);
-                bob.setActions(
-                        PlaybackState.getActionsFromRccControlFlags(transportControlFlags));
+                bob.setActions(getActionsFromRccControlFlags(transportControlFlags));
                 mSessionPlaybackState = bob.build();
                 mSession.setPlaybackState(mSessionPlaybackState);
             }
@@ -1001,16 +996,19 @@
      * Period for playback position drift checks, 15s when playing at 1x or slower.
      */
     private final static long POSITION_REFRESH_PERIOD_PLAYING_MS = 15000;
+
     /**
      * Minimum period for playback position drift checks, never more often when every 2s, when
      * fast forwarding or rewinding.
      */
     private final static long POSITION_REFRESH_PERIOD_MIN_MS = 2000;
+
     /**
      * The value above which the difference between client-reported playback position and
      * estimated position is considered a drift.
      */
     private final static long POSITION_DRIFT_MAX_MS = 500;
+
     /**
      * Compute the period at which the estimated playback position should be compared against the
      * actual playback position. Is a funciton of playback speed.
@@ -1025,4 +1023,151 @@
                     POSITION_REFRESH_PERIOD_MIN_MS);
         }
     }
+
+    /**
+     * Get the {@link PlaybackState} state for the given
+     * {@link RemoteControlClient} state.
+     *
+     * @param rccState The state used by {@link RemoteControlClient}.
+     * @return The equivalent state used by {@link PlaybackState}.
+     */
+    private static int getStateFromRccState(int rccState) {
+        switch (rccState) {
+            case PLAYSTATE_BUFFERING:
+                return PlaybackState.STATE_BUFFERING;
+            case PLAYSTATE_ERROR:
+                return PlaybackState.STATE_ERROR;
+            case PLAYSTATE_FAST_FORWARDING:
+                return PlaybackState.STATE_FAST_FORWARDING;
+            case PLAYSTATE_NONE:
+                return PlaybackState.STATE_NONE;
+            case PLAYSTATE_PAUSED:
+                return PlaybackState.STATE_PAUSED;
+            case PLAYSTATE_PLAYING:
+                return PlaybackState.STATE_PLAYING;
+            case PLAYSTATE_REWINDING:
+                return PlaybackState.STATE_REWINDING;
+            case PLAYSTATE_SKIPPING_BACKWARDS:
+                return PlaybackState.STATE_SKIPPING_TO_PREVIOUS;
+            case PLAYSTATE_SKIPPING_FORWARDS:
+                return PlaybackState.STATE_SKIPPING_TO_NEXT;
+            case PLAYSTATE_STOPPED:
+                return PlaybackState.STATE_STOPPED;
+            default:
+                return -1;
+        }
+    }
+
+    /**
+     * Get the {@link RemoteControlClient} state for the given
+     * {@link PlaybackState} state.
+     *
+     * @param state The state used by {@link PlaybackState}.
+     * @return The equivalent state used by {@link RemoteControlClient}.
+     */
+    static int getRccStateFromState(int state) {
+        switch (state) {
+            case PlaybackState.STATE_BUFFERING:
+                return PLAYSTATE_BUFFERING;
+            case PlaybackState.STATE_ERROR:
+                return PLAYSTATE_ERROR;
+            case PlaybackState.STATE_FAST_FORWARDING:
+                return PLAYSTATE_FAST_FORWARDING;
+            case PlaybackState.STATE_NONE:
+                return PLAYSTATE_NONE;
+            case PlaybackState.STATE_PAUSED:
+                return PLAYSTATE_PAUSED;
+            case PlaybackState.STATE_PLAYING:
+                return PLAYSTATE_PLAYING;
+            case PlaybackState.STATE_REWINDING:
+                return PLAYSTATE_REWINDING;
+            case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
+                return PLAYSTATE_SKIPPING_BACKWARDS;
+            case PlaybackState.STATE_SKIPPING_TO_NEXT:
+                return PLAYSTATE_SKIPPING_FORWARDS;
+            case PlaybackState.STATE_STOPPED:
+                return PLAYSTATE_STOPPED;
+            default:
+                return -1;
+        }
+    }
+
+    private static long getActionsFromRccControlFlags(int rccFlags) {
+        long actions = 0;
+        long flag = 1;
+        while (flag <= rccFlags) {
+            if ((flag & rccFlags) != 0) {
+                actions |= getActionForRccFlag((int) flag);
+            }
+            flag = flag << 1;
+        }
+        return actions;
+    }
+
+    static int getRccControlFlagsFromActions(long actions) {
+        int rccFlags = 0;
+        long action = 1;
+        while (action <= actions && action < Integer.MAX_VALUE) {
+            if ((action & actions) != 0) {
+                rccFlags |= getRccFlagForAction(action);
+            }
+            action = action << 1;
+        }
+        return rccFlags;
+    }
+
+    private static long getActionForRccFlag(int flag) {
+        switch (flag) {
+            case FLAG_KEY_MEDIA_PREVIOUS:
+                return PlaybackState.ACTION_SKIP_TO_PREVIOUS;
+            case FLAG_KEY_MEDIA_REWIND:
+                return PlaybackState.ACTION_REWIND;
+            case FLAG_KEY_MEDIA_PLAY:
+                return PlaybackState.ACTION_PLAY;
+            case FLAG_KEY_MEDIA_PLAY_PAUSE:
+                return PlaybackState.ACTION_PLAY_PAUSE;
+            case FLAG_KEY_MEDIA_PAUSE:
+                return PlaybackState.ACTION_PAUSE;
+            case FLAG_KEY_MEDIA_STOP:
+                return PlaybackState.ACTION_STOP;
+            case FLAG_KEY_MEDIA_FAST_FORWARD:
+                return PlaybackState.ACTION_FAST_FORWARD;
+            case FLAG_KEY_MEDIA_NEXT:
+                return PlaybackState.ACTION_SKIP_TO_NEXT;
+            case FLAG_KEY_MEDIA_POSITION_UPDATE:
+                return PlaybackState.ACTION_SEEK_TO;
+            case FLAG_KEY_MEDIA_RATING:
+                return PlaybackState.ACTION_SET_RATING;
+        }
+        return 0;
+    }
+
+    private static int getRccFlagForAction(long action) {
+        // We only care about the lower set of actions that can map to rcc
+        // flags.
+        int testAction = action < Integer.MAX_VALUE ? (int) action : 0;
+        switch (testAction) {
+            case (int) PlaybackState.ACTION_SKIP_TO_PREVIOUS:
+                return FLAG_KEY_MEDIA_PREVIOUS;
+            case (int) PlaybackState.ACTION_REWIND:
+                return FLAG_KEY_MEDIA_REWIND;
+            case (int) PlaybackState.ACTION_PLAY:
+                return FLAG_KEY_MEDIA_PLAY;
+            case (int) PlaybackState.ACTION_PLAY_PAUSE:
+                return FLAG_KEY_MEDIA_PLAY_PAUSE;
+            case (int) PlaybackState.ACTION_PAUSE:
+                return FLAG_KEY_MEDIA_PAUSE;
+            case (int) PlaybackState.ACTION_STOP:
+                return FLAG_KEY_MEDIA_STOP;
+            case (int) PlaybackState.ACTION_FAST_FORWARD:
+                return FLAG_KEY_MEDIA_FAST_FORWARD;
+            case (int) PlaybackState.ACTION_SKIP_TO_NEXT:
+                return FLAG_KEY_MEDIA_NEXT;
+            case (int) PlaybackState.ACTION_SEEK_TO:
+                return FLAG_KEY_MEDIA_POSITION_UPDATE;
+            case (int) PlaybackState.ACTION_SET_RATING:
+                return FLAG_KEY_MEDIA_RATING;
+        }
+        return 0;
+    }
 }
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 5e9eed7..f70963a 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -632,8 +632,8 @@
             l = this.mOnClientUpdateListener;
         }
         if (l != null) {
-            int playstate = state == null ? RemoteControlClient.PLAYSTATE_NONE : PlaybackState
-                    .getRccStateFromState(state.getState());
+            int playstate = state == null ? RemoteControlClient.PLAYSTATE_NONE
+                    : RemoteControlClient.getRccStateFromState(state.getState());
             if (state == null || state.getPosition() == PlaybackState.PLAYBACK_POSITION_UNKNOWN) {
                 l.onClientPlaybackStateUpdate(playstate);
             } else {
@@ -642,7 +642,7 @@
             }
             if (state != null) {
                 l.onClientTransportControlUpdate(
-                        PlaybackState.getRccControlFlagsFromActions(state.getActions()));
+                        RemoteControlClient.getRccControlFlagsFromActions(state.getActions()));
             }
         }
     }
diff --git a/media/java/android/media/Session2Token.java b/media/java/android/media/Session2Token.java
index 023ee46..238cc2b 100644
--- a/media/java/android/media/Session2Token.java
+++ b/media/java/android/media/Session2Token.java
@@ -35,7 +35,7 @@
 import java.util.Objects;
 
 /**
- * Represents an ongoing {@link MediaSession2} or a MediaSession2Service.
+ * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}.
  * If it's representing a session service, it may not be ongoing.
  * <p>
  * This API is not generally intended for third party application developers.
@@ -199,14 +199,6 @@
     }
 
     /**
-     * @hide
-     * @return component name of the session. Can be {@code null} for {@link #TYPE_SESSION}.
-     */
-    public ComponentName getComponentName() {
-        return mComponentName;
-    }
-
-    /**
      * @return type of the token
      * @see #TYPE_SESSION
      * @see #TYPE_SESSION_SERVICE
@@ -215,10 +207,7 @@
         return mType;
     }
 
-    /**
-     * @hide
-     */
-    public Session2Link getSessionLink() {
+    Session2Link getSessionLink() {
         return mSessionLink;
     }
 
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 52e9ae1..5b4bbce 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -26,7 +26,6 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Parcel;
-import android.os.Parcelable;
 import android.util.Log;
 
 import java.lang.ref.WeakReference;
@@ -229,7 +228,7 @@
      * The method {@link #queryEffects()} returns an array of Descriptors to facilitate effects
      * enumeration.
      */
-    public static final class Descriptor implements Parcelable {
+    public static class Descriptor {
 
         public Descriptor() {
         }
@@ -294,7 +293,9 @@
             this.implementor = implementor;
         }
 
-        private Descriptor(Parcel in) {
+        /** @hide */
+        @TestApi
+        public Descriptor(Parcel in) {
             type = UUID.fromString(in.readString());
             uuid = UUID.fromString(in.readString());
             connectMode = in.readString();
@@ -302,33 +303,14 @@
             implementor = in.readString();
         }
 
-        public static final Parcelable.Creator<Descriptor> CREATOR =
-                new Parcelable.Creator<Descriptor>() {
-                    /**
-                     * Rebuilds a Descriptor previously stored with writeToParcel().
-                     * @param p Parcel object to read the Descriptor from
-                     * @return a new Descriptor created from the data in the parcel
-                     */
-                    public Descriptor createFromParcel(Parcel p) {
-                        return new Descriptor(p);
-                    }
-                    public Descriptor[] newArray(int size) {
-                        return new Descriptor[size];
-                    }
-        };
-
         @Override
         public int hashCode() {
             return Objects.hash(type, uuid, connectMode, name, implementor);
         }
 
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
+        /** @hide */
+        @TestApi
+        public void writeToParcel(Parcel dest) {
             dest.writeString(type.toString());
             dest.writeString(uuid.toString());
             dest.writeString(connectMode);
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index eda913e..7166616 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -144,7 +144,7 @@
             SessionLink sessionLink = manager.createSession(cbLink, tag);
             mImpl = new MediaSessionEngine(context, sessionLink, cbLink, cbStub,
                     context.getResources().getDimensionPixelSize(
-                            com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize));
+                            android.R.dimen.config_mediaMetadataBitmapMaxSize));
         } catch (RuntimeException e) {
             throw new RuntimeException("Remote error creating session.", e);
         }
@@ -723,7 +723,7 @@
 
         private QueueItem(Parcel in) {
             mImpl = new MediaSessionEngine.QueueItem(in);
-            mId = in.readLong();
+            mId = mImpl.getQueueId();
         }
 
         /**
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 2c57d1f..0d0ec4c 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.LongDef;
 import android.annotation.Nullable;
-import android.media.RemoteControlClient;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -481,161 +480,6 @@
         return mExtras;
     }
 
-    /**
-     * Get the {@link PlaybackState} state for the given
-     * {@link RemoteControlClient} state.
-     *
-     * @param rccState The state used by {@link RemoteControlClient}.
-     * @return The equivalent state used by {@link PlaybackState}.
-     * @hide
-     */
-    public static int getStateFromRccState(int rccState) {
-        switch (rccState) {
-            case RemoteControlClient.PLAYSTATE_BUFFERING:
-                return STATE_BUFFERING;
-            case RemoteControlClient.PLAYSTATE_ERROR:
-                return STATE_ERROR;
-            case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
-                return STATE_FAST_FORWARDING;
-            case RemoteControlClient.PLAYSTATE_NONE:
-                return STATE_NONE;
-            case RemoteControlClient.PLAYSTATE_PAUSED:
-                return STATE_PAUSED;
-            case RemoteControlClient.PLAYSTATE_PLAYING:
-                return STATE_PLAYING;
-            case RemoteControlClient.PLAYSTATE_REWINDING:
-                return STATE_REWINDING;
-            case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
-                return STATE_SKIPPING_TO_PREVIOUS;
-            case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
-                return STATE_SKIPPING_TO_NEXT;
-            case RemoteControlClient.PLAYSTATE_STOPPED:
-                return STATE_STOPPED;
-            default:
-                return -1;
-        }
-    }
-
-    /**
-     * Get the {@link RemoteControlClient} state for the given
-     * {@link PlaybackState} state.
-     *
-     * @param state The state used by {@link PlaybackState}.
-     * @return The equivalent state used by {@link RemoteControlClient}.
-     * @hide
-     */
-    public static int getRccStateFromState(int state) {
-        switch (state) {
-            case STATE_BUFFERING:
-                return RemoteControlClient.PLAYSTATE_BUFFERING;
-            case STATE_ERROR:
-                return RemoteControlClient.PLAYSTATE_ERROR;
-            case STATE_FAST_FORWARDING:
-                return RemoteControlClient.PLAYSTATE_FAST_FORWARDING;
-            case STATE_NONE:
-                return RemoteControlClient.PLAYSTATE_NONE;
-            case STATE_PAUSED:
-                return RemoteControlClient.PLAYSTATE_PAUSED;
-            case STATE_PLAYING:
-                return RemoteControlClient.PLAYSTATE_PLAYING;
-            case STATE_REWINDING:
-                return RemoteControlClient.PLAYSTATE_REWINDING;
-            case STATE_SKIPPING_TO_PREVIOUS:
-                return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS;
-            case STATE_SKIPPING_TO_NEXT:
-                return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS;
-            case STATE_STOPPED:
-                return RemoteControlClient.PLAYSTATE_STOPPED;
-            default:
-                return -1;
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public static long getActionsFromRccControlFlags(int rccFlags) {
-        long actions = 0;
-        long flag = 1;
-        while (flag <= rccFlags) {
-            if ((flag & rccFlags) != 0) {
-                actions |= getActionForRccFlag((int) flag);
-            }
-            flag = flag << 1;
-        }
-        return actions;
-    }
-
-    /**
-     * @hide
-     */
-    public static int getRccControlFlagsFromActions(long actions) {
-        int rccFlags = 0;
-        long action = 1;
-        while (action <= actions && action < Integer.MAX_VALUE) {
-            if ((action & actions) != 0) {
-                rccFlags |= getRccFlagForAction(action);
-            }
-            action = action << 1;
-        }
-        return rccFlags;
-    }
-
-    private static long getActionForRccFlag(int flag) {
-        switch (flag) {
-            case RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS:
-                return ACTION_SKIP_TO_PREVIOUS;
-            case RemoteControlClient.FLAG_KEY_MEDIA_REWIND:
-                return ACTION_REWIND;
-            case RemoteControlClient.FLAG_KEY_MEDIA_PLAY:
-                return ACTION_PLAY;
-            case RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE:
-                return ACTION_PLAY_PAUSE;
-            case RemoteControlClient.FLAG_KEY_MEDIA_PAUSE:
-                return ACTION_PAUSE;
-            case RemoteControlClient.FLAG_KEY_MEDIA_STOP:
-                return ACTION_STOP;
-            case RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD:
-                return ACTION_FAST_FORWARD;
-            case RemoteControlClient.FLAG_KEY_MEDIA_NEXT:
-                return ACTION_SKIP_TO_NEXT;
-            case RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE:
-                return ACTION_SEEK_TO;
-            case RemoteControlClient.FLAG_KEY_MEDIA_RATING:
-                return ACTION_SET_RATING;
-        }
-        return 0;
-    }
-
-    private static int getRccFlagForAction(long action) {
-        // We only care about the lower set of actions that can map to rcc
-        // flags.
-        int testAction = action < Integer.MAX_VALUE ? (int) action : 0;
-        switch (testAction) {
-            case (int) ACTION_SKIP_TO_PREVIOUS:
-                return RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS;
-            case (int) ACTION_REWIND:
-                return RemoteControlClient.FLAG_KEY_MEDIA_REWIND;
-            case (int) ACTION_PLAY:
-                return RemoteControlClient.FLAG_KEY_MEDIA_PLAY;
-            case (int) ACTION_PLAY_PAUSE:
-                return RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE;
-            case (int) ACTION_PAUSE:
-                return RemoteControlClient.FLAG_KEY_MEDIA_PAUSE;
-            case (int) ACTION_STOP:
-                return RemoteControlClient.FLAG_KEY_MEDIA_STOP;
-            case (int) ACTION_FAST_FORWARD:
-                return RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD;
-            case (int) ACTION_SKIP_TO_NEXT:
-                return RemoteControlClient.FLAG_KEY_MEDIA_NEXT;
-            case (int) ACTION_SEEK_TO:
-                return RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE;
-            case (int) ACTION_SET_RATING:
-                return RemoteControlClient.FLAG_KEY_MEDIA_RATING;
-        }
-        return 0;
-    }
-
     public static final Parcelable.Creator<PlaybackState> CREATOR =
             new Parcelable.Creator<PlaybackState>() {
         @Override
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b903142..c3c3f25 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -166,6 +166,8 @@
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
     <uses-permission android:name="android.permission.SUSPEND_APPS" />
     <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
+    <!-- Permission needed to wipe the device for Test Harness Mode -->
+    <uses-permission android:name="android.permission.ENABLE_TEST_HARNESS_MODE" />
 
     <uses-permission android:name="android.permission.MANAGE_APPOPS" />
 
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 35abfd4..49db488 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -676,6 +676,10 @@
                         // 3tap and hold
                         afterLongTapTimeoutTransitionToDraggingState(event);
 
+                    } else if (isTapOutOfDistanceSlop()) {
+
+                        transitionToDelegatingStateAndClear();
+
                     } else if (mDetectTripleTap
                             // If magnified, delay an ACTION_DOWN for mMultiTapMaxDelay
                             // to ensure reachability of
@@ -921,6 +925,31 @@
             // TODO: multi-display support for magnification gesture handler
             mMagnificationController.setForceShowMagnifiableBounds(Display.DEFAULT_DISPLAY, state);
         }
+
+        /**
+         * Detects if last action down is out of distance slop between with previous
+         * one, when triple tap is enabled.
+         *
+         * @return true if tap is out of distance slop
+         */
+        boolean isTapOutOfDistanceSlop() {
+            if (!mDetectTripleTap) return false;
+            if (mPreLastDown == null || mLastDown == null) {
+                return false;
+            }
+            final boolean outOfDistanceSlop =
+                    GestureUtils.distance(mPreLastDown, mLastDown) > mMultiTapMaxDistance;
+            if (tapCount() > 0) {
+                return outOfDistanceSlop;
+            }
+            // There's no tap in the queue here. We still need to check if this is the case that
+            // user tap screen quickly and out of distance slop.
+            if (outOfDistanceSlop
+                    && !GestureUtils.isTimedOut(mPreLastDown, mLastDown, mMultiTapMaxDelay)) {
+                return true;
+            }
+            return false;
+        }
     }
 
     private void zoomOn(float centerX, float centerY) {
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 121a830..39030aa 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -341,6 +341,29 @@
     @VisibleForTesting
     static final int STATE_QUICK_DOZE_DELAY = 7;
 
+    private static final int ACTIVE_REASON_UNKNOWN = 0;
+    private static final int ACTIVE_REASON_MOTION = 1;
+    private static final int ACTIVE_REASON_SCREEN = 2;
+    private static final int ACTIVE_REASON_CHARGING = 3;
+    private static final int ACTIVE_REASON_UNLOCKED = 4;
+    private static final int ACTIVE_REASON_FROM_BINDER_CALL = 5;
+    private static final int ACTIVE_REASON_FORCED = 6;
+    private static final int ACTIVE_REASON_ALARM = 7;
+    @VisibleForTesting
+    static final int SET_IDLE_FACTOR_RESULT_UNINIT = -1;
+    @VisibleForTesting
+    static final int SET_IDLE_FACTOR_RESULT_IGNORED = 0;
+    @VisibleForTesting
+    static final int SET_IDLE_FACTOR_RESULT_OK = 1;
+    @VisibleForTesting
+    static final int SET_IDLE_FACTOR_RESULT_NOT_SUPPORT = 2;
+    @VisibleForTesting
+    static final int SET_IDLE_FACTOR_RESULT_INVALID = 3;
+    @VisibleForTesting
+    static final long MIN_STATE_STEP_ALARM_CHANGE = 60 * 1000;
+    @VisibleForTesting
+    static final float MIN_PRE_IDLE_FACTOR_CHANGE = 0.05f;
+
     @VisibleForTesting
     static String stateToString(int state) {
         switch (state) {
@@ -405,6 +428,7 @@
     private long mNextSensingTimeoutAlarmTime;
     private long mCurIdleBudget;
     private long mMaintenanceStartTime;
+    private long mIdleStartTime;
 
     private int mActiveIdleOpCount;
     private PowerManager.WakeLock mActiveIdleWakeLock; // held when there are operations in progress
@@ -415,6 +439,17 @@
     private boolean mAlarmsActive;
     private boolean mReportedMaintenanceActivity;
 
+    /* Factor to apply to INACTIVE_TIMEOUT and IDLE_AFTER_INACTIVE_TIMEOUT in order to enter
+     * STATE_IDLE faster or slower. Don't apply this to SENSING_TIMEOUT or LOCATING_TIMEOUT because:
+     *   - Both of them are shorter
+     *   - Device sensor might take time be to become be stabilized
+     * Also don't apply the factor if the device is in motion because device motion provides a
+     * stronger signal than a prediction algorithm.
+     */
+    private float mPreIdleFactor;
+    private float mLastPreIdleFactor;
+    private int mActiveReason;
+
     public final AtomicFile mConfigFile;
 
     private final RemoteCallbackList<IMaintenanceActivityListener> mMaintenanceActivityListeners =
@@ -760,6 +795,10 @@
          * exit doze. Default = true
          */
         private static final String KEY_WAIT_FOR_UNLOCK = "wait_for_unlock";
+        private static final String KEY_PRE_IDLE_FACTOR_LONG =
+                "pre_idle_factor_long";
+        private static final String KEY_PRE_IDLE_FACTOR_SHORT =
+                "pre_idle_factor_short";
 
         /**
          * This is the time, after becoming inactive, that we go in to the first
@@ -987,6 +1026,16 @@
          */
         public long NOTIFICATION_WHITELIST_DURATION;
 
+        /**
+         * Pre idle time factor use to make idle delay longer
+         */
+        public float PRE_IDLE_FACTOR_LONG;
+
+        /**
+         * Pre idle time factor use to make idle delay shorter
+         */
+        public float PRE_IDLE_FACTOR_SHORT;
+
         public boolean WAIT_FOR_UNLOCK;
 
         private final ContentResolver mResolver;
@@ -1082,6 +1131,8 @@
                 NOTIFICATION_WHITELIST_DURATION = mParser.getDurationMillis(
                         KEY_NOTIFICATION_WHITELIST_DURATION, 30 * 1000L);
                 WAIT_FOR_UNLOCK = mParser.getBoolean(KEY_WAIT_FOR_UNLOCK, false);
+                PRE_IDLE_FACTOR_LONG = mParser.getFloat(KEY_PRE_IDLE_FACTOR_LONG, 1.67f);
+                PRE_IDLE_FACTOR_SHORT = mParser.getFloat(KEY_PRE_IDLE_FACTOR_SHORT, 0.33f);
             }
         }
 
@@ -1196,6 +1247,12 @@
 
             pw.print("    "); pw.print(KEY_WAIT_FOR_UNLOCK); pw.print("=");
             pw.println(WAIT_FOR_UNLOCK);
+
+            pw.print("    "); pw.print(KEY_PRE_IDLE_FACTOR_LONG); pw.print("=");
+            pw.println(PRE_IDLE_FACTOR_LONG);
+
+            pw.print("    "); pw.print(KEY_PRE_IDLE_FACTOR_SHORT); pw.print("=");
+            pw.println(PRE_IDLE_FACTOR_SHORT);
         }
     }
 
@@ -1244,6 +1301,8 @@
     private static final int MSG_FINISH_IDLE_OP = 8;
     private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED = 9;
     private static final int MSG_SEND_CONSTRAINT_MONITORING = 10;
+    private static final int MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR = 11;
+    private static final int MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR = 12;
 
     final class MyHandler extends Handler {
         MyHandler(Looper looper) {
@@ -1373,6 +1432,13 @@
                         constraint.stopMonitoring();
                     }
                 } break;
+                case MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR: {
+                    updatePreIdleFactor();
+                } break;
+                case MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR: {
+                    updatePreIdleFactor();
+                    maybeDoImmediateMaintenance();
+                } break;
             }
         }
     }
@@ -1526,6 +1592,28 @@
             DeviceIdleController.this.unregisterMaintenanceActivityListener(listener);
         }
 
+        @Override public int setPreIdleTimeoutMode(int mode) {
+            getContext().enforceCallingOrSelfPermission(Manifest.permission.DEVICE_POWER,
+                    null);
+            long ident = Binder.clearCallingIdentity();
+            try {
+                return DeviceIdleController.this.setPreIdleTimeoutMode(mode);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override public void resetPreIdleTimeoutMode() {
+            getContext().enforceCallingOrSelfPermission(Manifest.permission.DEVICE_POWER,
+                    null);
+            long ident = Binder.clearCallingIdentity();
+            try {
+                DeviceIdleController.this.resetPreIdleTimeoutMode();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
         @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             DeviceIdleController.this.dump(fd, pw, args);
         }
@@ -1768,9 +1856,12 @@
             // Start out assuming we are charging.  If we aren't, we will at least get
             // a battery update the next time the level drops.
             mCharging = true;
+            mActiveReason = ACTIVE_REASON_UNKNOWN;
             mState = STATE_ACTIVE;
             mLightState = LIGHT_STATE_ACTIVE;
             mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
+            mPreIdleFactor = 1.0f;
+            mLastPreIdleFactor = 1.0f;
         }
 
         mBinderService = new BinderService();
@@ -2394,6 +2485,7 @@
 
     public void exitIdleInternal(String reason) {
         synchronized (this) {
+            mActiveReason = ACTIVE_REASON_FROM_BINDER_CALL;
             becomeActiveLocked(reason, Binder.getCallingUid());
         }
     }
@@ -2463,6 +2555,7 @@
         } else if (screenOn) {
             mScreenOn = true;
             if (!mForceIdle && (!mScreenLocked || !mConstants.WAIT_FOR_UNLOCK)) {
+                mActiveReason = ACTIVE_REASON_SCREEN;
                 becomeActiveLocked("screen", Process.myUid());
             }
         }
@@ -2485,6 +2578,7 @@
         } else if (charging) {
             mCharging = charging;
             if (!mForceIdle) {
+                mActiveReason = ACTIVE_REASON_CHARGING;
                 becomeActiveLocked("charging", Process.myUid());
             }
         }
@@ -2516,6 +2610,7 @@
         if (mScreenLocked != showing) {
             mScreenLocked = showing;
             if (mScreenOn && !mForceIdle && !mScreenLocked) {
+                mActiveReason = ACTIVE_REASON_UNLOCKED;
                 becomeActiveLocked("unlocked", Process.myUid());
             }
         }
@@ -2587,7 +2682,11 @@
                     mState = STATE_INACTIVE;
                     if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");
                     resetIdleManagementLocked();
-                    scheduleAlarmLocked(mInactiveTimeout, false);
+                    long delay = mInactiveTimeout;
+                    if (shouldUseIdleTimeoutFactorLocked()) {
+                        delay = (long) (mPreIdleFactor * delay);
+                    }
+                    scheduleAlarmLocked(delay, false);
                     EventLogTags.writeDeviceIdle(mState, "no activity");
                 }
             }
@@ -2605,6 +2704,7 @@
         mNextIdlePendingDelay = 0;
         mNextIdleDelay = 0;
         mNextLightIdleDelay = 0;
+        mIdleStartTime = 0;
         cancelAlarmLocked();
         cancelSensingTimeoutAlarmLocked();
         cancelLocatingLocked();
@@ -2621,6 +2721,7 @@
         if (mForceIdle) {
             mForceIdle = false;
             if (mScreenOn || mCharging) {
+                mActiveReason = ACTIVE_REASON_FORCED;
                 becomeActiveLocked("exit-force", Process.myUid());
             }
         }
@@ -2740,6 +2841,7 @@
         if ((now+mConstants.MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) {
             // Whoops, there is an upcoming alarm.  We don't actually want to go idle.
             if (mState != STATE_ACTIVE) {
+                mActiveReason = ACTIVE_REASON_ALARM;
                 becomeActiveLocked("alarm", Process.myUid());
                 becomeInactiveIfAppropriateLocked();
             }
@@ -2763,7 +2865,11 @@
                 // We have now been inactive long enough, it is time to start looking
                 // for motion and sleep some more while doing so.
                 startMonitoringMotionLocked();
-                scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);
+                long delay = mConstants.IDLE_AFTER_INACTIVE_TIMEOUT;
+                if (shouldUseIdleTimeoutFactorLocked()) {
+                    delay = (long) (mPreIdleFactor * delay);
+                }
+                scheduleAlarmLocked(delay, false);
                 moveToStateLocked(STATE_IDLE_PENDING, reason);
                 break;
             case STATE_IDLE_PENDING:
@@ -2834,6 +2940,7 @@
                         " ms.");
                 mNextIdleDelay = (long)(mNextIdleDelay * mConstants.IDLE_FACTOR);
                 if (DEBUG) Slog.d(TAG, "Setting mNextIdleDelay = " + mNextIdleDelay);
+                mIdleStartTime = SystemClock.elapsedRealtime();
                 mNextIdleDelay = Math.min(mNextIdleDelay, mConstants.MAX_IDLE_TIMEOUT);
                 if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) {
                     mNextIdleDelay = mConstants.IDLE_TIMEOUT;
@@ -2934,6 +3041,127 @@
         }
     }
 
+    @VisibleForTesting
+    int setPreIdleTimeoutMode(int mode) {
+        return setPreIdleTimeoutFactor(getPreIdleTimeoutByMode(mode));
+    }
+
+    @VisibleForTesting
+    float getPreIdleTimeoutByMode(int mode) {
+        switch (mode) {
+            case PowerManager.PRE_IDLE_TIMEOUT_MODE_LONG: {
+                return mConstants.PRE_IDLE_FACTOR_LONG;
+            }
+            case PowerManager.PRE_IDLE_TIMEOUT_MODE_SHORT: {
+                return mConstants.PRE_IDLE_FACTOR_SHORT;
+            }
+            case PowerManager.PRE_IDLE_TIMEOUT_MODE_NORMAL: {
+                return 1.0f;
+            }
+            default: {
+                Slog.w(TAG, "Invalid time out factor mode: " + mode);
+                return 1.0f;
+            }
+        }
+    }
+
+    @VisibleForTesting
+    float getPreIdleTimeoutFactor() {
+        return mPreIdleFactor;
+    }
+
+    @VisibleForTesting
+    int setPreIdleTimeoutFactor(float ratio) {
+        if (!mDeepEnabled) {
+            if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: Deep Idle disable");
+            return SET_IDLE_FACTOR_RESULT_NOT_SUPPORT;
+        } else if (ratio <= MIN_PRE_IDLE_FACTOR_CHANGE) {
+            if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: Invalid input");
+            return SET_IDLE_FACTOR_RESULT_INVALID;
+        } else if (Math.abs(ratio - mPreIdleFactor) < MIN_PRE_IDLE_FACTOR_CHANGE) {
+            if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: New factor same as previous factor");
+            return SET_IDLE_FACTOR_RESULT_IGNORED;
+        }
+        synchronized (this) {
+            mLastPreIdleFactor = mPreIdleFactor;
+            mPreIdleFactor = ratio;
+        }
+        if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: " + ratio);
+        postUpdatePreIdleFactor();
+        return SET_IDLE_FACTOR_RESULT_OK;
+    }
+
+    @VisibleForTesting
+    void resetPreIdleTimeoutMode() {
+        synchronized (this) {
+            mLastPreIdleFactor = mPreIdleFactor;
+            mPreIdleFactor = 1.0f;
+        }
+        if (DEBUG) Slog.d(TAG, "resetPreIdleTimeoutMode to 1.0");
+        postResetPreIdleTimeoutFactor();
+    }
+
+    private void postUpdatePreIdleFactor() {
+        mHandler.sendEmptyMessage(MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR);
+    }
+
+    private void postResetPreIdleTimeoutFactor() {
+        mHandler.sendEmptyMessage(MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR);
+    }
+
+    @VisibleForTesting
+    void updatePreIdleFactor() {
+        synchronized (this) {
+            if (!shouldUseIdleTimeoutFactorLocked()) {
+                return;
+            }
+            if (mState == STATE_INACTIVE || mState == STATE_IDLE_PENDING) {
+                if (mNextAlarmTime == 0) {
+                    return;
+                }
+                long delay = mNextAlarmTime - SystemClock.elapsedRealtime();
+                if (delay < MIN_STATE_STEP_ALARM_CHANGE) {
+                    return;
+                }
+                long newDelay = (long) (delay / mLastPreIdleFactor * mPreIdleFactor);
+                if (Math.abs(delay - newDelay) < MIN_STATE_STEP_ALARM_CHANGE) {
+                    return;
+                }
+                scheduleAlarmLocked(newDelay, false);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    void maybeDoImmediateMaintenance() {
+        synchronized (this) {
+            if (mState == STATE_IDLE) {
+                long duration = SystemClock.elapsedRealtime() - mIdleStartTime;
+                /* Let's trgger a immediate maintenance,
+                 * if it has been idle for a long time */
+                if (duration > mConstants.IDLE_TIMEOUT) {
+                    scheduleAlarmLocked(0, false);
+                }
+            }
+        }
+    }
+
+    private boolean shouldUseIdleTimeoutFactorLocked() {
+        // exclude ACTIVE_REASON_MOTION, for exclude device in pocket case
+        if (mActiveReason == ACTIVE_REASON_MOTION) {
+            return false;
+        }
+        return true;
+    }
+
+    /** Must only be used in tests. */
+    @VisibleForTesting
+    void setIdleStartTimeForTest(long idleStartTime) {
+        synchronized (this) {
+            mIdleStartTime = idleStartTime;
+        }
+    }
+
     void reportMaintenanceActivityIfNeededLocked() {
         boolean active = mJobsActive;
         if (active == mReportedMaintenanceActivity) {
@@ -2945,6 +3173,11 @@
         mHandler.sendMessage(msg);
     }
 
+    @VisibleForTesting
+    long getNextAlarmTime() {
+        return mNextAlarmTime;
+    }
+
     boolean isOpsInactiveLocked() {
         return mActiveIdleOpCount <= 0 && !mJobsActive && !mAlarmsActive;
     }
@@ -2994,6 +3227,7 @@
                 scheduleReportActiveLocked(type, Process.myUid());
                 addEvent(EVENT_NORMAL, type);
             }
+            mActiveReason = ACTIVE_REASON_MOTION;
             mState = STATE_ACTIVE;
             mInactiveTimeout = timeout;
             mCurIdleBudget = 0;
@@ -3401,6 +3635,11 @@
                 + "and any [-d] is ignored");
         pw.println("  motion");
         pw.println("    Simulate a motion event to bring the device out of deep doze");
+        pw.println("  pre-idle-factor [0|1|2]");
+        pw.println("    Set a new factor to idle time before step to idle"
+                + "(inactive_to and idle_after_inactive_to)");
+        pw.println("  reset-pre-idle-factor");
+        pw.println("    Reset factor to idle time to default");
     }
 
     class Shell extends ShellCommand {
@@ -3571,6 +3810,7 @@
                         }
                     }
                     if (becomeActive) {
+                        mActiveReason = ACTIVE_REASON_FORCED;
                         becomeActiveLocked((arg == null ? "all" : arg) + "-disabled",
                                 Process.myUid());
                     }
@@ -3820,6 +4060,52 @@
                     Binder.restoreCallingIdentity(token);
                 }
             }
+        } else if ("pre-idle-factor".equals(cmd)) {
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+                    null);
+            synchronized (this) {
+                long token = Binder.clearCallingIdentity();
+                int ret  = SET_IDLE_FACTOR_RESULT_UNINIT;
+                try {
+                    String arg = shell.getNextArg();
+                    boolean valid = false;
+                    int mode = 0;
+                    if (arg != null) {
+                        mode = Integer.parseInt(arg);
+                        ret = setPreIdleTimeoutMode(mode);
+                        if (ret == SET_IDLE_FACTOR_RESULT_OK) {
+                            pw.println("pre-idle-factor: " + mode);
+                            valid = true;
+                        } else if (ret == SET_IDLE_FACTOR_RESULT_NOT_SUPPORT) {
+                            valid = true;
+                            pw.println("Deep idle not supported");
+                        } else if (ret == SET_IDLE_FACTOR_RESULT_IGNORED) {
+                            valid = true;
+                            pw.println("Idle timeout factor not changed");
+                        }
+                    }
+                    if (!valid) {
+                        pw.println("Unknown idle timeout factor: " + arg
+                                + ",(error code: " + ret + ")");
+                    }
+                } catch (NumberFormatException e) {
+                    pw.println("Unknown idle timeout factor"
+                            + ",(error code: " + ret + ")");
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        } else if ("reset-pre-idle-factor".equals(cmd)) {
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+                    null);
+            synchronized (this) {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    resetPreIdleTimeoutMode();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
         } else {
             return shell.handleDefaultCommands(cmd);
         }
@@ -4053,6 +4339,9 @@
             if (mAlarmsActive) {
                 pw.print("  mAlarmsActive="); pw.println(mAlarmsActive);
             }
+            if (Math.abs(mPreIdleFactor - 1.0f) > MIN_PRE_IDLE_FACTOR_CHANGE) {
+                pw.print("  mPreIdleFactor="); pw.println(mPreIdleFactor);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index c84389b..2ce2f8d 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -46,7 +46,9 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.net.ConnectivityManager;
+import android.net.InetAddresses;
 import android.net.INetd;
+import android.net.INetdUnsolicitedEventListener;
 import android.net.INetworkManagementEventObserver;
 import android.net.ITetheringStatsProvider;
 import android.net.InterfaceConfiguration;
@@ -205,6 +207,8 @@
 
     private INetd mNetdService;
 
+    private final NetdUnsolicitedEventListener mNetdUnsolicitedEventListener;
+
     private IBatteryStats mBatteryStats;
 
     private final Thread mThread;
@@ -322,6 +326,8 @@
 
         mDaemonHandler = new Handler(FgThread.get().getLooper());
 
+        mNetdUnsolicitedEventListener = new NetdUnsolicitedEventListener();
+
         // Add ourself to the Watchdog monitors.
         Watchdog.getInstance().addMonitor(this);
 
@@ -340,6 +346,7 @@
         mFgHandler = null;
         mThread = null;
         mServices = null;
+        mNetdUnsolicitedEventListener = null;
     }
 
     static NetworkManagementService create(Context context, String socket, SystemServices services)
@@ -446,7 +453,6 @@
         // our sanity-checking state.
         mActiveAlerts.remove(iface);
         mActiveQuotas.remove(iface);
-
         invokeForAllObservers(o -> o.interfaceRemoved(iface));
     }
 
@@ -552,7 +558,7 @@
                 return;
             }
             // No current code examines the interface parameter in a global alert. Just pass null.
-            notifyLimitReached(LIMIT_GLOBAL_ALERT, null);
+            mDaemonHandler.post(() -> notifyLimitReached(LIMIT_GLOBAL_ALERT, null));
         }
     }
 
@@ -583,6 +589,12 @@
 
     private void connectNativeNetdService() {
         mNetdService = mServices.getNetd();
+        try {
+            mNetdService.registerUnsolicitedEventListener(mNetdUnsolicitedEventListener);
+            if (DBG) Slog.d(TAG, "Register unsolicited event listener");
+        } catch (RemoteException | ServiceSpecificException e) {
+            Slog.e(TAG, "Failed to set Netd unsolicited event listener " + e);
+        }
     }
 
     /**
@@ -709,14 +721,96 @@
     /**
      * Notify our observers of a route change.
      */
-    private void notifyRouteChange(String action, RouteInfo route) {
-        if (action.equals("updated")) {
+    private void notifyRouteChange(boolean updated, RouteInfo route) {
+        if (updated) {
             invokeForAllObservers(o -> o.routeUpdated(route));
         } else {
             invokeForAllObservers(o -> o.routeRemoved(route));
         }
     }
 
+    private class NetdUnsolicitedEventListener extends INetdUnsolicitedEventListener.Stub {
+        @Override
+        public void onInterfaceClassActivityChanged(boolean isActive,
+                int label, long timestamp, int uid) throws RemoteException {
+            final long timestampNanos;
+            if (timestamp <= 0) {
+                timestampNanos = SystemClock.elapsedRealtimeNanos();
+            } else {
+                timestampNanos = timestamp;
+            }
+            mDaemonHandler.post(() -> notifyInterfaceClassActivity(label,
+                    isActive ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
+                    : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
+                    timestampNanos, uid, false));
+        }
+
+        @Override
+        public void onQuotaLimitReached(String alertName, String ifName)
+                throws RemoteException {
+            mDaemonHandler.post(() -> notifyLimitReached(alertName, ifName));
+        }
+
+        @Override
+        public void onInterfaceDnsServerInfo(String ifName,
+                long lifetime, String[] servers) throws RemoteException {
+            mDaemonHandler.post(() -> notifyInterfaceDnsServerInfo(ifName, lifetime, servers));
+        }
+
+        @Override
+        public void onInterfaceAddressUpdated(String addr,
+                String ifName, int flags, int scope) throws RemoteException {
+            final LinkAddress address = new LinkAddress(addr, flags, scope);
+            mDaemonHandler.post(() -> notifyAddressUpdated(ifName, address));
+        }
+
+        @Override
+        public void onInterfaceAddressRemoved(String addr,
+                String ifName, int flags, int scope) throws RemoteException {
+            final LinkAddress address = new LinkAddress(addr, flags, scope);
+            mDaemonHandler.post(() -> notifyAddressRemoved(ifName, address));
+        }
+
+        @Override
+        public void onInterfaceAdded(String ifName) throws RemoteException {
+            mDaemonHandler.post(() -> notifyInterfaceAdded(ifName));
+        }
+
+        @Override
+        public void onInterfaceRemoved(String ifName) throws RemoteException {
+            mDaemonHandler.post(() -> notifyInterfaceRemoved(ifName));
+        }
+
+        @Override
+        public void onInterfaceChanged(String ifName, boolean up)
+                throws RemoteException {
+            mDaemonHandler.post(() -> notifyInterfaceStatusChanged(ifName, up));
+        }
+
+        @Override
+        public void onInterfaceLinkStateChanged(String ifName, boolean up)
+                throws RemoteException {
+            mDaemonHandler.post(() -> notifyInterfaceLinkStateChanged(ifName, up));
+        }
+
+        @Override
+        public void onRouteChanged(boolean updated,
+                String route, String gateway, String ifName) throws RemoteException {
+            final RouteInfo processRoute = new RouteInfo(new IpPrefix(route),
+                    ("".equals(gateway)) ? null : InetAddresses.parseNumericAddress(gateway),
+                    ifName);
+            mDaemonHandler.post(() -> notifyRouteChange(updated, processRoute));
+        }
+
+        @Override
+        public void onStrictCleartextDetected(int uid, String hex) throws RemoteException {
+            // Don't need to post to mDaemonHandler because the only thing
+            // that notifyCleartextNetwork does is post to a handler
+            ActivityManager.getService().notifyCleartextNetwork(uid,
+                    HexDump.hexStringToByteArray(hex));
+        }
+    }
+
     //
     // Netd Callback handling
     //
@@ -904,7 +998,7 @@
                             InetAddress gateway = null;
                             if (via != null) gateway = InetAddress.parseNumericAddress(via);
                             RouteInfo route = new RouteInfo(new IpPrefix(cooked[3]), gateway, dev);
-                            notifyRouteChange(cooked[2], route);
+                            notifyRouteChange(cooked[2].equals("updated"), route);
                             return true;
                         } catch (IllegalArgumentException e) {}
                     }
@@ -1367,13 +1461,9 @@
             if (ConnectivityManager.isNetworkTypeMobile(type)) {
                 mNetworkActive = false;
             }
-            mDaemonHandler.post(new Runnable() {
-                @Override public void run() {
-                    notifyInterfaceClassActivity(type,
-                            DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
-                            SystemClock.elapsedRealtimeNanos(), -1, false);
-                }
-            });
+            mDaemonHandler.post(() -> notifyInterfaceClassActivity(type,
+                    DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
+                    SystemClock.elapsedRealtimeNanos(), -1, false));
         }
     }
 
@@ -1396,13 +1486,9 @@
                 throw new IllegalStateException(e);
             }
             mActiveIdleTimers.remove(iface);
-            mDaemonHandler.post(new Runnable() {
-                @Override public void run() {
-                    notifyInterfaceClassActivity(params.type,
-                            DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
-                            SystemClock.elapsedRealtimeNanos(), -1, false);
-                }
-            });
+            mDaemonHandler.post(() -> notifyInterfaceClassActivity(params.type,
+                    DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
+                    SystemClock.elapsedRealtimeNanos(), -1, false));
         }
     }
 
diff --git a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
index 1e9a007..190fff1 100644
--- a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
+++ b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
@@ -31,6 +31,19 @@
      */
     byte[] getFrpCredentialHandle();
 
+    /** Stores the data used to enable the Test Harness Mode after factory-resetting. */
+    void setTestHarnessModeData(byte[] data);
+
+    /**
+     * Retrieves the data used to place the device into Test Harness Mode.
+     *
+     * @throws IllegalStateException if the underlying storage is corrupt or inaccessible.
+     */
+    byte[] getTestHarnessModeData();
+
+    /** Clear out the Test Harness Mode data. */
+    void clearTestHarnessModeData();
+
     /** Update the OEM unlock enabled bit, bypassing user restriction checks. */
     void forceOemUnlockEnabled(boolean enabled);
 }
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 21093b9..bd5ad96 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import static com.android.internal.util.Preconditions.checkArgument;
+
 import android.Manifest;
 import android.app.ActivityManager;
 import android.content.Context;
@@ -28,12 +30,10 @@
 import android.os.UserManager;
 import android.service.persistentdata.IPersistentDataBlockService;
 import android.service.persistentdata.PersistentDataBlockManager;
-import android.util.Log;
 import android.util.Slog;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
 
 import libcore.io.IoUtils;
 
@@ -65,6 +65,40 @@
  *
  * Clients can read any number of bytes from the currently written block up to its total size by
  * invoking {@link IPersistentDataBlockService#read}
+ *
+ * The persistent data block is currently laid out as follows:
+ * | ---------BEGINNING OF PARTITION-------------|
+ * | Partition digest (32 bytes)                 |
+ * | --------------------------------------------|
+ * | PARTITION_TYPE_MARKER (4 bytes)             |
+ * | --------------------------------------------|
+ * | FRP data block length (4 bytes)             |
+ * | --------------------------------------------|
+ * | FRP data (variable length)                  |
+ * | --------------------------------------------|
+ * | ...                                         |
+ * | --------------------------------------------|
+ * | Test mode data block (10000 bytes)          |
+ * | --------------------------------------------|
+ * |     | Test mode data length (4 bytes)       |
+ * | --------------------------------------------|
+ * |     | Test mode data (variable length)      |
+ * |     | ...                                   |
+ * | --------------------------------------------|
+ * | FRP credential handle block (1000 bytes)    |
+ * | --------------------------------------------|
+ * |     | FRP credential handle length (4 bytes)|
+ * | --------------------------------------------|
+ * |     | FRP credential handle (variable len)  |
+ * |     | ...                                   |
+ * | --------------------------------------------|
+ * | OEM Unlock bit (1 byte)                     |
+ * | ---------END OF PARTITION-------------------|
+ *
+ * TODO: now that the persistent partition contains several blocks, next time someone wants a new
+ * block, we should look at adding more generic block definitions and get rid of the various raw
+ * XXX_RESERVED_SIZE and XXX_DATA_SIZE constants. That will ensure the code is easier to maintain
+ * and less likely to introduce out-of-bounds read/write.
  */
 public class PersistentDataBlockService extends SystemService {
     private static final String TAG = PersistentDataBlockService.class.getSimpleName();
@@ -73,10 +107,16 @@
     private static final int HEADER_SIZE = 8;
     // Magic number to mark block device as adhering to the format consumed by this service
     private static final int PARTITION_TYPE_MARKER = 0x19901873;
-    /** Size of the block reserved for FPR credential, including 4 bytes for the size header. */
+    /** Size of the block reserved for FRP credential, including 4 bytes for the size header. */
     private static final int FRP_CREDENTIAL_RESERVED_SIZE = 1000;
     /** Maximum size of the FRP credential handle that can be stored. */
     private static final int MAX_FRP_CREDENTIAL_HANDLE_SIZE = FRP_CREDENTIAL_RESERVED_SIZE - 4;
+    /**
+     * Size of the block reserved for Test Harness Mode data, including 4 bytes for the size header.
+     */
+    private static final int TEST_MODE_RESERVED_SIZE = 10000;
+    /** Maximum size of the Test Harness Mode data that can be stored. */
+    private static final int MAX_TEST_MODE_DATA_SIZE = TEST_MODE_RESERVED_SIZE - 4;
     // Limit to 100k as blocks larger than this might cause strain on Binder.
     private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
 
@@ -221,6 +261,14 @@
         return mBlockDeviceSize;
     }
 
+    private long getFrpCredentialDataOffset() {
+        return getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE;
+    }
+
+    private long getTestHarnessModeDataOffset() {
+        return getFrpCredentialDataOffset() - TEST_MODE_RESERVED_SIZE;
+    }
+
     private boolean enforceChecksumValidity() {
         byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];
 
@@ -383,7 +431,7 @@
 
     private long doGetMaximumDataBlockSize() {
         long actualSize = getBlockDeviceSize() - HEADER_SIZE - DIGEST_SIZE_BYTES
-                - FRP_CREDENTIAL_RESERVED_SIZE - 1;
+                - TEST_MODE_RESERVED_SIZE - FRP_CREDENTIAL_RESERVED_SIZE - 1;
         return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
     }
 
@@ -391,6 +439,13 @@
     private native int nativeWipe(String path);
 
     private final IBinder mService = new IPersistentDataBlockService.Stub() {
+
+        /**
+         * Write the data to the persistent data block.
+         *
+         * @return a positive integer of the number of bytes that were written if successful,
+         * otherwise a negative integer indicating there was a problem
+         */
         @Override
         public int write(byte[] data) throws RemoteException {
             enforceUid(Binder.getCallingUid());
@@ -597,12 +652,51 @@
 
         @Override
         public void setFrpCredentialHandle(byte[] handle) {
-            Preconditions.checkArgument(handle == null || handle.length > 0,
-                    "handle must be null or non-empty");
-            Preconditions.checkArgument(handle == null
-                            || handle.length <= MAX_FRP_CREDENTIAL_HANDLE_SIZE,
-                    "handle must not be longer than " + MAX_FRP_CREDENTIAL_HANDLE_SIZE);
+            writeInternal(handle, getFrpCredentialDataOffset(), MAX_FRP_CREDENTIAL_HANDLE_SIZE);
+        }
 
+        @Override
+        public byte[] getFrpCredentialHandle() {
+            return readInternal(getFrpCredentialDataOffset(), MAX_FRP_CREDENTIAL_HANDLE_SIZE);
+        }
+
+        @Override
+        public void setTestHarnessModeData(byte[] data) {
+            writeInternal(data, getTestHarnessModeDataOffset(), MAX_TEST_MODE_DATA_SIZE);
+        }
+
+        @Override
+        public byte[] getTestHarnessModeData() {
+            byte[] data = readInternal(getTestHarnessModeDataOffset(), MAX_TEST_MODE_DATA_SIZE);
+            if (data == null) {
+                return new byte[0];
+            }
+            return data;
+        }
+
+        @Override
+        public void clearTestHarnessModeData() {
+            int size = Math.min(MAX_TEST_MODE_DATA_SIZE, getTestHarnessModeData().length) + 4;
+            writeDataBuffer(getTestHarnessModeDataOffset(), ByteBuffer.allocate(size));
+        }
+
+        private void writeInternal(byte[] data, long offset, int dataLength) {
+            checkArgument(data == null || data.length > 0, "data must be null or non-empty");
+            checkArgument(
+                    data == null || data.length <= dataLength,
+                    "data must not be longer than " + dataLength);
+
+            ByteBuffer dataBuffer = ByteBuffer.allocate(dataLength + 4);
+            dataBuffer.putInt(data == null ? 0 : data.length);
+            if (data != null) {
+                dataBuffer.put(data);
+            }
+            dataBuffer.flip();
+
+            writeDataBuffer(offset, dataBuffer);
+        }
+
+        private void writeDataBuffer(long offset, ByteBuffer dataBuffer) {
             FileOutputStream outputStream;
             try {
                 outputStream = new FileOutputStream(new File(mDataBlockFile));
@@ -610,25 +704,15 @@
                 Slog.e(TAG, "partition not available", e);
                 return;
             }
-
-            ByteBuffer data = ByteBuffer.allocate(FRP_CREDENTIAL_RESERVED_SIZE);
-            data.putInt(handle == null ? 0 : handle.length);
-            if (handle != null) {
-                data.put(handle);
-            }
-            data.flip();
-
             synchronized (mLock) {
                 if (!mIsWritable) {
                     IoUtils.closeQuietly(outputStream);
                     return;
                 }
-
                 try {
                     FileChannel channel = outputStream.getChannel();
-
-                    channel.position(getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE);
-                    channel.write(data);
+                    channel.position(offset);
+                    channel.write(dataBuffer);
                     outputStream.flush();
                 } catch (IOException e) {
                     Slog.e(TAG, "unable to access persistent partition", e);
@@ -641,8 +725,7 @@
             }
         }
 
-        @Override
-        public byte[] getFrpCredentialHandle() {
+        private byte[] readInternal(long offset, int maxLength) {
             if (!enforceChecksumValidity()) {
                 throw new IllegalStateException("invalid checksum");
             }
@@ -652,14 +735,14 @@
                 inputStream = new DataInputStream(
                         new FileInputStream(new File(mDataBlockFile)));
             } catch (FileNotFoundException e) {
-                throw new IllegalStateException("frp partition not available");
+                throw new IllegalStateException("persistent partition not available");
             }
 
             try {
                 synchronized (mLock) {
-                    inputStream.skip(getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE);
+                    inputStream.skip(offset);
                     int length = inputStream.readInt();
-                    if (length <= 0 || length > MAX_FRP_CREDENTIAL_HANDLE_SIZE) {
+                    if (length <= 0 || length > maxLength) {
                         return null;
                     }
                     byte[] bytes = new byte[length];
@@ -667,7 +750,7 @@
                     return bytes;
                 }
             } catch (IOException e) {
-                throw new IllegalStateException("frp handle not readable", e);
+                throw new IllegalStateException("persistent partition not readable", e);
             } finally {
                 IoUtils.closeQuietly(inputStream);
             }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index ac392a1..a96676e 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -82,7 +82,6 @@
 import android.webkit.WebViewZygote;
 
 import com.android.internal.R;
-import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.app.procstats.ServiceState;
 import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.notification.SystemNotificationChannels;
@@ -169,8 +168,6 @@
     /** Temporary list for holding the results of calls to {@link #collectPackageServicesLocked} */
     private ArrayList<ServiceRecord> mTmpCollectionResults = null;
 
-    private int mNumServiceRestarts = 0;
-
     /**
      * For keeping ActiveForegroundApps retaining state while the screen is off.
      */
@@ -2330,50 +2327,19 @@
                 r.restartDelay = mAm.mConstants.BOUND_SERVICE_CRASH_RESTART_DURATION
                         * (r.crashCount - 1);
             } else {
-                mNumServiceRestarts++;
-                if (!mAm.mConstants.FLAG_USE_MEM_AWARE_SERVICE_RESTARTS) {
-                    // If it has been a "reasonably long time" since the service
-                    // was started, then reset our restart duration back to
-                    // the beginning, so we don't infinitely increase the duration
-                    // on a service that just occasionally gets killed (which is
-                    // a normal case, due to process being killed to reclaim memory).
-                    if (now > (r.restartTime + resetTime)) {
-                        r.restartCount = 1;
-                        r.restartDelay = minDuration;
-                    } else {
-                        r.restartDelay *= mAm.mConstants.SERVICE_RESTART_DURATION_FACTOR;
-                        if (r.restartDelay < minDuration) {
-                            r.restartDelay = minDuration;
-                        }
-                    }
+                // If it has been a "reasonably long time" since the service
+                // was started, then reset our restart duration back to
+                // the beginning, so we don't infinitely increase the duration
+                // on a service that just occasionally gets killed (which is
+                // a normal case, due to process being killed to reclaim memory).
+                if (now > (r.restartTime+resetTime)) {
+                    r.restartCount = 1;
+                    r.restartDelay = minDuration;
                 } else {
-                    // The service will be restarted based on the last known oom_adj value
-                    // for the process when it was destroyed. A higher oom_adj value
-                    // means that the service will be restarted quicker.
-                    // If the service seems to keep crashing, the service restart counter will be
-                    // incremented and the restart delay will have an exponential backoff.
-                    final int lastOomAdj = r.app == null ? r.lastKnownOomAdj : r.app.setAdj;
-                    if (r.restartCount > 1) {
-                        r.restartDelay *= mAm.mConstants.SERVICE_RESTART_DURATION_FACTOR;
-                        if (r.restartDelay < minDuration) {
-                            r.restartDelay = minDuration;
-                        }
-                    } else {
-                        if (lastOomAdj <= ProcessList.VISIBLE_APP_ADJ) {
-                            r.restartDelay = 1 * 1000; // 1 second
-                        } else if (lastOomAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
-                            r.restartDelay = 2 * 1000; // 2 seconds
-                        } else if (lastOomAdj <= ProcessList.SERVICE_ADJ) {
-                            r.restartDelay = 5 * 1000; // 5 seconds
-                        } else if (lastOomAdj <= ProcessList.PREVIOUS_APP_ADJ) {
-                            r.restartDelay = 10 * 1000; // 10 seconds
-                        } else {
-                            r.restartDelay = 20 * 1000; // 20 seconds
-                        }
+                    r.restartDelay *= mAm.mConstants.SERVICE_RESTART_DURATION_FACTOR;
+                    if (r.restartDelay < minDuration) {
+                        r.restartDelay = minDuration;
                     }
-                    // If the last time the service restarted was more than a minute ago,
-                    // reset the service restart count, otherwise increment by one.
-                    r.restartCount = (now > (r.restartTime + resetTime)) ? 1 : (r.restartCount + 1);
                 }
             }
 
@@ -2381,7 +2347,21 @@
 
             // Make sure that we don't end up restarting a bunch of services
             // all at the same time.
-            ensureSpacedServiceRestarts(r, now);
+            boolean repeat;
+            do {
+                repeat = false;
+                final long restartTimeBetween = mAm.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN;
+                for (int i=mRestartingServices.size()-1; i>=0; i--) {
+                    ServiceRecord r2 = mRestartingServices.get(i);
+                    if (r2 != r && r.nextRestartTime >= (r2.nextRestartTime-restartTimeBetween)
+                            && r.nextRestartTime < (r2.nextRestartTime+restartTimeBetween)) {
+                        r.nextRestartTime = r2.nextRestartTime + restartTimeBetween;
+                        r.restartDelay = r.nextRestartTime - now;
+                        repeat = true;
+                        break;
+                    }
+                }
+            } while (repeat);
 
         } else {
             // Persistent processes are immediately restarted, so there is no
@@ -2411,24 +2391,6 @@
         return canceled;
     }
 
-    private void ensureSpacedServiceRestarts(ServiceRecord r, long now) {
-        boolean repeat;
-        do {
-            repeat = false;
-            final long restartTimeBetween = mAm.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN;
-            for (int i = mRestartingServices.size() - 1; i >= 0; i--) {
-                ServiceRecord r2 = mRestartingServices.get(i);
-                if (r2 != r && r.nextRestartTime >= (r2.nextRestartTime - restartTimeBetween)
-                        && r.nextRestartTime < (r2.nextRestartTime + restartTimeBetween)) {
-                    r.nextRestartTime = r2.nextRestartTime + restartTimeBetween;
-                    r.restartDelay = r.nextRestartTime - now;
-                    repeat = true;
-                    break;
-                }
-            }
-        } while (repeat);
-    }
-
     final void performServiceRestartLocked(ServiceRecord r) {
         if (!mRestartingServices.contains(r)) {
             return;
@@ -2503,26 +2465,6 @@
             return null;
         }
 
-        // If the service is restarting, check the memory pressure - if it's low or critical, then
-        // further delay the restart because it will most likely get killed again; if the pressure
-        // is not low or critical, continue restarting.
-        if (mAm.mConstants.FLAG_USE_MEM_AWARE_SERVICE_RESTARTS && whileRestarting) {
-            final int memLevel = mAm.getMemoryTrimLevel();
-            if (memLevel >= ProcessStats.ADJ_MEM_FACTOR_LOW) {
-                final long now = SystemClock.uptimeMillis();
-                // Delay the restart based on the current memory pressure
-                // Default delay duration is 5 seconds
-                r.restartDelay = memLevel * mAm.mConstants.SERVICE_RESTART_DELAY_DURATION;
-                r.nextRestartTime = now + r.restartDelay;
-                ensureSpacedServiceRestarts(r, now);
-                mAm.mHandler.removeCallbacks(r.restarter);
-                mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime);
-                Slog.w(TAG, "Delaying restart of crashed service " + r.shortInstanceName
-                        + " in " + r.restartDelay + "ms due to continued memory pressure");
-                return null;
-            }
-        }
-
         if (DEBUG_SERVICE) {
             Slog.v(TAG_SERVICE, "Bringing up " + r + " " + r.intent + " fg=" + r.fgRequired);
         }
@@ -3637,7 +3579,6 @@
                     || !mAm.mUserController.isUserRunning(sr.userId, 0)) {
                 bringDownServiceLocked(sr);
             } else {
-                sr.lastKnownOomAdj = app.setAdj;
                 boolean canceled = scheduleServiceRestartLocked(sr, true);
 
                 // Should the service remain running?  Note that in the
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 546975a..dd2b33a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -76,8 +76,6 @@
     static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
     static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
     static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4";
-    static final String KEY_USE_MEM_AWARE_SERVICE_RESTARTS = "use_mem_aware_service_restarts";
-    static final String KEY_SERVICE_RESTART_DELAY_DURATION = "service_restart_delay_duration";
 
     private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
     private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
@@ -115,8 +113,6 @@
     public static final long DEFAULT_COMPACT_THROTTLE_2 = 10000;
     public static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
     public static final long DEFAULT_COMPACT_THROTTLE_4 = 10000;
-    private static final boolean DEFAULT_USE_MEM_AWARE_SERVICE_RESTARTS = true;
-    private static final long DEFAULT_SERVICE_RESTART_DELAY_DURATION = 5 * 1000;
 
     // Maximum number of cached processes we will allow.
     public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
@@ -253,12 +249,6 @@
     // How long we'll skip second compactAppFull after first compactAppFull
     public long COMPACT_THROTTLE_4 = DEFAULT_COMPACT_THROTTLE_4;
 
-    // Use memory aware optimized logic to restart services
-    public boolean FLAG_USE_MEM_AWARE_SERVICE_RESTARTS = DEFAULT_USE_MEM_AWARE_SERVICE_RESTARTS;
-
-    // How long a service restart will be delayed by if there is memory pressure
-    public long SERVICE_RESTART_DELAY_DURATION = DEFAULT_SERVICE_RESTART_DELAY_DURATION;
-
     // Indicates whether the activity starts logging is enabled.
     // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
     volatile boolean mFlagActivityStartsLoggingEnabled;
@@ -423,10 +413,6 @@
             COMPACT_THROTTLE_2 = mParser.getLong(KEY_COMPACT_THROTTLE_2, DEFAULT_COMPACT_THROTTLE_2);
             COMPACT_THROTTLE_3 = mParser.getLong(KEY_COMPACT_THROTTLE_3, DEFAULT_COMPACT_THROTTLE_3);
             COMPACT_THROTTLE_4 = mParser.getLong(KEY_COMPACT_THROTTLE_4, DEFAULT_COMPACT_THROTTLE_4);
-            FLAG_USE_MEM_AWARE_SERVICE_RESTARTS = mParser.getBoolean(
-                    KEY_USE_MEM_AWARE_SERVICE_RESTARTS, DEFAULT_USE_MEM_AWARE_SERVICE_RESTARTS);
-            SERVICE_RESTART_DELAY_DURATION = mParser.getLong(
-                    KEY_SERVICE_RESTART_DELAY_DURATION, DEFAULT_SERVICE_RESTART_DELAY_DURATION);
 
             updateMaxCachedProcesses();
         }
@@ -519,10 +505,6 @@
         pw.println(TOP_TO_FGS_GRACE_DURATION);
         pw.print("  "); pw.print(KEY_USE_COMPACTION); pw.print("=");
         pw.println(USE_COMPACTION);
-        pw.print("  "); pw.print(KEY_USE_MEM_AWARE_SERVICE_RESTARTS); pw.print("=");
-        pw.println(FLAG_USE_MEM_AWARE_SERVICE_RESTARTS);
-        pw.print("  "); pw.print(KEY_SERVICE_RESTART_DELAY_DURATION); pw.print("=");
-        pw.println(SERVICE_RESTART_DELAY_DURATION);
 
         pw.println();
         if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java
index fe27c49..fd402cc 100644
--- a/services/core/java/com/android/server/am/AppCompactor.java
+++ b/services/core/java/com/android/server/am/AppCompactor.java
@@ -64,7 +64,7 @@
 
     final private String COMPACT_ACTION_FILE = "file";
     final private String COMPACT_ACTION_ANON = "anon";
-    final private String COMPACT_ACTION_FULL = "full";
+    final private String COMPACT_ACTION_FULL = "all";
 
     final private String compactActionSome;
     final private String compactActionFull;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index fc2a444..da5ce1c 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -16,8 +16,10 @@
 
 package com.android.server.am;
 
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import com.android.internal.app.procstats.ServiceState;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.LocalServices;
+import com.android.server.notification.NotificationManagerInternal;
 
 import android.app.INotificationManager;
 import android.app.Notification;
@@ -42,11 +44,6 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.ProtoUtils;
-
-import com.android.internal.app.procstats.ServiceState;
-import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.LocalServices;
-import com.android.server.notification.NotificationManagerInternal;
 import com.android.server.uri.NeededUriGrants;
 import com.android.server.uri.UriPermissionOwner;
 
@@ -55,6 +52,9 @@
 import java.util.List;
 import java.util.Objects;
 
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
 /**
  * A running application service.
  */
@@ -114,7 +114,6 @@
     long executingStart;    // start time of last execute request.
     boolean createdFromFg;  // was this service last created due to a foreground process call?
     int crashCount;         // number of times proc has crashed with service running
-    int lastKnownOomAdj;    // last known OOM adjustment for process (used if ProcessRecord is null)
     int totalRestartCount;  // number of times we have had to restart.
     int restartCount;       // number of restarts performed in a row.
     long restartDelay;      // delay until next restart attempt.
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index d4b8eb2..944a95d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
-import android.content.ComponentName;
 import android.view.inputmethod.InputMethodInfo;
 
 import com.android.server.LocalServices;
@@ -42,11 +41,6 @@
     public abstract void hideCurrentInputMethod();
 
     /**
-     * Switches to VR InputMethod defined in the packageName of {@param componentName}.
-     */
-    public abstract void startVrInputMethodNoCheck(ComponentName componentName);
-
-    /**
      * Returns the list of installed input methods for the specified user.
      *
      * @param userId The user ID to be queried.
@@ -76,10 +70,6 @@
                 }
 
                 @Override
-                public void startVrInputMethodNoCheck(ComponentName componentName) {
-                }
-
-                @Override
                 public List<InputMethodInfo> getInputMethodListAsUser(int userId) {
                     return Collections.emptyList();
                 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 52074a7..eca371a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -91,8 +91,6 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.provider.Settings;
-import android.service.vr.IVrManager;
-import android.service.vr.IVrStateCallbacks;
 import android.text.TextUtils;
 import android.text.style.SuggestionSpan;
 import android.util.ArrayMap;
@@ -203,7 +201,6 @@
     static final int MSG_CREATE_SESSION = 1050;
 
     static final int MSG_START_INPUT = 2000;
-    static final int MSG_START_VR_INPUT = 2010;
 
     static final int MSG_UNBIND_CLIENT = 3000;
     static final int MSG_BIND_CLIENT = 3010;
@@ -381,28 +378,6 @@
         }
     }
 
-    /**
-     * VR state callback.
-     * Listens for when VR mode finishes.
-     */
-    private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
-        @Override
-        public void onVrStateChanged(boolean enabled) {
-            if (!enabled) {
-                restoreNonVrImeFromSettingsNoCheck();
-            }
-        }
-    };
-
-    private void restoreNonVrImeFromSettingsNoCheck() {
-        // switch back to non-VR InputMethod from settings.
-        synchronized (mMethodMap) {
-            if (!mIsVrImeStarted) return;
-            mIsVrImeStarted = false;
-            updateFromSettingsLocked(false);
-        }
-    }
-
     private static final class ClientDeathRecipient implements IBinder.DeathRecipient {
         private final InputMethodManagerService mImms;
         private final IInputMethodClient mClient;
@@ -597,9 +572,6 @@
 
     final ImeDisplayValidator mImeDisplayValidator;
 
-    /** True if VR IME started by {@link #startVrInputMethodNoCheck}. */
-    boolean mIsVrImeStarted;
-
     /**
      * If non-null, this is the input method service we are currently connected
      * to.
@@ -977,31 +949,6 @@
     }
 
     /**
-     * Start a VR InputMethod that matches IME with package name of {@param component}.
-     * Note: This method is called from {@link android.app.VrManager}.
-     */
-    private void startVrInputMethodNoCheck(@Nullable ComponentName component) {
-        if (component == null) {
-            // clear the current VR-only IME (if any) and restore normal IME.
-            restoreNonVrImeFromSettingsNoCheck();
-            return;
-        }
-
-        synchronized (mMethodMap) {
-            String packageName = component.getPackageName();
-            for (InputMethodInfo info : mMethodList) {
-                if (TextUtils.equals(info.getPackageName(), packageName) && info.isVrOnly()) {
-                    // set this is as current inputMethod without updating settings.
-                    setInputMethodEnabledLocked(info.getId(), true);
-                    setInputMethodLocked(info.getId(), NOT_A_SUBTYPE_ID);
-                    mIsVrImeStarted = true;
-                    break;
-                }
-            }
-        }
-    }
-
-    /**
      * Handles {@link Intent#ACTION_LOCALE_CHANGED}.
      *
      * <p>Note: For historical reasons, {@link Intent#ACTION_LOCALE_CHANGED} has been sent to all
@@ -1452,15 +1399,6 @@
         AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);
         mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
                 mSettings, context);
-        // Register VR-state listener.
-        IVrManager vrManager = (IVrManager) ServiceManager.getService(Context.VR_SERVICE);
-        if (vrManager != null) {
-            try {
-                vrManager.registerListener(mVrStateCallbacks);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to register VR mode state listener.");
-            }
-        }
     }
 
     private void resetDefaultImeLocked(Context context) {
@@ -1688,25 +1626,7 @@
             }
             final long ident = Binder.clearCallingIdentity();
             try {
-                return getInputMethodListLocked(false /* isVrOnly */, resolvedUserIds[0]);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public List<InputMethodInfo> getVrInputMethodList() {
-        final int callingUserId = UserHandle.getCallingUserId();
-        synchronized (mMethodMap) {
-            final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
-                    mSettings.getCurrentUserId(), null);
-            if (resolvedUserIds.length != 1) {
-                return Collections.emptyList();
-            }
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                return getInputMethodListLocked(true /* isVrOnly */, resolvedUserIds[0]);
+                return getInputMethodListLocked(resolvedUserIds[0]);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -1732,8 +1652,7 @@
     }
 
     @GuardedBy("mMethodMap")
-    private List<InputMethodInfo> getInputMethodListLocked(boolean isVrOnly,
-            @UserIdInt int userId) {
+    private List<InputMethodInfo> getInputMethodListLocked(@UserIdInt int userId) {
         final ArrayList<InputMethodInfo> methodList;
         if (userId == mSettings.getCurrentUserId()) {
             // Create a copy.
@@ -1747,7 +1666,6 @@
             queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
                     methodList);
         }
-        methodList.removeIf(imi -> imi.isVrOnly() != isVrOnly);
         return methodList;
     }
 
@@ -2021,8 +1939,8 @@
         }
         // Compute the final shown display ID with validated cs.selfReportedDisplayId for this
         // session & other conditions.
-        final int displayIdToShowIme = computeImeDisplayIdForTarget(
-                cs.selfReportedDisplayId, mIsVrImeStarted, mImeDisplayValidator);
+        final int displayIdToShowIme = computeImeDisplayIdForTarget(cs.selfReportedDisplayId,
+                mImeDisplayValidator);
 
         if (mCurClient != cs) {
             // Was the keyguard locked when switching over to the new client?
@@ -2131,17 +2049,11 @@
      * Find the display where the IME should be shown.
      *
      * @param displayId the ID of the display where the IME client target is.
-     * @param isVrImeStarted {@code true} if VR IME started, {@code false} otherwise.
      * @param checker instance of {@link ImeDisplayValidator} which is used for
      *                checking display config to adjust the final target display.
      * @return The ID of the display where the IME should be shown.
      */
-    static int computeImeDisplayIdForTarget(int displayId, boolean isVrImeStarted,
-            @NonNull ImeDisplayValidator checker) {
-        // For VR IME, we always show in default display.
-        if (isVrImeStarted) {
-            return DEFAULT_DISPLAY;
-        }
+    static int computeImeDisplayIdForTarget(int displayId, @NonNull ImeDisplayValidator checker) {
         if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) {
             // We always assume that the default display id suitable to show the IME window.
             return DEFAULT_DISPLAY;
@@ -3627,9 +3539,6 @@
             case MSG_SET_INTERACTIVE:
                 handleSetInteractive(msg.arg1 != 0);
                 return true;
-            case MSG_START_VR_INPUT:
-                startVrInputMethodNoCheck((ComponentName) msg.obj);
-                return true;
             case MSG_REPORT_FULLSCREEN_MODE: {
                 final boolean fullscreen = msg.arg1 != 0;
                 final ClientState clientState = (ClientState)msg.obj;
@@ -3716,6 +3625,9 @@
             try {
                 final InputMethodInfo imi = new InputMethodInfo(context, ri,
                         additionalSubtypeMap.get(imeId));
+                if (imi.isVrOnly()) {
+                    continue;  // Skip VR-only IME, which isn't supported for now.
+                }
                 methodList.add(imi);
                 methodMap.put(imi.getId(), imi);
                 if (DEBUG) {
@@ -4099,17 +4011,7 @@
 
     private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
             boolean setSubtypeOnly) {
-        // Updates to InputMethod are transient in VR mode. Its not included in history.
-        final boolean isVrInput = imi != null && imi.isVrOnly();
-        if (!isVrInput) {
-            // Update the history of InputMethod and Subtype
-            mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
-        }
-
-        if (isVrInput) {
-            // Updates to InputMethod are transient in VR mode. Any changes to Settings are skipped.
-            return;
-        }
+        mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
 
         // Set Subtype here
         if (imi == null || subtypeId < 0) {
@@ -4225,7 +4127,7 @@
 
     private List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
         synchronized (mMethodMap) {
-            return getInputMethodListLocked(false, userId);
+            return getInputMethodListLocked(userId);
         }
     }
 
@@ -4257,11 +4159,6 @@
         }
 
         @Override
-        public void startVrInputMethodNoCheck(@Nullable ComponentName componentName) {
-            mService.mHandler.obtainMessage(MSG_START_VR_INPUT, componentName).sendToTarget();
-        }
-
-        @Override
         public List<InputMethodInfo> getInputMethodListAsUser(int userId) {
             return mService.getInputMethodListAsUser(userId);
         }
@@ -4648,7 +4545,7 @@
                     mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
             for (int userId : userIds) {
                 final List<InputMethodInfo> methods = all
-                        ? getInputMethodListLocked(false, userId)
+                        ? getInputMethodListLocked(userId)
                         : getEnabledInputMethodListLocked(userId);
                 if (userIds.length > 1) {
                     pr.print("User #");
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 6f0c5e8..a31b3b4 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -159,11 +159,6 @@
                         }
 
                         @Override
-                        public void startVrInputMethodNoCheck(ComponentName componentName) {
-                            reportNotSupported();
-                        }
-
-                        @Override
                         public List<InputMethodInfo> getInputMethodListAsUser(
                                 @UserIdInt int userId) {
                             return userIdToInputMethodInfoMapper.getAsList(userId);
@@ -1244,13 +1239,6 @@
 
         @BinderThread
         @Override
-        public List<InputMethodInfo> getVrInputMethodList() {
-            reportNotSupported();
-            return Collections.emptyList();
-        }
-
-        @BinderThread
-        @Override
         public List<InputMethodInfo> getEnabledInputMethodList() {
             return mInputMethodInfoMap.getAsList(UserHandle.getUserId(Binder.getCallingUid()));
         }
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index cc0ac9a..bd12075 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -443,6 +443,16 @@
                 "qc_window_size_rare_ms";
         private static final String KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
                 "qc_max_execution_time_ms";
+        private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE =
+                "qc_max_job_count_active";
+        private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING =
+                "qc_max_job_count_working";
+        private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT =
+                "qc_max_job_count_frequent";
+        private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE =
+                "qc_max_job_count_rare";
+        private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME =
+                "qc_max_count_per_allowed_time";
 
         private static final int DEFAULT_MIN_IDLE_COUNT = 1;
         private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
@@ -479,6 +489,15 @@
                 24 * 60 * 60 * 1000L; // 24 hours
         private static final long DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
                 4 * 60 * 60 * 1000L; // 4 hours
+        private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE =
+                200; // 1200/hr
+        private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING =
+                1200; // 600/hr
+        private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT =
+                1800; // 225/hr
+        private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE =
+                2400; // 100/hr
+        private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = 20;
 
         /**
          * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
@@ -682,6 +701,41 @@
         public long QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
                 DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS;
 
+        /**
+         * The maximum number of jobs an app can run within this particular standby bucket's
+         * window size.
+         */
+        public int QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE =
+                DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE;
+
+        /**
+         * The maximum number of jobs an app can run within this particular standby bucket's
+         * window size.
+         */
+        public int QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING =
+                DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING;
+
+        /**
+         * The maximum number of jobs an app can run within this particular standby bucket's
+         * window size.
+         */
+        public int QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT =
+                DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT;
+
+        /**
+         * The maximum number of jobs an app can run within this particular standby bucket's
+         * window size.
+         */
+        public int QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE =
+                DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE;
+
+        /**
+         * The maximum number of jobs that can run within the past
+         * {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS}.
+         */
+        public int QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME =
+                DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME;
+
         private final KeyValueListParser mParser = new KeyValueListParser(',');
 
         void updateConstantsLocked(String value) {
@@ -767,6 +821,21 @@
             QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = mParser.getDurationMillis(
                     KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS,
                     DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS);
+            QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = mParser.getInt(
+                    KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE,
+                    DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE);
+            QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = mParser.getInt(
+                    KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING,
+                    DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING);
+            QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = mParser.getInt(
+                    KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT,
+                    DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT);
+            QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = mParser.getInt(
+                    KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE,
+                    DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE);
+            QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = mParser.getInt(
+                    KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME,
+                    DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME);
         }
 
         void dump(IndentingPrintWriter pw) {
@@ -823,6 +892,16 @@
                     QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS).println();
             pw.printPair(KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS,
                     QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS).println();
+            pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE,
+                    QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE).println();
+            pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING,
+                    QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING).println();
+            pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT,
+                    QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT).println();
+            pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE,
+                    QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE).println();
+            pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME,
+                    QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME).println();
             pw.decreaseIndent();
         }
 
@@ -872,6 +951,16 @@
                     QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS);
             proto.write(ConstantsProto.QuotaController.MAX_EXECUTION_TIME_MS,
                     QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS);
+            proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_ACTIVE,
+                    QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE);
+            proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_WORKING,
+                    QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING);
+            proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_FREQUENT,
+                    QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT);
+            proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RARE,
+                    QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE);
+            proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_ALLOWED_TIME,
+                    QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME);
             proto.end(qcToken);
 
             proto.end(token);
diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java
index c16d1b4..5a0b991 100644
--- a/services/core/java/com/android/server/job/controllers/QuotaController.java
+++ b/services/core/java/com/android/server/job/controllers/QuotaController.java
@@ -230,10 +230,10 @@
     @VisibleForTesting
     static class ExecutionStats {
         /**
-         * The time at which this record should be considered invalid, in the elapsed realtime
-         * timebase.
+         * The time after which this record should be considered invalid (out of date), in the
+         * elapsed realtime timebase.
          */
-        public long invalidTimeElapsed;
+        public long expirationTimeElapsed;
 
         public long windowSizeMs;
 
@@ -241,29 +241,45 @@
         public long executionTimeInWindowMs;
         public int bgJobCountInWindow;
 
-        /** The total amount of time the app ran in the last {@link MAX_PERIOD_MS}. */
+        /** The total amount of time the app ran in the last {@link #MAX_PERIOD_MS}. */
         public long executionTimeInMaxPeriodMs;
         public int bgJobCountInMaxPeriod;
 
         /**
-         * The time after which the sum of all the app's sessions plus {@link mQuotaBufferMs} equals
-         * the quota. This is only valid if
-         * executionTimeInWindowMs >= {@link mAllowedTimePerPeriodMs} or
-         * executionTimeInMaxPeriodMs >= {@link mMaxExecutionTimeMs}.
+         * The time after which the sum of all the app's sessions plus {@link #mQuotaBufferMs}
+         * equals the quota. This is only valid if
+         * executionTimeInWindowMs >= {@link #mAllowedTimePerPeriodMs} or
+         * executionTimeInMaxPeriodMs >= {@link #mMaxExecutionTimeMs}.
          */
         public long quotaCutoffTimeElapsed;
 
+        /**
+         * The time after which {@link #jobCountInAllowedTime} should be considered invalid, in the
+         * elapsed realtime timebase.
+         */
+        public long jobCountExpirationTimeElapsed;
+
+        /**
+         * The number of jobs that ran in at least the last {@link #mAllowedTimePerPeriodMs}.
+         * It may contain a few stale entries since cleanup won't happen exactly every
+         * {@link #mAllowedTimePerPeriodMs}.
+         */
+        public int jobCountInAllowedTime;
+
         @Override
         public String toString() {
             return new StringBuilder()
-                    .append("invalidTime=").append(invalidTimeElapsed).append(", ")
+                    .append("expirationTime=").append(expirationTimeElapsed).append(", ")
                     .append("windowSize=").append(windowSizeMs).append(", ")
                     .append("executionTimeInWindow=").append(executionTimeInWindowMs).append(", ")
                     .append("bgJobCountInWindow=").append(bgJobCountInWindow).append(", ")
                     .append("executionTimeInMaxPeriod=").append(executionTimeInMaxPeriodMs)
                     .append(", ")
                     .append("bgJobCountInMaxPeriod=").append(bgJobCountInMaxPeriod).append(", ")
-                    .append("quotaCutoffTime=").append(quotaCutoffTimeElapsed)
+                    .append("quotaCutoffTime=").append(quotaCutoffTimeElapsed).append(", ")
+                    .append("jobCountExpirationTime").append(jobCountExpirationTimeElapsed)
+                    .append(", ")
+                    .append("jobCountInAllowedTime").append(jobCountInAllowedTime)
                     .toString();
         }
 
@@ -271,13 +287,15 @@
         public boolean equals(Object obj) {
             if (obj instanceof ExecutionStats) {
                 ExecutionStats other = (ExecutionStats) obj;
-                return this.invalidTimeElapsed == other.invalidTimeElapsed
+                return this.expirationTimeElapsed == other.expirationTimeElapsed
                         && this.windowSizeMs == other.windowSizeMs
                         && this.executionTimeInWindowMs == other.executionTimeInWindowMs
                         && this.bgJobCountInWindow == other.bgJobCountInWindow
                         && this.executionTimeInMaxPeriodMs == other.executionTimeInMaxPeriodMs
                         && this.bgJobCountInMaxPeriod == other.bgJobCountInMaxPeriod
-                        && this.quotaCutoffTimeElapsed == other.quotaCutoffTimeElapsed;
+                        && this.quotaCutoffTimeElapsed == other.quotaCutoffTimeElapsed
+                        && this.jobCountExpirationTimeElapsed == other.jobCountExpirationTimeElapsed
+                        && this.jobCountInAllowedTime == other.jobCountInAllowedTime;
             } else {
                 return false;
             }
@@ -286,13 +304,15 @@
         @Override
         public int hashCode() {
             int result = 0;
-            result = 31 * result + hashLong(invalidTimeElapsed);
+            result = 31 * result + hashLong(expirationTimeElapsed);
             result = 31 * result + hashLong(windowSizeMs);
             result = 31 * result + hashLong(executionTimeInWindowMs);
             result = 31 * result + bgJobCountInWindow;
             result = 31 * result + hashLong(executionTimeInMaxPeriodMs);
             result = 31 * result + bgJobCountInMaxPeriod;
             result = 31 * result + hashLong(quotaCutoffTimeElapsed);
+            result = 31 * result + hashLong(jobCountExpirationTimeElapsed);
+            result = 31 * result + jobCountInAllowedTime;
             return result;
         }
     }
@@ -320,7 +340,7 @@
 
     /**
      * List of jobs that started while the UID was in the TOP state. There will be no more than
-     * 16 ({@link JobSchedulerService.MAX_JOB_CONTEXTS_COUNT}) running at once, so an ArraySet is
+     * 16 ({@link JobSchedulerService#MAX_JOB_CONTEXTS_COUNT}) running at once, so an ArraySet is
      * fine.
      */
     private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>();
@@ -343,7 +363,7 @@
     private long mAllowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
 
     /**
-     * The maximum amount of time an app can have its jobs running within a {@link MAX_PERIOD_MS}
+     * The maximum amount of time an app can have its jobs running within a {@link #MAX_PERIOD_MS}
      * window.
      */
     private long mMaxExecutionTimeMs = 4 * 60 * MINUTE_IN_MILLIS;
@@ -355,17 +375,20 @@
     private long mQuotaBufferMs = 30 * 1000L; // 30 seconds
 
     /**
-     * {@link mAllowedTimePerPeriodMs} - {@link mQuotaBufferMs}. This can be used to determine when
-     * an app will have enough quota to transition from out-of-quota to in-quota.
+     * {@link #mAllowedTimePerPeriodMs} - {@link #mQuotaBufferMs}. This can be used to determine
+     * when an app will have enough quota to transition from out-of-quota to in-quota.
      */
     private long mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
 
     /**
-     * {@link mMaxExecutionTimeMs} - {@link mQuotaBufferMs}. This can be used to determine when an
+     * {@link #mMaxExecutionTimeMs} - {@link #mQuotaBufferMs}. This can be used to determine when an
      * app will have enough quota to transition from out-of-quota to in-quota.
      */
     private long mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
 
+    /** The maximum number of jobs that can run within the past {@link #mAllowedTimePerPeriodMs}. */
+    private int mMaxJobCountPerAllowedTime = 20;
+
     private long mNextCleanupTimeElapsed = 0;
     private final AlarmManager.OnAlarmListener mSessionCleanupAlarmListener =
             new AlarmManager.OnAlarmListener() {
@@ -412,6 +435,23 @@
     /** The maximum period any bucket can have. */
     private static final long MAX_PERIOD_MS = 24 * 60 * MINUTE_IN_MILLIS;
 
+    /**
+     * The maximum number of jobs based on its standby bucket. For each max value count in the
+     * array, the app will not be allowed to run more than that many number of jobs within the
+     * latest time interval of its rolling window size.
+     *
+     * @see #mBucketPeriodsMs
+     */
+    private final int[] mMaxBucketJobCounts = new int[] {
+            200,  // ACTIVE   -- 1200/hr
+            1200, // WORKING  -- 600/hr
+            1800, // FREQUENT -- 225/hr
+            2400  // RARE     -- 100/hr
+    };
+
+    /** The minimum number of jobs that any bucket will be allowed to run. */
+    private static final int MIN_BUCKET_JOB_COUNT = 100;
+
     /** An app has reached its quota. The message should contain a {@link Package} object. */
     private static final int MSG_REACHED_QUOTA = 0;
     /** Drop any old timing sessions. */
@@ -463,17 +503,21 @@
     @Override
     public void prepareForExecutionLocked(JobStatus jobStatus) {
         if (DEBUG) Slog.d(TAG, "Prepping for " + jobStatus.toShortString());
+
+        final int uid = jobStatus.getSourceUid();
+        if (mActivityManagerInternal.getUidProcessState(uid) <= ActivityManager.PROCESS_STATE_TOP) {
+            mTopStartedJobs.add(jobStatus);
+            // Top jobs won't count towards quota so there's no need to involve the Timer.
+            return;
+        }
+
         final int userId = jobStatus.getSourceUserId();
         final String packageName = jobStatus.getSourcePackageName();
-        final int uid = jobStatus.getSourceUid();
         Timer timer = mPkgTimers.get(userId, packageName);
         if (timer == null) {
             timer = new Timer(uid, userId, packageName);
             mPkgTimers.add(userId, packageName, timer);
         }
-        if (mActivityManagerInternal.getUidProcessState(uid) == ActivityManager.PROCESS_STATE_TOP) {
-            mTopStartedJobs.add(jobStatus);
-        }
         timer.startTrackingJob(jobStatus);
     }
 
@@ -548,6 +592,36 @@
             mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
             changed = true;
         }
+        int newMaxCountPerAllowedPeriod = Math.max(10,
+                mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME);
+        if (mMaxJobCountPerAllowedTime != newMaxCountPerAllowedPeriod) {
+            mMaxJobCountPerAllowedTime = newMaxCountPerAllowedPeriod;
+            changed = true;
+        }
+        int newActiveMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
+                Math.max(MIN_BUCKET_JOB_COUNT, mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE));
+        if (mMaxBucketJobCounts[ACTIVE_INDEX] != newActiveMaxJobCount) {
+            mMaxBucketJobCounts[ACTIVE_INDEX] = newActiveMaxJobCount;
+            changed = true;
+        }
+        int newWorkingMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
+                Math.max(MIN_BUCKET_JOB_COUNT, mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING));
+        if (mMaxBucketJobCounts[WORKING_INDEX] != newWorkingMaxJobCount) {
+            mMaxBucketJobCounts[WORKING_INDEX] = newWorkingMaxJobCount;
+            changed = true;
+        }
+        int newFrequentMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
+                Math.max(MIN_BUCKET_JOB_COUNT, mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT));
+        if (mMaxBucketJobCounts[FREQUENT_INDEX] != newFrequentMaxJobCount) {
+            mMaxBucketJobCounts[FREQUENT_INDEX] = newFrequentMaxJobCount;
+            changed = true;
+        }
+        int newRareMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
+                Math.max(MIN_BUCKET_JOB_COUNT, mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE));
+        if (mMaxBucketJobCounts[RARE_INDEX] != newRareMaxJobCount) {
+            mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount;
+            changed = true;
+        }
 
         if (changed) {
             // Update job bookkeeping out of band.
@@ -631,18 +705,39 @@
         return isTopStartedJob(jobStatus)
                 || isUidInForeground(jobStatus.getSourceUid())
                 || isWithinQuotaLocked(
-                      jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket);
+                jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket);
     }
 
-    private boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
+    @VisibleForTesting
+    boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
             final int standbyBucket) {
         if (standbyBucket == NEVER_INDEX) return false;
         // This check is needed in case the flag is toggled after a job has been registered.
         if (!mShouldThrottle) return true;
 
         // Quota constraint is not enforced while charging or when parole is on.
-        return mChargeTracker.isCharging() || mInParole
-                || getRemainingExecutionTimeLocked(userId, packageName, standbyBucket) > 0;
+        if (mChargeTracker.isCharging() || mInParole) {
+            return true;
+        }
+
+        return getRemainingExecutionTimeLocked(userId, packageName, standbyBucket) > 0
+                && isUnderJobCountQuotaLocked(userId, packageName, standbyBucket);
+    }
+
+    private boolean isUnderJobCountQuotaLocked(final int userId, @NonNull final String packageName,
+            final int standbyBucket) {
+        ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket, false);
+        return isUnderJobCountQuotaLocked(stats, standbyBucket);
+    }
+
+    private boolean isUnderJobCountQuotaLocked(@NonNull ExecutionStats stats,
+            final int standbyBucket) {
+        final long now = sElapsedRealtimeClock.millis();
+        final boolean isUnderAllowedTimeQuota =
+                (stats.jobCountExpirationTimeElapsed <= now
+                        || stats.jobCountInAllowedTime < mMaxJobCountPerAllowedTime);
+        return isUnderAllowedTimeQuota
+                && (stats.bgJobCountInWindow < mMaxBucketJobCounts[standbyBucket]);
     }
 
     @VisibleForTesting
@@ -679,6 +774,13 @@
     @NonNull
     ExecutionStats getExecutionStatsLocked(final int userId, @NonNull final String packageName,
             final int standbyBucket) {
+        return getExecutionStatsLocked(userId, packageName, standbyBucket, true);
+    }
+
+    @NonNull
+    private ExecutionStats getExecutionStatsLocked(final int userId,
+            @NonNull final String packageName, final int standbyBucket,
+            final boolean refreshStatsIfOld) {
         if (standbyBucket == NEVER_INDEX) {
             Slog.wtf(TAG, "getExecutionStatsLocked called for a NEVER app.");
             return new ExecutionStats();
@@ -693,14 +795,16 @@
             stats = new ExecutionStats();
             appStats[standbyBucket] = stats;
         }
-        final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
-        Timer timer = mPkgTimers.get(userId, packageName);
-        if ((timer != null && timer.isActive())
-                || stats.invalidTimeElapsed <= sElapsedRealtimeClock.millis()
-                || stats.windowSizeMs != bucketWindowSizeMs) {
-            // The stats are no longer valid.
-            stats.windowSizeMs = bucketWindowSizeMs;
-            updateExecutionStatsLocked(userId, packageName, stats);
+        if (refreshStatsIfOld) {
+            final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
+            Timer timer = mPkgTimers.get(userId, packageName);
+            if ((timer != null && timer.isActive())
+                    || stats.expirationTimeElapsed <= sElapsedRealtimeClock.millis()
+                    || stats.windowSizeMs != bucketWindowSizeMs) {
+                // The stats are no longer valid.
+                stats.windowSizeMs = bucketWindowSizeMs;
+                updateExecutionStatsLocked(userId, packageName, stats);
+            }
         }
 
         return stats;
@@ -717,14 +821,14 @@
 
         Timer timer = mPkgTimers.get(userId, packageName);
         final long nowElapsed = sElapsedRealtimeClock.millis();
-        stats.invalidTimeElapsed = nowElapsed + MAX_PERIOD_MS;
+        stats.expirationTimeElapsed = nowElapsed + MAX_PERIOD_MS;
         if (timer != null && timer.isActive()) {
             stats.executionTimeInWindowMs =
                     stats.executionTimeInMaxPeriodMs = timer.getCurrentDuration(nowElapsed);
             stats.bgJobCountInWindow = stats.bgJobCountInMaxPeriod = timer.getBgJobCount();
             // If the timer is active, the value will be stale at the next method call, so
             // invalidate now.
-            stats.invalidTimeElapsed = nowElapsed;
+            stats.expirationTimeElapsed = nowElapsed;
             if (stats.executionTimeInWindowMs >= mAllowedTimeIntoQuotaMs) {
                 stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
                         nowElapsed - mAllowedTimeIntoQuotaMs);
@@ -800,7 +904,7 @@
                 break;
             }
         }
-        stats.invalidTimeElapsed = nowElapsed + emptyTimeMs;
+        stats.expirationTimeElapsed = nowElapsed + emptyTimeMs;
     }
 
     private void invalidateAllExecutionStatsLocked(final int userId,
@@ -811,13 +915,35 @@
             for (int i = 0; i < appStats.length; ++i) {
                 ExecutionStats stats = appStats[i];
                 if (stats != null) {
-                    stats.invalidTimeElapsed = nowElapsed;
+                    stats.expirationTimeElapsed = nowElapsed;
                 }
             }
         }
     }
 
     @VisibleForTesting
+    void incrementJobCount(final int userId, @NonNull final String packageName, int count) {
+        final long now = sElapsedRealtimeClock.millis();
+        ExecutionStats[] appStats = mExecutionStatsCache.get(userId, packageName);
+        if (appStats == null) {
+            appStats = new ExecutionStats[mBucketPeriodsMs.length];
+            mExecutionStatsCache.add(userId, packageName, appStats);
+        }
+        for (int i = 0; i < appStats.length; ++i) {
+            ExecutionStats stats = appStats[i];
+            if (stats == null) {
+                stats = new ExecutionStats();
+                appStats[i] = stats;
+            }
+            if (stats.jobCountExpirationTimeElapsed <= now) {
+                stats.jobCountExpirationTimeElapsed = now + mAllowedTimePerPeriodMs;
+                stats.jobCountInAllowedTime = 0;
+            }
+            stats.jobCountInAllowedTime += count;
+        }
+    }
+
+    @VisibleForTesting
     void saveTimingSession(final int userId, @NonNull final String packageName,
             @NonNull final TimingSession session) {
         synchronized (mLock) {
@@ -1023,9 +1149,12 @@
 
         final String pkgString = string(userId, packageName);
         ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
+        final boolean isUnderJobCountQuota = isUnderJobCountQuotaLocked(stats, standbyBucket);
+
         QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
         if (stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs
-                && stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs) {
+                && stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
+                && isUnderJobCountQuota) {
             // Already in quota. Why was this method called?
             if (DEBUG) {
                 Slog.e(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
@@ -1042,18 +1171,22 @@
             mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget();
             return;
         }
+
         if (alarmListener == null) {
             alarmListener = new QcAlarmListener(userId, packageName);
             mInQuotaAlarmListeners.add(userId, packageName, alarmListener);
         }
 
         // The time this app will have quota again.
-        long inQuotaTimeElapsed =
-                stats.quotaCutoffTimeElapsed + stats.windowSizeMs;
+        long inQuotaTimeElapsed = stats.quotaCutoffTimeElapsed + stats.windowSizeMs;
         if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeMs) {
             inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
                     stats.quotaCutoffTimeElapsed + MAX_PERIOD_MS);
         }
+        if (!isUnderJobCountQuota) {
+            inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
+                    stats.jobCountExpirationTimeElapsed + mAllowedTimePerPeriodMs);
+        }
         // Only schedule the alarm if:
         // 1. There isn't one currently scheduled
         // 2. The new alarm is significantly earlier than the previous alarm (which could be the
@@ -1228,6 +1361,7 @@
                 mRunningBgJobs.add(jobStatus);
                 if (shouldTrackLocked()) {
                     mBgJobCount++;
+                    incrementJobCount(mPkg.userId, mPkg.packageName, 1);
                     if (mRunningBgJobs.size() == 1) {
                         // Started tracking the first job.
                         mStartTimeElapsed = sElapsedRealtimeClock.millis();
@@ -1324,6 +1458,7 @@
                         // repeatedly plugged in and unplugged, or an app changes foreground state
                         // very frequently, the job count for a package may be artificially high.
                         mBgJobCount = mRunningBgJobs.size();
+                        incrementJobCount(mPkg.userId, mPkg.packageName, mBgJobCount);
                         // Starting the timer means that all cached execution stats are now
                         // incorrect.
                         invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName);
@@ -1604,6 +1739,12 @@
 
     @VisibleForTesting
     @NonNull
+    int[] getBucketMaxJobCounts() {
+        return mMaxBucketJobCounts;
+    }
+
+    @VisibleForTesting
+    @NonNull
     long[] getBucketWindowSizes() {
         return mBucketPeriodsMs;
     }
@@ -1631,6 +1772,11 @@
     }
 
     @VisibleForTesting
+    int getMaxJobCountPerAllowedTime() {
+        return mMaxJobCountPerAllowedTime;
+    }
+
+    @VisibleForTesting
     @Nullable
     List<TimingSession> getTimingSessions(int userId, String packageName) {
         return mTimingSessions.get(userId, packageName);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 8734ceb6..a9ae74f 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -947,6 +947,10 @@
     public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
             String managedUserPassword) {
         checkWritePermission(userId);
+        if (!mLockPatternUtils.hasSecureLockScreen()) {
+            throw new UnsupportedOperationException(
+                    "This operation requires secure lock screen feature.");
+        }
         synchronized (mSeparateChallengeLock) {
             setSeparateProfileChallengeEnabledLocked(userId, enabled, managedUserPassword);
         }
@@ -1305,6 +1309,10 @@
     public void setLockCredential(String credential, int type, String savedCredential,
             int requestedQuality, int userId)
             throws RemoteException {
+        if (!mLockPatternUtils.hasSecureLockScreen()) {
+            throw new UnsupportedOperationException(
+                    "This operation requires secure lock screen feature");
+        }
         checkWritePermission(userId);
         synchronized (mSeparateChallengeLock) {
             setLockCredentialInternal(credential, type, savedCredential, requestedQuality, userId);
@@ -2906,6 +2914,10 @@
         @Override
         public boolean setLockCredentialWithToken(String credential, int type, long tokenHandle,
                 byte[] token, int requestedQuality, int userId) {
+            if (!mLockPatternUtils.hasSecureLockScreen()) {
+                throw new UnsupportedOperationException(
+                        "This operation requires secure lock screen feature.");
+            }
             try {
                 return LockSettingsService.this.setLockCredentialWithToken(credential, type,
                         tokenHandle, token, requestedQuality, userId);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 07f23ce..6163077 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -18,6 +18,7 @@
 
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+
 import static com.android.internal.widget.LockPatternUtils.stringToPattern;
 
 import android.app.ActivityManager;
@@ -58,6 +59,18 @@
             mCurrentUserId = ActivityManager.getService().getCurrentUser().id;
 
             parseArgs();
+            if (!mLockPatternUtils.hasSecureLockScreen()) {
+                switch (cmd) {
+                    case COMMAND_HELP:
+                    case COMMAND_GET_DISABLED:
+                    case COMMAND_SET_DISABLED:
+                        break;
+                    default:
+                        getErrPrintWriter().println(
+                                "The device does not support lock screen - ignoring the command.");
+                        return -1;
+                }
+            }
             if (!checkCredential()) {
                 return -1;
             }
diff --git a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
index 2ae424d..5b765df 100644
--- a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
+++ b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
@@ -22,63 +22,117 @@
 import android.app.job.JobService;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.Process;
 import android.os.ServiceManager;
+import android.util.ByteStringUtils;
+import android.util.EventLog;
 import android.util.Log;
 
 import com.android.server.pm.dex.DexLogger;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
- * Scheduled job to trigger logging of app dynamic code loading. This runs daily while idle and
- * charging. The actual logging is performed by {@link DexLogger}.
+ * Scheduled jobs related to logging of app dynamic code loading. The idle logging job runs daily
+ * while idle and charging  and calls {@link DexLogger} to write dynamic code information to the
+ * event log. The audit watching job scans the event log periodically while idle to find AVC audit
+ * messages indicating use of dynamic native code and adds the information to {@link DexLogger}.
  * {@hide}
  */
 public class DynamicCodeLoggingService extends JobService {
     private static final String TAG = DynamicCodeLoggingService.class.getName();
 
-    private static final int JOB_ID = 2030028;
-    private static final long PERIOD_MILLIS = TimeUnit.DAYS.toMillis(1);
-
-    private volatile boolean mStopRequested = false;
-
     private static final boolean DEBUG = false;
 
+    private static final int IDLE_LOGGING_JOB_ID = 2030028;
+    private static final int AUDIT_WATCHING_JOB_ID = 203142925;
+
+    private static final long IDLE_LOGGING_PERIOD_MILLIS = TimeUnit.DAYS.toMillis(1);
+    private static final long AUDIT_WATCHING_PERIOD_MILLIS = TimeUnit.HOURS.toMillis(2);
+
+    private static final int AUDIT_AVC = 1400;  // Defined in linux/audit.h
+    private static final String AVC_PREFIX = "type=" + AUDIT_AVC + " ";
+
+    private static final Pattern EXECUTE_NATIVE_AUDIT_PATTERN =
+            Pattern.compile(".*\\bavc: granted \\{ execute(?:_no_trans|) \\} .*"
+                    + "\\bpath=(?:\"([^\" ]*)\"|([0-9A-F]+)) .*"
+                    + "\\bscontext=u:r:untrusted_app_2(?:5|7):.*"
+                    + "\\btcontext=u:object_r:app_data_file:.*"
+                    + "\\btclass=file\\b.*");
+
+    private volatile boolean mIdleLoggingStopRequested = false;
+    private volatile boolean mAuditWatchingStopRequested = false;
+
     /**
-     * Schedule our job with the {@link JobScheduler}.
+     * Schedule our jobs with the {@link JobScheduler}.
      */
     public static void schedule(Context context) {
         ComponentName serviceName = new ComponentName(
                 "android", DynamicCodeLoggingService.class.getName());
 
         JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-        js.schedule(new JobInfo.Builder(JOB_ID, serviceName)
+        js.schedule(new JobInfo.Builder(IDLE_LOGGING_JOB_ID, serviceName)
                 .setRequiresDeviceIdle(true)
                 .setRequiresCharging(true)
-                .setPeriodic(PERIOD_MILLIS)
+                .setPeriodic(IDLE_LOGGING_PERIOD_MILLIS)
                 .build());
+        js.schedule(new JobInfo.Builder(AUDIT_WATCHING_JOB_ID, serviceName)
+                .setRequiresDeviceIdle(true)
+                .setRequiresBatteryNotLow(true)
+                .setPeriodic(AUDIT_WATCHING_PERIOD_MILLIS)
+                .build());
+
         if (DEBUG) {
-            Log.d(TAG, "Job scheduled");
+            Log.d(TAG, "Jobs scheduled");
         }
     }
 
     @Override
     public boolean onStartJob(JobParameters params) {
+        int jobId = params.getJobId();
         if (DEBUG) {
-            Log.d(TAG, "onStartJob");
+            Log.d(TAG, "onStartJob " + jobId);
         }
-        mStopRequested = false;
-        new IdleLoggingThread(params).start();
-        return true;  // Job is running on another thread
+        switch (jobId) {
+            case IDLE_LOGGING_JOB_ID:
+                mIdleLoggingStopRequested = false;
+                new IdleLoggingThread(params).start();
+                return true;  // Job is running on another thread
+            case AUDIT_WATCHING_JOB_ID:
+                mAuditWatchingStopRequested = false;
+                new AuditWatchingThread(params).start();
+                return true;  // Job is running on another thread
+            default:
+                // Shouldn't happen, but indicate nothing is running.
+                return false;
+        }
     }
 
     @Override
     public boolean onStopJob(JobParameters params) {
+        int jobId = params.getJobId();
         if (DEBUG) {
-            Log.d(TAG, "onStopJob");
+            Log.d(TAG, "onStopJob " + jobId);
         }
-        mStopRequested = true;
-        return true;  // Requests job be re-scheduled.
+        switch (jobId) {
+            case IDLE_LOGGING_JOB_ID:
+                mIdleLoggingStopRequested = true;
+                return true;  // Requests job be re-scheduled.
+            case AUDIT_WATCHING_JOB_ID:
+                mAuditWatchingStopRequested = true;
+                return true;  // Requests job be re-scheduled.
+            default:
+                return false;
+        }
+    }
+
+    private static DexLogger getDexLogger() {
+        PackageManagerService pm = (PackageManagerService) ServiceManager.getService("package");
+        return pm.getDexManager().getDexLogger();
     }
 
     private class IdleLoggingThread extends Thread {
@@ -92,14 +146,13 @@
         @Override
         public void run() {
             if (DEBUG) {
-                Log.d(TAG, "Starting logging run");
+                Log.d(TAG, "Starting IdleLoggingJob run");
             }
 
-            PackageManagerService pm = (PackageManagerService) ServiceManager.getService("package");
-            DexLogger dexLogger = pm.getDexManager().getDexLogger();
+            DexLogger dexLogger = getDexLogger();
             for (String packageName : dexLogger.getAllPackagesWithDynamicCodeLoading()) {
-                if (mStopRequested) {
-                    Log.w(TAG, "Stopping logging run at scheduler request");
+                if (mIdleLoggingStopRequested) {
+                    Log.w(TAG, "Stopping IdleLoggingJob run at scheduler request");
                     return;
                 }
 
@@ -108,8 +161,128 @@
 
             jobFinished(mParams, /* reschedule */ false);
             if (DEBUG) {
-                Log.d(TAG, "Finished logging run");
+                Log.d(TAG, "Finished IdleLoggingJob run");
             }
         }
     }
+
+    private class AuditWatchingThread extends Thread {
+        private final JobParameters mParams;
+
+        AuditWatchingThread(JobParameters params) {
+            super("DynamicCodeLoggingService_AuditWatchingJob");
+            mParams = params;
+        }
+
+        @Override
+        public void run() {
+            if (DEBUG) {
+                Log.d(TAG, "Starting AuditWatchingJob run");
+            }
+
+            if (processAuditEvents()) {
+                jobFinished(mParams, /* reschedule */ false);
+                if (DEBUG) {
+                    Log.d(TAG, "Finished AuditWatchingJob run");
+                }
+            }
+        }
+
+        private boolean processAuditEvents() {
+            // Scan the event log for SELinux (avc) audit messages indicating when an
+            // (untrusted) app has executed native code from an app data
+            // file. Matches are recorded in DexLogger.
+            //
+            // These messages come from the kernel audit system via logd. (Note that
+            // some devices may not generate these messages at all, or the format may
+            // be different, in which case nothing will be recorded.)
+            //
+            // The messages use the auditd tag and the uid of the app that executed
+            // the code.
+            //
+            // A typical message might look like this:
+            // type=1400 audit(0.0:521): avc: granted { execute } for comm="executable"
+            //  path="/data/data/com.dummy.app/executable" dev="sda13" ino=1655302
+            //  scontext=u:r:untrusted_app_27:s0:c66,c257,c512,c768
+            //  tcontext=u:object_r:app_data_file:s0:c66,c257,c512,c768 tclass=file
+            //
+            // The information we want is the uid and the path. (Note this may be
+            // either a quoted string, as shown above, or a sequence of hex-encoded
+            // bytes.)
+            //
+            // On each run we process all the matching events in the log. This may
+            // mean re-processing events we have already seen, and in any case there
+            // may be duplicate events for the same app+file. These are de-duplicated
+            // by DexLogger.
+            //
+            // Note that any app can write a message to the event log, including one
+            // that looks exactly like an AVC audit message, so the information may
+            // be spoofed by an app; in such a case the uid we see will be the app
+            // that generated the spoof message.
+
+            try {
+                int[] tags = { EventLog.getTagCode("auditd") };
+                if (tags[0] == -1) {
+                    // auditd is not a registered tag on this system, so there can't be any messages
+                    // of interest.
+                    return true;
+                }
+
+                DexLogger dexLogger = getDexLogger();
+
+                List<EventLog.Event> events = new ArrayList<>();
+                EventLog.readEvents(tags, events);
+
+                for (int i = 0; i < events.size(); ++i) {
+                    if (mAuditWatchingStopRequested) {
+                        Log.w(TAG, "Stopping AuditWatchingJob run at scheduler request");
+                        return false;
+                    }
+
+                    EventLog.Event event = events.get(i);
+
+                    // Discard clearly unrelated messages as quickly as we can.
+                    int uid = event.getUid();
+                    if (!Process.isApplicationUid(uid)) {
+                        continue;
+                    }
+                    Object data = event.getData();
+                    if (!(data instanceof String)) {
+                        continue;
+                    }
+                    String message = (String) data;
+                    if (!message.startsWith(AVC_PREFIX)) {
+                        continue;
+                    }
+
+                    // And then use a regular expression to verify it's one of the messages we're
+                    // interested in and to extract the path of the file being loaded.
+                    Matcher matcher = EXECUTE_NATIVE_AUDIT_PATTERN.matcher(message);
+                    if (!matcher.matches()) {
+                        continue;
+                    }
+                    String path = matcher.group(1);
+                    if (path == null) {
+                        // If the path contains spaces or various weird characters the kernel
+                        // hex-encodes the bytes; we need to undo that.
+                        path = unhex(matcher.group(2));
+                    }
+                    dexLogger.recordNative(uid, path);
+                }
+
+                return true;
+            } catch (Exception e) {
+                Log.e(TAG, "AuditWatchingJob failed", e);
+                return true;
+            }
+        }
+    }
+
+    private static String unhex(String hexEncodedPath) {
+        byte[] bytes = ByteStringUtils.fromHexToByteArray(hexEncodedPath);
+        if (bytes == null || bytes.length == 0) {
+            return "";
+        }
+        return new String(bytes);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 8a6105c..efafdfa 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -611,6 +611,31 @@
         }
     }
 
+    public boolean snapshotAppData(String pkg, @UserIdInt int userId, int storageFlags)
+            throws InstallerException {
+        if (!checkBeforeRemote()) return false;
+
+        try {
+            mInstalld.snapshotAppData(null, pkg, userId, storageFlags);
+            return true;
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
+    public boolean restoreAppDataSnapshot(String pkg, @AppIdInt  int appId, long ceDataInode,
+            String seInfo, @UserIdInt int userId, int storageFlags) throws InstallerException {
+        if (!checkBeforeRemote()) return false;
+
+        try {
+            mInstalld.restoreAppDataSnapshot(null, pkg, appId, ceDataInode, seInfo, userId,
+                    storageFlags);
+            return true;
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
     private static void assertValidInstructionSet(String instructionSet)
             throws InstallerException {
         for (String abi : Build.SUPPORTED_ABIS) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 09fe26d..b8342cf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -42,6 +42,7 @@
 import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
+import static android.content.pm.PackageManager.INSTALL_ALLOW_DOWNGRADE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
@@ -201,6 +202,7 @@
 import android.content.pm.dex.ArtManager;
 import android.content.pm.dex.DexMetadataHelper;
 import android.content.pm.dex.IArtManager;
+import android.content.rollback.IRollbackManager;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
@@ -13872,6 +13874,38 @@
             }
         }
 
+        // If this is an update to a package that might be potentially downgraded, then we
+        // need to check with the rollback manager whether there's any userdata that might
+        // need to be restored for the package.
+        //
+        // TODO(narayan): Get this working for cases where userId == UserHandle.USER_ALL.
+        if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && !doRestore && update) {
+            IRollbackManager rm = IRollbackManager.Stub.asInterface(
+                    ServiceManager.getService(Context.ROLLBACK_SERVICE));
+
+            final String packageName = res.pkg.applicationInfo.packageName;
+            final String seInfo = res.pkg.applicationInfo.seInfo;
+            final PackageSetting ps;
+            int appId = -1;
+            long ceDataInode = -1;
+            synchronized (mSettings) {
+                ps = mSettings.getPackageLPr(packageName);
+                if (ps != null) {
+                    appId = ps.appId;
+                    ceDataInode = ps.getCeDataInode(userId);
+                }
+            }
+
+            if (ps != null) {
+                try {
+                    rm.restoreUserData(packageName, userId, appId, ceDataInode, seInfo, token);
+                } catch (RemoteException re) {
+                    // Cannot happen, the RollbackManager is hosted in the same process.
+                }
+                doRestore = true;
+            }
+        }
+
         if (!doRestore) {
             // No restore possible, or the Backup Manager was mysteriously not
             // available -- just fire the post-install work request directly.
@@ -14569,6 +14603,9 @@
                     enableRollbackIntent.putExtra(
                             PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS,
                             installFlags);
+                    enableRollbackIntent.putExtra(
+                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALLED_USERS,
+                            resolveUserIds(args.user.getIdentifier()));
                     enableRollbackIntent.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
                             PACKAGE_MIME_TYPE);
                     enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -23791,6 +23828,11 @@
             }
             return mArtManagerService.compileLayouts(pkg);
         }
+
+        @Override
+        public void finishPackageInstall(int token, boolean didLaunch) {
+            PackageManagerService.this.finishPackageInstall(token, didLaunch);
+        }
     }
 
     @GuardedBy("mPackages")
diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DexLogger.java
index 78fa82c..59cc0cf 100644
--- a/services/core/java/com/android/server/pm/dex/DexLogger.java
+++ b/services/core/java/com/android/server/pm/dex/DexLogger.java
@@ -16,11 +16,15 @@
 
 package com.android.server.pm.dex;
 
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.FILE_TYPE_DEX;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.FILE_TYPE_NATIVE;
+
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.os.FileUtils;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.util.ByteStringUtils;
 import android.util.EventLog;
@@ -35,20 +39,23 @@
 import com.android.server.pm.dex.PackageDynamicCodeLoading.PackageDynamicCode;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.Map;
 import java.util.Set;
 
 /**
- * This class is responsible for logging data about secondary dex files.
- * The data logged includes hashes of the name and content of each file.
+ * This class is responsible for logging data about secondary dex files and, despite the name,
+ * native code executed from an app's private directory. The data logged includes hashes of the
+ * name and content of each file.
  */
 public class DexLogger {
     private static final String TAG = "DexLogger";
 
-    // Event log tag & subtag used for SafetyNet logging of dynamic
-    // code loading (DCL) - see b/63927552.
+    // Event log tag & subtags used for SafetyNet logging of dynamic code loading (DCL) -
+    // see b/63927552.
     private static final int SNET_TAG = 0x534e4554;
-    private static final String DCL_SUBTAG = "dcl";
+    private static final String DCL_DEX_SUBTAG = "dcl";
+    private static final String DCL_NATIVE_SUBTAG = "dcln";
 
     private final IPackageManager mPackageManager;
     private final PackageDynamicCodeLoading mPackageDynamicCodeLoading;
@@ -114,12 +121,11 @@
             }
 
             int storageFlags;
-            if (appInfo.deviceProtectedDataDir != null
-                    && FileUtils.contains(appInfo.deviceProtectedDataDir, filePath)) {
-                storageFlags = StorageManager.FLAG_STORAGE_DE;
-            } else if (appInfo.credentialProtectedDataDir != null
-                    && FileUtils.contains(appInfo.credentialProtectedDataDir, filePath)) {
+
+            if (fileIsUnder(filePath, appInfo.credentialProtectedDataDir)) {
                 storageFlags = StorageManager.FLAG_STORAGE_CE;
+            } else if (fileIsUnder(filePath, appInfo.deviceProtectedDataDir)) {
+                storageFlags = StorageManager.FLAG_STORAGE_DE;
             } else {
                 Slog.e(TAG, "Could not infer CE/DE storage for path " + filePath);
                 needWrite |= mPackageDynamicCodeLoading.removeFile(packageName, filePath, userId);
@@ -139,6 +145,9 @@
                         + ": " + e.getMessage());
             }
 
+            String subtag = fileInfo.mFileType == FILE_TYPE_DEX
+                    ? DCL_DEX_SUBTAG
+                    : DCL_NATIVE_SUBTAG;
             String fileName = new File(filePath).getName();
             String message = PackageUtils.computeSha256Digest(fileName.getBytes());
 
@@ -165,7 +174,7 @@
                 }
 
                 if (loadingUid != -1) {
-                    writeDclEvent(loadingUid, message);
+                    writeDclEvent(subtag, loadingUid, message);
                 }
             }
         }
@@ -175,21 +184,58 @@
         }
     }
 
+    private boolean fileIsUnder(String filePath, String directoryPath) {
+        if (directoryPath == null) {
+            return false;
+        }
+
+        try {
+            return FileUtils.contains(new File(directoryPath).getCanonicalPath(),
+                    new File(filePath).getCanonicalPath());
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
     @VisibleForTesting
     PackageDynamicCode getPackageDynamicCodeInfo(String packageName) {
         return mPackageDynamicCodeLoading.getPackageDynamicCodeInfo(packageName);
     }
 
     @VisibleForTesting
-    void writeDclEvent(int uid, String message) {
-        EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, uid, message);
+    void writeDclEvent(String subtag, int uid, String message) {
+        EventLog.writeEvent(SNET_TAG, subtag, uid, message);
     }
 
-    void record(int loaderUserId, String dexPath,
-            String owningPackageName, String loadingPackageName) {
+    void recordDex(int loaderUserId, String dexPath, String owningPackageName,
+            String loadingPackageName) {
         if (mPackageDynamicCodeLoading.record(owningPackageName, dexPath,
-                PackageDynamicCodeLoading.FILE_TYPE_DEX, loaderUserId,
-                loadingPackageName)) {
+                FILE_TYPE_DEX, loaderUserId, loadingPackageName)) {
+            mPackageDynamicCodeLoading.maybeWriteAsync();
+        }
+    }
+
+    /**
+     * Record that an app running in the specified uid has executed native code from the file at
+     * {@link path}.
+     */
+    public void recordNative(int loadingUid, String path) {
+        String[] packages;
+        try {
+            packages = mPackageManager.getPackagesForUid(loadingUid);
+            if (packages == null || packages.length == 0) {
+                return;
+            }
+        } catch (RemoteException e) {
+            // Can't happen, we're local.
+            return;
+        }
+
+        String loadingPackageName = packages[0];
+        int loadingUserId = UserHandle.getUserId(loadingUid);
+
+        if (mPackageDynamicCodeLoading.record(loadingPackageName, path,
+                FILE_TYPE_NATIVE, loadingUserId, loadingPackageName)) {
             mPackageDynamicCodeLoading.maybeWriteAsync();
         }
     }
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 b546836..1a2b115 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -235,7 +235,7 @@
                     continue;
                 }
 
-                mDexLogger.record(loaderUserId, dexPath, searchResult.mOwningPackageName,
+                mDexLogger.recordDex(loaderUserId, dexPath, searchResult.mOwningPackageName,
                         loadingAppInfo.packageName);
 
                 if (classLoaderContexts != null) {
diff --git a/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java b/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
index 6d4bc82..cc26c9b 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
@@ -53,6 +53,9 @@
     // is represented in the text file format.)
     static final int FILE_TYPE_DEX = 'D';
 
+    // Type code to indicate a secondary file containing native code.
+    static final int FILE_TYPE_NATIVE = 'N';
+
     private static final String TAG = "PackageDynamicCodeLoading";
 
     private static final String FILE_VERSION_HEADER = "DCL1";
@@ -107,7 +110,7 @@
      */
     boolean record(String owningPackageName, String filePath, int fileType, int ownerUserId,
             String loadingPackageName) {
-        if (fileType != FILE_TYPE_DEX) {
+        if (!isValidFileType(fileType)) {
             throw new IllegalArgumentException("Bad file type: " + fileType);
         }
         synchronized (mLock) {
@@ -120,6 +123,10 @@
         }
     }
 
+    private static boolean isValidFileType(int fileType) {
+        return fileType == FILE_TYPE_DEX || fileType == FILE_TYPE_NATIVE;
+    }
+
     /**
      * Return all packages that contain records of secondary dex files. (Note that data updates
      * asynchronously, so {@link #getPackageDynamicCodeInfo} may still return null if passed
@@ -407,7 +414,7 @@
             if (packages.length == 0) {
                 throw new IOException("Malformed line: " + line);
             }
-            if (type != FILE_TYPE_DEX) {
+            if (!isValidFileType(type)) {
                 throw new IOException("Unknown file type: " + line);
             }
 
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index d5af313..20d6d4e 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1290,6 +1290,7 @@
             return mContext.getPackageManager().getPackageInfo(pkg,
                     DEFAULT_PACKAGE_INFO_QUERY_FLAGS | extraFlags);
         } catch (NameNotFoundException e) {
+            Slog.e(TAG, "PackageNot found: " + pkg, e);
             return null;
         }
     }
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 9df0f72..085b4af 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -43,10 +43,14 @@
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.storage.StorageManager;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.LocalServices;
+import com.android.server.pm.Installer;
+import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.PackageManagerServiceUtils;
 
 import java.io.File;
@@ -56,12 +60,11 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 
 /**
  * Implementation of service that manages APK level rollbacks.
@@ -103,9 +106,14 @@
 
     private final Context mContext;
     private final HandlerThread mHandlerThread;
+    private final Installer mInstaller;
 
     RollbackManagerServiceImpl(Context context) {
         mContext = context;
+        // Note that we're calling onStart here because this object is only constructed on
+        // SystemService#onStart.
+        mInstaller = new Installer(mContext);
+        mInstaller.onStart();
         mHandlerThread = new HandlerThread("RollbackManagerServiceHandler");
         mHandlerThread.start();
 
@@ -120,8 +128,8 @@
         // expiration.
         getHandler().post(() -> ensureRollbackDataLoaded());
 
-        PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
-        installer.registerSessionCallback(new SessionCallback(), getHandler());
+        PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
+        packageInstaller.registerSessionCallback(new SessionCallback(), getHandler());
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
@@ -158,10 +166,13 @@
                             PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
                     int installFlags = intent.getIntExtra(
                             PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS, 0);
+                    int[] installedUsers = intent.getIntArrayExtra(
+                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALLED_USERS);
                     File newPackageCodePath = new File(intent.getData().getPath());
 
                     getHandler().post(() -> {
-                        boolean success = enableRollback(installFlags, newPackageCodePath);
+                        boolean success = enableRollback(installFlags, newPackageCodePath,
+                                installedUsers);
                         int ret = PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED;
                         if (!success) {
                             ret = PackageManagerInternal.ENABLE_ROLLBACK_FAILED;
@@ -356,28 +367,30 @@
                 parentSession.addChildSessionId(sessionId);
             }
 
-            final LocalIntentReceiver receiver = new LocalIntentReceiver();
+            final LocalIntentReceiver receiver = new LocalIntentReceiver(
+                    (Intent result) -> {
+                        int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                                PackageInstaller.STATUS_FAILURE);
+                        if (status != PackageInstaller.STATUS_SUCCESS) {
+                            sendFailure(statusReceiver, "Rollback downgrade install failed: "
+                                    + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE));
+                            return;
+                        }
+
+                        addRecentlyExecutedRollback(rollback);
+                        sendSuccess(statusReceiver);
+
+                        Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED,
+                                Uri.fromParts("package", targetPackageName,
+                                        Manifest.permission.MANAGE_ROLLBACKS));
+
+                        // TODO: This call emits the warning "Calling a method in the
+                        // system process without a qualified user". Fix that.
+                        mContext.sendBroadcast(broadcast);
+                    }
+            );
+
             parentSession.commit(receiver.getIntentSender());
-
-            Intent result = receiver.getResult();
-            int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                    PackageInstaller.STATUS_FAILURE);
-            if (status != PackageInstaller.STATUS_SUCCESS) {
-                sendFailure(statusReceiver, "Rollback downgrade install failed: "
-                        + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE));
-                return;
-            }
-
-            addRecentlyExecutedRollback(rollback);
-            sendSuccess(statusReceiver);
-
-            Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED,
-                    Uri.fromParts("package", targetPackageName,
-                        Manifest.permission.MANAGE_ROLLBACKS));
-
-            // TODO: This call emits the warning "Calling a method in the
-            // system process without a qualified user". Fix that.
-            mContext.sendBroadcast(broadcast);
         } catch (IOException e) {
             Log.e(TAG, "Unable to roll back " + targetPackageName, e);
             sendFailure(statusReceiver, "IOException: " + e.toString());
@@ -620,12 +633,13 @@
      * staged for install with rollback enabled. Called before the package has
      * been installed.
      *
-     * @param id the id of the enable rollback request
      * @param installFlags information about what is being installed.
      * @param newPackageCodePath path to the package about to be installed.
+     * @param installedUsers the set of users for which a given package is installed.
      * @return true if enabling the rollback succeeds, false otherwise.
      */
-    private boolean enableRollback(int installFlags, File newPackageCodePath) {
+    private boolean enableRollback(int installFlags, File newPackageCodePath,
+            int[] installedUsers) {
         if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
             Log.e(TAG, "Rollbacks not supported for instant app install");
             return false;
@@ -690,6 +704,25 @@
         PackageRollbackInfo.PackageVersion installedVersion =
                 new PackageRollbackInfo.PackageVersion(installedPackage.getLongVersionCode());
 
+        for (int user : installedUsers) {
+            final int storageFlags;
+            if (StorageManager.isFileEncryptedNativeOrEmulated()
+                    && !StorageManager.isUserKeyUnlocked(user)) {
+                // We've encountered a user that hasn't unlocked on a FBE device, so we can't copy
+                // across app user data until the user unlocks their device.
+                Log.e(TAG, "User: " + user + " isn't unlocked, skipping CE userdata backup.");
+                storageFlags = Installer.FLAG_STORAGE_DE;
+            } else {
+                storageFlags = Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE;
+            }
+
+            try {
+                mInstaller.snapshotAppData(packageName, user, storageFlags);
+            } catch (InstallerException ie) {
+                Log.e(TAG, "Unable to create app data snapshot for: " + packageName, ie);
+            }
+        }
+
         PackageRollbackInfo info = new PackageRollbackInfo(
                 packageName, newVersion, installedVersion);
 
@@ -722,33 +755,64 @@
         return true;
     }
 
-    // TODO: Don't copy this from PackageManagerShellCommand like this?
-    private static class LocalIntentReceiver {
-        private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
+    @Override
+    public void restoreUserData(String packageName, int userId, int appId, long ceDataInode,
+            String seInfo, int token) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("restoureUserData may only be called by the system.");
+        }
+
+        getHandler().post(() -> {
+            PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+            // TODO(narayan): Should we make sure we're in the middle of a session commit for a
+            // a package with this package name ? Otherwise it's possible we may roll back data
+            // for some other downgrade.
+            if (getRollbackForPackage(packageName) == null) {
+                pmi.finishPackageInstall(token, false);
+                return;
+            }
+
+            final int storageFlags;
+            if (StorageManager.isFileEncryptedNativeOrEmulated()
+                    && !StorageManager.isUserKeyUnlocked(userId)) {
+                // We've encountered a user that hasn't unlocked on a FBE device, so we can't copy
+                // across app user data until the user unlocks their device.
+                Log.e(TAG, "User: " + userId + " isn't unlocked, skipping CE userdata restore.");
+
+                storageFlags = Installer.FLAG_STORAGE_DE;
+            } else {
+                storageFlags = Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE;
+            }
+
+            try {
+                mInstaller.restoreAppDataSnapshot(packageName, appId, ceDataInode,
+                        seInfo, userId, storageFlags);
+            } catch (InstallerException ie) {
+                Log.e(TAG, "Unable to restore app data snapshot: " + packageName, ie);
+            }
+
+            pmi.finishPackageInstall(token, false);
+        });
+    }
+
+    private class LocalIntentReceiver {
+        final Consumer<Intent> mConsumer;
+
+        LocalIntentReceiver(Consumer<Intent> consumer) {
+            mConsumer = consumer;
+        }
 
         private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
             @Override
             public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
                     IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
-                try {
-                    mResult.offer(intent, 5, TimeUnit.SECONDS);
-                } catch (InterruptedException e) {
-                    throw new RuntimeException(e);
-                }
+                getHandler().post(() -> mConsumer.accept(intent));
             }
         };
 
         public IntentSender getIntentSender() {
             return new IntentSender((IIntentSender) mLocalSender);
         }
-
-        public Intent getResult() {
-            try {
-                return mResult.take();
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
-        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index f9a838f..eb06bb2 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -178,6 +178,8 @@
      * rollback.
      */
     void deleteAvailableRollback(RollbackData data) {
+        // TODO(narayan): Make sure we delete the userdata snapshot along with the backup of the
+        // actual app.
         removeFile(data.backupDir);
     }
 
diff --git a/services/core/java/com/android/server/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
new file mode 100644
index 0000000..23c042a
--- /dev/null
+++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.testharness;
+
+import android.annotation.Nullable;
+import android.app.KeyguardManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.BatteryManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+import com.android.server.PersistentDataBlockManagerInternal;
+import com.android.server.SystemService;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.Set;
+
+/**
+ * Manages the Test Harness Mode service for setting up test harness mode on the device.
+ *
+ * <p>Test Harness Mode is a feature that allows the user to clean their device, retain ADB keys,
+ * and provision the device for Instrumentation testing. This means that all parts of the device
+ * that would otherwise interfere with testing (auto-syncing accounts, package verification,
+ * automatic updates, etc.) are all disabled by default but may be re-enabled by the user.
+ */
+public class TestHarnessModeService extends SystemService {
+    private static final String TAG = TestHarnessModeService.class.getSimpleName();
+    private static final String TEST_HARNESS_MODE_PROPERTY = "persist.sys.test_harness";
+
+    private PersistentDataBlockManagerInternal mPersistentDataBlockManagerInternal;
+
+    public TestHarnessModeService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService("testharness", mService);
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        switch (phase) {
+            case PHASE_SYSTEM_SERVICES_READY:
+                setUpTestHarnessMode();
+                break;
+            case PHASE_BOOT_COMPLETED:
+                disableAutoSync();
+                break;
+        }
+        super.onBootPhase(phase);
+    }
+
+    private void setUpTestHarnessMode() {
+        Slog.d(TAG, "Setting up test harness mode");
+        byte[] testHarnessModeData = getPersistentDataBlock().getTestHarnessModeData();
+        if (testHarnessModeData == null || testHarnessModeData.length == 0) {
+            // There's no data to apply, so leave it as-is.
+            return;
+        }
+        PersistentData persistentData = PersistentData.fromBytes(testHarnessModeData);
+
+        SystemProperties.set(TEST_HARNESS_MODE_PROPERTY, persistentData.mEnabled ? "1" : "0");
+        writeAdbKeysFile(persistentData);
+        // Clear out the data block so that we don't revert the ADB keys on every boot.
+        getPersistentDataBlock().clearTestHarnessModeData();
+
+        ContentResolver cr = getContext().getContentResolver();
+        if (Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) == 0) {
+            // Enable ADB
+            Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 1);
+        } else {
+            // ADB is already enabled, we should restart the service so it picks up the new keys
+            android.os.SystemService.restart("adbd");
+        }
+
+        Settings.Global.putInt(cr, Settings.Global.PACKAGE_VERIFIER_ENABLE, 0);
+        Settings.Global.putInt(
+                cr,
+                Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
+                BatteryManager.BATTERY_PLUGGED_ANY);
+        Settings.Global.putInt(cr, Settings.Global.OTA_DISABLE_AUTOMATIC_UPDATE, 1);
+        Settings.Global.putInt(cr, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
+
+        setDeviceProvisioned();
+    }
+
+    private void disableAutoSync() {
+        UserInfo primaryUser = UserManager.get(getContext()).getPrimaryUser();
+        ContentResolver
+            .setMasterSyncAutomaticallyAsUser(false, primaryUser.getUserHandle().getIdentifier());
+    }
+
+    private void writeAdbKeysFile(PersistentData persistentData) {
+        Path adbKeys = Paths.get("/data/misc/adb/adb_keys");
+        try {
+            OutputStream fileOutputStream = Files.newOutputStream(adbKeys);
+            fileOutputStream.write(persistentData.mAdbKeys);
+            fileOutputStream.close();
+
+            Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(adbKeys);
+            permissions.add(PosixFilePermission.GROUP_READ);
+            Files.setPosixFilePermissions(adbKeys, permissions);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to set up adb keys", e);
+            // Note: if a device enters this block, it will remain UNAUTHORIZED in ADB, but all
+            // other settings will be set up.
+        }
+    }
+
+    // Setting the device as provisioned skips the setup wizard.
+    private void setDeviceProvisioned() {
+        ContentResolver cr = getContext().getContentResolver();
+        Settings.Global.putInt(cr, Settings.Global.DEVICE_PROVISIONED, 1);
+        Settings.Secure.putIntForUser(
+                cr,
+                Settings.Secure.USER_SETUP_COMPLETE,
+                1,
+                UserHandle.USER_CURRENT);
+    }
+
+    @Nullable
+    private PersistentDataBlockManagerInternal getPersistentDataBlock() {
+        if (mPersistentDataBlockManagerInternal == null) {
+            Slog.d(TAG, "Getting PersistentDataBlockManagerInternal from LocalServices");
+            mPersistentDataBlockManagerInternal =
+                    LocalServices.getService(PersistentDataBlockManagerInternal.class);
+        }
+        return mPersistentDataBlockManagerInternal;
+    }
+
+    private final IBinder mService = new Binder() {
+        @Override
+        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+                String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+            (new TestHarnessModeShellCommand())
+                .exec(this, in, out, err, args, callback, resultReceiver);
+        }
+    };
+
+    private class TestHarnessModeShellCommand extends ShellCommand {
+        @Override
+        public int onCommand(String cmd) {
+            switch (cmd) {
+                case "enable":
+                case "restore":
+                    checkPermissions();
+                    final long originalId = Binder.clearCallingIdentity();
+                    try {
+                        if (isDeviceSecure()) {
+                            getErrPrintWriter().println(
+                                    "Test Harness Mode cannot be enabled if there is a lock "
+                                            + "screen");
+                            return 2;
+                        }
+                        return handleEnable();
+                    } finally {
+                        Binder.restoreCallingIdentity(originalId);
+                    }
+                default:
+                    return handleDefaultCommands(cmd);
+            }
+        }
+
+        private void checkPermissions() {
+            getContext().enforceCallingPermission(
+                    android.Manifest.permission.ENABLE_TEST_HARNESS_MODE,
+                    "You must hold android.permission.ENABLE_TEST_HARNESS_MODE "
+                            + "to enable Test Harness Mode");
+        }
+
+        private boolean isDeviceSecure() {
+            UserInfo primaryUser = UserManager.get(getContext()).getPrimaryUser();
+            KeyguardManager keyguardManager = getContext().getSystemService(KeyguardManager.class);
+            return keyguardManager.isDeviceSecure(primaryUser.id);
+        }
+
+        private int handleEnable() {
+            Path adbKeys = Paths.get("/data/misc/adb/adb_keys");
+            if (!Files.exists(adbKeys)) {
+                // This should only be accessible on eng builds that haven't yet set up ADB keys
+                getErrPrintWriter()
+                    .println("No ADB keys stored; not enabling test harness mode");
+                return 1;
+            }
+
+            try (InputStream inputStream = Files.newInputStream(adbKeys)) {
+                long size = Files.size(adbKeys);
+                byte[] adbKeysBytes = new byte[(int) size];
+                int numBytes = inputStream.read(adbKeysBytes);
+                if (numBytes != size) {
+                    getErrPrintWriter().println("Failed to read all bytes of adb_keys");
+                    return 1;
+                }
+                PersistentData persistentData = new PersistentData(true, adbKeysBytes);
+                getPersistentDataBlock().setTestHarnessModeData(persistentData.toBytes());
+            } catch (IOException e) {
+                Slog.e(TAG, "Failed to store ADB keys.", e);
+                getErrPrintWriter().println("Failed to enable Test Harness Mode");
+                return 1;
+            }
+
+            Intent i = new Intent(Intent.ACTION_FACTORY_RESET);
+            i.setPackage("android");
+            i.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            i.putExtra(Intent.EXTRA_REASON, TAG);
+            i.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, true);
+            getContext().sendBroadcastAsUser(i, UserHandle.SYSTEM);
+            return 0;
+        }
+
+        @Override
+        public void onHelp() {
+            PrintWriter pw = getOutPrintWriter();
+            pw.println("About:");
+            pw.println("  Test Harness Mode is a mode that the device can be placed in to prepare");
+            pw.println("  the device for running UI tests. The device is placed into this mode by");
+            pw.println("  first wiping all data from the device, preserving ADB keys.");
+            pw.println();
+            pw.println("  By default, the following settings are configured:");
+            pw.println("    * Package Verifier is disabled");
+            pw.println("    * Stay Awake While Charging is enabled");
+            pw.println("    * OTA Updates are disabled");
+            pw.println("    * Auto-Sync for accounts is disabled");
+            pw.println();
+            pw.println("  Other apps may configure themselves differently in Test Harness Mode by");
+            pw.println("  checking ActivityManager.isRunningInUserTestHarness()");
+            pw.println();
+            pw.println("Test Harness Mode commands:");
+            pw.println("  help");
+            pw.println("    Print this help text.");
+            pw.println();
+            pw.println("  enable|restore");
+            pw.println("    Erase all data from this device and enable Test Harness Mode,");
+            pw.println("    preserving the stored ADB keys currently on the device and toggling");
+            pw.println("    settings in a way that are conducive to Instrumentation testing.");
+        }
+    }
+
+    /**
+     * The object that will serialize/deserialize the Test Harness Mode data to and from the
+     * persistent data block.
+     */
+    public static class PersistentData {
+        static final byte VERSION_1 = 1;
+
+        final int mVersion;
+        final boolean mEnabled;
+        final byte[] mAdbKeys;
+
+        PersistentData(boolean enabled, byte[] adbKeys) {
+            this(VERSION_1, enabled, adbKeys);
+        }
+
+        PersistentData(int version, boolean enabled, byte[] adbKeys) {
+            this.mVersion = version;
+            this.mEnabled = enabled;
+            this.mAdbKeys = adbKeys;
+        }
+
+        static PersistentData fromBytes(byte[] bytes) {
+            try {
+                DataInputStream is = new DataInputStream(new ByteArrayInputStream(bytes));
+                int version = is.readInt();
+                boolean enabled = is.readBoolean();
+                int adbKeysLength = is.readInt();
+                byte[] adbKeys = new byte[adbKeysLength];
+                is.readFully(adbKeys);
+                return new PersistentData(version, enabled, adbKeys);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        byte[] toBytes() {
+            try {
+                ByteArrayOutputStream os = new ByteArrayOutputStream();
+                DataOutputStream dos = new DataOutputStream(os);
+                dos.writeInt(VERSION_1);
+                dos.writeBoolean(mEnabled);
+                dos.writeInt(mAdbKeys.length);
+                dos.write(mAdbKeys);
+                dos.close();
+                return os.toByteArray();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index b3eafa4..45689ce 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -65,7 +65,6 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 import com.android.server.SystemService;
-import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.utils.ManagedApplicationService;
 import com.android.server.utils.ManagedApplicationService.BinderChecker;
 import com.android.server.utils.ManagedApplicationService.LogEvent;
@@ -623,14 +622,6 @@
         }
 
         @Override
-        public void setVrInputMethod(ComponentName componentName) {
-            enforceCallerPermissionAnyOf(Manifest.permission.RESTRICTED_VR_ACCESS);
-            InputMethodManagerInternal imm =
-                    LocalServices.getService(InputMethodManagerInternal.class);
-            imm.startVrInputMethodNoCheck(componentName);
-        }
-
-        @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index e817dd4..65d66f4 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -558,26 +558,22 @@
     }
 
     /**
-     * Pause all activities in either all of the stacks or just the back stacks. This is done before
-     * resuming a new activity and to make sure that previously active activities are
-     * paused in stacks that are no longer visible or in pinned windowing mode. This does not
-     * pause activities in visible stacks, so if an activity is launched within the same stack/task,
-     * then we should explicitly pause that stack's top activity.
+     * Pause all activities in either all of the stacks or just the back stacks.
      * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
      * @param resuming The resuming activity.
      * @param dontWait The resuming activity isn't going to wait for all activities to be paused
      *                 before resuming.
-     * @return {@code true} if any activity was paused as a result of this call.
+     * @return true if any activity was paused as a result of this call.
      */
     boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
         boolean someActivityPaused = false;
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             final ActivityStack stack = mStacks.get(stackNdx);
-            final ActivityRecord resumedActivity = stack.getResumedActivity();
-            if (resumedActivity != null
-                    && (!stack.shouldBeVisible(resuming) || !stack.isFocusable())) {
+            // TODO(b/111541062): Check if resumed activity on this display instead
+            if (!mRootActivityContainer.isTopDisplayFocusedStack(stack)
+                    && stack.getResumedActivity() != null) {
                 if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
-                        " mResumedActivity=" + resumedActivity);
+                        " mResumedActivity=" + stack.getResumedActivity());
                 someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
                         dontWait);
             }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6213fa0..b8634d8 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1946,84 +1946,30 @@
         try {
             mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                     WindowVisibilityItem.obtain(true /* showWindow */));
-            makeActiveIfNeeded(null /* activeActivity*/);
+            if (shouldPauseWhenBecomingVisible()) {
+                // An activity must be in the {@link PAUSING} state for the system to validate
+                // the move to {@link PAUSED}.
+                setState(PAUSING, "makeVisibleIfNeeded");
+                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                        PauseActivityItem.obtain(finishing, false /* userLeaving */,
+                                configChangeFlags, false /* dontReport */));
+            }
         } catch (Exception e) {
             Slog.w(TAG, "Exception thrown sending visibility update: " + intent.getComponent(), e);
         }
     }
 
-    /**
-     * Make activity resumed or paused if needed.
-     * @param activeActivity an activity that is resumed or just completed pause action.
-     *                       We won't change the state of this activity.
-     */
-    boolean makeActiveIfNeeded(ActivityRecord activeActivity) {
-        if (shouldResumeActivity(activeActivity)) {
-            if (DEBUG_VISIBILITY) {
-                Slog.v("TAG_VISIBILITY", "Resume visible activity, " + this);
-            }
-            return getActivityStack().resumeTopActivityUncheckedLocked(activeActivity /* prev */,
-                    null /* options */);
-        } else if (shouldPauseActivity(activeActivity)) {
-            if (DEBUG_VISIBILITY) {
-                Slog.v("TAG_VISIBILITY", "Pause visible activity, " + this);
-            }
-            // An activity must be in the {@link PAUSING} state for the system to validate
-            // the move to {@link PAUSED}.
-            setState(PAUSING, "makeVisibleIfNeeded");
-            try {
-                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
-                        PauseActivityItem.obtain(finishing, false /* userLeaving */,
-                                configChangeFlags, false /* dontReport */));
-            } catch (Exception e) {
-                Slog.w(TAG, "Exception thrown sending pause: " + intent.getComponent(), e);
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Check if activity should be moved to PAUSED state. The activity:
-     * - should be eligible to be made active (see {@link #shouldMakeActive(ActivityRecord)})
-     * - should be non-focusable
-     * - should not be currently pausing or paused
-     * @param activeActivity the activity that is active or just completed pause action. We won't
-     *                       resume if this activity is active.
-     */
-    private boolean shouldPauseActivity(ActivityRecord activeActivity) {
-        return shouldMakeActive(activeActivity) && !isFocusable() && !isState(PAUSING, PAUSED);
-    }
-
-    /**
-     * Check if activity should be moved to RESUMED state. The activity:
-     * - should be eligible to be made active (see {@link #shouldMakeActive(ActivityRecord)})
-     * - should be focusable
-     * @param activeActivity the activity that is active or just completed pause action. We won't
-     *                       resume if this activity is active.
-     */
-    private boolean shouldResumeActivity(ActivityRecord activeActivity) {
-        return shouldMakeActive(activeActivity) && isFocusable() && !isState(RESUMED);
-    }
-
-    /**
-     * Check if activity is eligible to be made active (resumed of paused). The activity:
-     * - should be paused, stopped or stopping
-     * - should not be the currently active one
-     * - should be either the topmost in task, or right below the top activity that is finishing
-     * If all of these conditions are not met at the same time, the activity cannot be made active.
-     */
-    private boolean shouldMakeActive(ActivityRecord activeActivity) {
-        // If the activity is stopped, stopping, cycle to an active state. We avoid doing
+    /** Check if activity should be moved to PAUSED state when it becomes visible. */
+    private boolean shouldPauseWhenBecomingVisible() {
+        // If the activity is stopped or stopping, cycle to the paused state. We avoid doing
         // this when there is an activity waiting to become translucent as the extra binder
         // calls will lead to noticeable jank. A later call to
-        // ActivityStack#ensureActivitiesVisibleLocked will bring the activity to a proper
-        // active state.
-        if (!isState(RESUMED, PAUSED, STOPPED, STOPPING)
-                || getActivityStack().mTranslucentActivityWaiting != null) {
-            return false;
-        }
-
-        if (this == activeActivity) {
+        // ActivityStack#ensureActivitiesVisibleLocked will bring the activity to the proper
+        // paused state. We also avoid doing this for the activity the stack supervisor
+        // considers the resumed activity, as normal means will bring the activity from STOPPED
+        // to RESUMED. Adding PAUSING in this scenario will lead to double lifecycles.
+        if (!isState(STOPPED, STOPPING) || getActivityStack().mTranslucentActivityWaiting != null
+                || isResumedActivityOnDisplay()) {
             return false;
         }
 
@@ -2033,14 +1979,14 @@
             throw new IllegalStateException("Activity not found in its task");
         }
         if (positionInTask == task.mActivities.size() - 1) {
-            // It's the topmost activity in the task - should become resumed now
+            // It's the topmost activity in the task - should become paused now
             return true;
         }
         // Check if activity above is finishing now and this one becomes the topmost in task.
         final ActivityRecord activityAbove = task.mActivities.get(positionInTask + 1);
         if (activityAbove.finishing && results == null) {
-            // We will only allow making active if activity above wasn't launched for result.
-            // Otherwise it will cause this activity to resume before getting result.
+            // We will only allow pausing if activity above wasn't launched for result. Otherwise it
+            // will cause this activity to resume before getting result.
             return true;
         }
         return false;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 3aef8e1f..891c3da 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -357,11 +357,6 @@
      */
     boolean mForceHidden = false;
 
-    /**
-     * Used to keep resumeTopActivityUncheckedLocked() from being entered recursively
-     */
-    boolean mInResumeTopActivity = false;
-
     private boolean mUpdateBoundsDeferred;
     private boolean mUpdateBoundsDeferredCalled;
     private boolean mUpdateDisplayedBoundsDeferredCalled;
@@ -1737,7 +1732,6 @@
             "Activity paused: token=" + token + ", timeout=" + timeout);
 
         final ActivityRecord r = isInStackLocked(token);
-
         if (r != null) {
             mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
             if (mPausingActivity == r) {
@@ -2094,7 +2088,8 @@
             boolean aboveTop = top != null;
             final boolean stackShouldBeVisible = shouldBeVisible(starting);
             boolean behindFullscreenActivity = !stackShouldBeVisible;
-            boolean resumeNextActivity = isFocusable() && isInStackLocked(starting) == null;
+            boolean resumeNextActivity = mRootActivityContainer.isTopDisplayFocusedStack(this)
+                    && (isInStackLocked(starting) == null);
             final boolean isTopNotPinnedStack =
                     isAttached() && getDisplay().isTopNotPinnedStack(this);
             for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -2155,10 +2150,6 @@
                             if (r.handleAlreadyVisible()) {
                                 resumeNextActivity = false;
                             }
-
-                            if (notifyClients) {
-                                r.makeActiveIfNeeded(starting);
-                            }
                         } else {
                             r.makeVisibleIfNeeded(starting, notifyClients);
                         }
@@ -2336,7 +2327,7 @@
                 r.setVisible(true);
             }
             if (r != starting) {
-                mStackSupervisor.startSpecificActivityLocked(r, andResume, true /* checkConfig */);
+                mStackSupervisor.startSpecificActivityLocked(r, andResume, false);
                 return true;
             }
         }
@@ -2514,7 +2505,7 @@
      */
     @GuardedBy("mService")
     boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
-        if (mInResumeTopActivity) {
+        if (mStackSupervisor.inResumeTopActivity) {
             // Don't even start recursing.
             return false;
         }
@@ -2522,7 +2513,7 @@
         boolean result = false;
         try {
             // Protect against recursion.
-            mInResumeTopActivity = true;
+            mStackSupervisor.inResumeTopActivity = true;
             result = resumeTopActivityInnerLocked(prev, options);
 
             // When resuming the top activity, it may be necessary to pause the top activity (for
@@ -2537,7 +2528,7 @@
                 checkReadyForSleep();
             }
         } finally {
-            mInResumeTopActivity = false;
+            mStackSupervisor.inResumeTopActivity = false;
         }
 
         return result;
@@ -2570,7 +2561,7 @@
         // Find the next top-most activity to resume in this stack that is not finishing and is
         // focusable. If it is not focusable, we will fall into the case below to resume the
         // top activity in the next focusable task.
-        ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
+        final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
 
         final boolean hasRunningActivity = next != null;
 
@@ -2658,12 +2649,6 @@
         if (!mRootActivityContainer.allPausedActivitiesComplete()) {
             if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
                     "resumeTopActivityLocked: Skip resume: some activity pausing.");
-
-            // Adding previous activity to the waiting visible list, or it would be stopped
-            // before top activity being visible.
-            if (prev != null && !next.nowVisible) {
-                mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(prev);
-            }
             return false;
         }
 
@@ -2873,9 +2858,7 @@
             // the screen based on the new activity order.
             boolean notUpdated = true;
 
-            // Activity should also be visible if set mLaunchTaskBehind to true (see
-            // ActivityRecord#shouldBeVisibleIgnoringKeyguard()).
-            if (shouldBeVisible(next)) {
+            if (isFocusedStackOnDisplay()) {
                 // We have special rotation behavior when here is some active activity that
                 // requests specific orientation or Keyguard is locked. Make sure all activity
                 // visibilities are set correctly as well as the transition is updated if needed
@@ -4104,12 +4087,6 @@
         mStackSupervisor.mFinishingActivities.add(r);
         r.resumeKeyDispatchingLocked();
         mRootActivityContainer.resumeFocusedStacksTopActivities();
-        // If activity was not paused at this point - explicitly pause it to start finishing
-        // process. Finishing will be completed once it reports pause back.
-        if (r.isState(RESUMED) && mPausingActivity != null) {
-            startPausingLocked(false /* userLeaving */, false /* uiSleeping */, next /* resuming */,
-                    false /* dontWait */);
-        }
         return r;
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a83ef34..3a288ca 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -327,6 +327,9 @@
      */
     PowerManager.WakeLock mGoingToSleep;
 
+    /** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */
+    boolean inResumeTopActivity;
+
     /**
      * Temporary rect used during docked stack resize calculation so we don't need to create a new
      * object each time.
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 43c1206..2807094 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -179,10 +179,7 @@
                 .setActivityOptions(options.toBundle())
                 .execute();
         mLastHomeActivityStartRecord = tmpOutRecord[0];
-        final ActivityDisplay display =
-                mService.mRootActivityContainer.getActivityDisplay(displayId);
-        final ActivityStack homeStack = display != null ? display.getHomeStack() : null;
-        if (homeStack != null && homeStack.mInResumeTopActivity) {
+        if (mSupervisor.inResumeTopActivity) {
             // If we are in resume section already, home activity will be initialized, but not
             // resumed (to avoid recursive resume) and will stay that way until something pokes it
             // again. We need to schedule another resume.
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 4e2dffc..d36e545 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1629,7 +1629,7 @@
                 // Also, we don't want to resume activities in a task that currently has an overlay
                 // as the starting activity just needs to be in the visible paused state until the
                 // over is removed.
-                mTargetStack.ensureActivitiesVisibleLocked(mStartActivity, 0, !PRESERVE_WINDOWS);
+                mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                 // Go ahead and tell window manager to execute app transition for this activity
                 // since the app transition will not be triggered through the resume channel.
                 mTargetStack.getDisplay().mDisplayContent.executeAppTransition();
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 9361e7f..750c5ca 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -2404,7 +2404,7 @@
     @Override
     protected void reparentSurfaceControl(Transaction t, SurfaceControl newParent) {
         if (!mSurfaceAnimator.hasLeash()) {
-            t.reparent(mSurfaceControl, newParent.getHandle());
+            t.reparent(mSurfaceControl, newParent);
         }
     }
 
@@ -2450,7 +2450,7 @@
             t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
 
             // Reparent leash to animation bounds layer.
-            t.reparent(leash, mAnimationBoundsLayer.getHandle());
+            t.reparent(leash, mAnimationBoundsLayer);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ac1159a..8fefd35 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4588,7 +4588,7 @@
      * Reparents the given surface to mOverlayLayer.
      */
     void reparentToOverlay(Transaction transaction, SurfaceControl surface) {
-        transaction.reparent(surface, mOverlayLayer.getHandle());
+        transaction.reparent(surface, mOverlayLayer);
     }
 
     void applyMagnificationSpec(MagnificationSpec spec) {
@@ -4831,11 +4831,11 @@
      * Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and
      * {@link #mOverlayLayer} to the specified surfaceControl.
      *
-     * @param surfaceControlHandle The handle for the new SurfaceControl, where the DisplayContent's
+     * @param surfaceControlHandle The new SurfaceControl, where the DisplayContent's
      *                             surfaces will be re-parented to.
      */
-    void reparentDisplayContent(IBinder surfaceControlHandle) {
-        mPendingTransaction.reparent(mWindowingLayer, surfaceControlHandle)
-                .reparent(mOverlayLayer, surfaceControlHandle);
+    void reparentDisplayContent(SurfaceControl sc) {
+        mPendingTransaction.reparent(mWindowingLayer, sc)
+                .reparent(mOverlayLayer, sc);
     }
 }
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index c4a853d..9b72141 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -1108,41 +1108,28 @@
             return false;
         }
 
-        boolean result = false;
         if (targetStack != null && (targetStack.isTopStackOnDisplay()
                 || getTopDisplayFocusedStack() == targetStack)) {
-            result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
+            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
         }
 
+        // Resume all top activities in focused stacks on all displays.
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            boolean resumedOnDisplay = false;
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord topRunningActivity = stack.topRunningActivityLocked();
-                if (!stack.isFocusableAndVisible() || topRunningActivity == null) {
-                    continue;
-                }
-                if (topRunningActivity.isState(RESUMED)) {
-                    // Kick off any lingering app transitions form the MoveTaskToFront operation.
-                    stack.executeAppTransition(targetOptions);
-                } else {
-                    resumedOnDisplay |= topRunningActivity.makeActiveIfNeeded(target);
-                }
+            final ActivityStack focusedStack = display.getFocusedStack();
+            if (focusedStack == null) {
+                continue;
             }
-            if (!resumedOnDisplay) {
-                // In cases when there are no valid activities (e.g. device just booted or launcher
-                // crashed) it's possible that nothing was resumed on a display. Requesting resume
-                // of top activity in focused stack explicitly will make sure that at least home
-                // activity is started and resumed, and no recursion occurs.
-                final ActivityStack focusedStack = display.getFocusedStack();
-                if (focusedStack != null) {
-                    focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);
-                }
+            final ActivityRecord r = focusedStack.topRunningActivityLocked();
+            if (r == null || !r.isState(RESUMED)) {
+                focusedStack.resumeTopActivityUncheckedLocked(null, null);
+            } else if (r.isState(RESUMED)) {
+                // Kick off any lingering app transitions form the MoveTaskToFront operation.
+                focusedStack.executeAppTransition(targetOptions);
             }
         }
 
-        return result;
+        return false;
     }
 
     void applySleepTokens(boolean applyToStacks) {
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 9d9b48a..1a8a911 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -199,7 +199,7 @@
      * @see #setLayer
      */
     void reparent(Transaction t, SurfaceControl newParent) {
-        t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent.getHandle());
+        t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent);
     }
 
     /**
@@ -228,8 +228,8 @@
 
         // Cancel source animation, but don't let animation runner cancel the animation.
         from.cancelAnimation(t, false /* restarting */, false /* forwardCancel */);
-        t.reparent(surface, mLeash.getHandle());
-        t.reparent(mLeash, parent.getHandle());
+        t.reparent(surface, mLeash);
+        t.reparent(mLeash, parent);
         mAnimatable.onAnimationLeashCreated(t, mLeash);
         mService.mAnimationTransferMap.put(mAnimation, this);
     }
@@ -275,7 +275,7 @@
         final boolean destroy = mLeash != null && surface != null && parent != null;
         if (destroy) {
             if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent");
-            t.reparent(surface, parent.getHandle());
+            t.reparent(surface, parent);
             scheduleAnim = true;
         }
         mService.mAnimationTransferMap.remove(mAnimation);
@@ -308,7 +308,7 @@
         if (!hidden) {
             t.show(leash);
         }
-        t.reparent(surface, leash.getHandle());
+        t.reparent(surface, leash);
         return leash;
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 0529ed1..69dcaf4 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -698,14 +698,6 @@
             return false;
         }
 
-        final boolean toTopOfStack = position == MAX_VALUE;
-        if (toTopOfStack && toStack.getResumedActivity() != null
-                && toStack.topRunningActivityLocked() != null) {
-            // Pause the resumed activity on the target stack while re-parenting task on top of it.
-            toStack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
-                    null /* resuming */, false /* pauseImmediately */);
-        }
-
         final int toStackWindowingMode = toStack.getWindowingMode();
         final ActivityRecord topActivity = getTopActivity();
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c8c834f..c6679a9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7334,7 +7334,7 @@
     }
 
     @Override
-    public void reparentDisplayContent(int displayId, IBinder surfaceControlHandle) {
+    public void reparentDisplayContent(int displayId, SurfaceControl sc) {
         final Display display = mDisplayManager.getDisplay(displayId);
         if (display == null) {
             throw new IllegalArgumentException(
@@ -7351,7 +7351,7 @@
             long token = Binder.clearCallingIdentity();
             try {
                 DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
-                displayContent.reparentDisplayContent(surfaceControlHandle);
+                displayContent.reparentDisplayContent(sc);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0977323..fb7e47d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4223,7 +4223,7 @@
 
     @Override
     public void setPasswordHistoryLength(ComponentName who, int length, boolean parent) {
-        if (!mHasFeature) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
@@ -4246,13 +4246,16 @@
 
     @Override
     public int getPasswordHistoryLength(ComponentName who, int userHandle, boolean parent) {
+        if (!mLockPatternUtils.hasSecureLockScreen()) {
+            return 0;
+        }
         return getStrictestPasswordRequirement(who, userHandle, parent,
                 admin -> admin.passwordHistoryLength, PASSWORD_QUALITY_UNSPECIFIED);
     }
 
     @Override
     public void setPasswordExpirationTimeout(ComponentName who, long timeout, boolean parent) {
-        if (!mHasFeature) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
@@ -4288,7 +4291,7 @@
      */
     @Override
     public long getPasswordExpirationTimeout(ComponentName who, int userHandle, boolean parent) {
-        if (!mHasFeature) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return 0L;
         }
         enforceFullCrossUsersPermission(userHandle);
@@ -4423,7 +4426,7 @@
 
     @Override
     public long getPasswordExpiration(ComponentName who, int userHandle, boolean parent) {
-        if (!mHasFeature) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return 0L;
         }
         enforceFullCrossUsersPermission(userHandle);
@@ -4770,6 +4773,9 @@
 
     @Override
     public int getCurrentFailedPasswordAttempts(int userHandle, boolean parent) {
+        if (!mLockPatternUtils.hasSecureLockScreen()) {
+            return 0;
+        }
         enforceFullCrossUsersPermission(userHandle);
         synchronized (getLockObject()) {
             if (!isCallerWithSystemUid()) {
@@ -4789,7 +4795,7 @@
 
     @Override
     public void setMaximumFailedPasswordsForWipe(ComponentName who, int num, boolean parent) {
-        if (!mHasFeature) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
@@ -4815,7 +4821,7 @@
 
     @Override
     public int getMaximumFailedPasswordsForWipe(ComponentName who, int userHandle, boolean parent) {
-        if (!mHasFeature) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return 0;
         }
         enforceFullCrossUsersPermission(userHandle);
@@ -4829,7 +4835,7 @@
 
     @Override
     public int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent) {
-        if (!mHasFeature) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return UserHandle.USER_NULL;
         }
         enforceFullCrossUsersPermission(userHandle);
@@ -4910,6 +4916,11 @@
     }
     @Override
     public boolean resetPassword(String passwordOrNull, int flags) throws RemoteException {
+        if (!mLockPatternUtils.hasSecureLockScreen()) {
+            Slog.w(LOG_TAG, "Cannot reset password when the device has no lock screen");
+            return false;
+        }
+
         final int callingUid = mInjector.binderGetCallingUid();
         final int userHandle = mInjector.userHandleGetCallingUserId();
 
@@ -5252,7 +5263,7 @@
     @Override
     public void setRequiredStrongAuthTimeout(ComponentName who, long timeoutMs,
             boolean parent) {
-        if (!mHasFeature) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
@@ -5285,7 +5296,7 @@
      */
     @Override
     public long getRequiredStrongAuthTimeout(ComponentName who, int userId, boolean parent) {
-        if (!mHasFeature) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
         }
         enforceFullCrossUsersPermission(userId);
@@ -6494,7 +6505,7 @@
      */
     @Override
     public void setActivePasswordState(PasswordMetrics metrics, int userHandle) {
-        if (!mHasFeature) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return;
         }
         enforceFullCrossUsersPermission(userHandle);
@@ -6514,7 +6525,7 @@
 
     @Override
     public void reportPasswordChanged(@UserIdInt int userId) {
-        if (!mHasFeature) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return;
         }
         enforceFullCrossUsersPermission(userId);
@@ -8800,7 +8811,7 @@
     @Override
     public void setTrustAgentConfiguration(ComponentName admin, ComponentName agent,
             PersistableBundle args, boolean parent) {
-        if (!mHasFeature) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return;
         }
         Preconditions.checkNotNull(admin, "admin is null");
@@ -8817,7 +8828,7 @@
     @Override
     public List<PersistableBundle> getTrustAgentConfiguration(ComponentName admin,
             ComponentName agent, int userHandle, boolean parent) {
-        if (!mHasFeature) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return null;
         }
         Preconditions.checkNotNull(agent, "agent null");
@@ -13215,7 +13226,7 @@
 
     @Override
     public boolean setResetPasswordToken(ComponentName admin, byte[] token) {
-        if (!mHasFeature) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return false;
         }
         if (token == null || token.length < 32) {
@@ -13243,7 +13254,7 @@
 
     @Override
     public boolean clearResetPasswordToken(ComponentName admin) {
-        if (!mHasFeature) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return false;
         }
         synchronized (getLockObject()) {
@@ -13269,6 +13280,9 @@
 
     @Override
     public boolean isResetPasswordTokenActive(ComponentName admin) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
+            return false;
+        }
         synchronized (getLockObject()) {
             final int userHandle = mInjector.userHandleGetCallingUserId();
             getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -13290,6 +13304,9 @@
     @Override
     public boolean resetPasswordWithToken(ComponentName admin, String passwordOrNull, byte[] token,
             int flags) {
+        if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
+            return false;
+        }
         Preconditions.checkNotNull(token);
         synchronized (getLockObject()) {
             final int userHandle = mInjector.userHandleGetCallingUserId();
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
index 32513c1..e99dd4f 100644
--- a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
@@ -21,9 +21,12 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
+import android.database.sqlite.SQLiteCursor;
+import android.database.sqlite.SQLiteCursorDriver;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteException;
 import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQuery;
 import android.net.NetworkUtils;
 import android.net.ipmemorystore.NetworkAttributes;
 import android.net.ipmemorystore.Status;
@@ -35,6 +38,7 @@
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.StringJoiner;
 
 /**
  * Encapsulating class for using the SQLite database backing the memory store.
@@ -46,6 +50,9 @@
  */
 public class IpMemoryStoreDatabase {
     private static final String TAG = IpMemoryStoreDatabase.class.getSimpleName();
+    // A pair of NetworkAttributes objects is group-close if the confidence that they are
+    // the same is above this cutoff. See NetworkAttributes and SameL3NetworkResponse.
+    private static final float GROUPCLOSE_CONFIDENCE = 0.5f;
 
     /**
      * Contract class for the Network Attributes table.
@@ -187,30 +194,35 @@
         return addresses;
     }
 
+    @NonNull
+    private static ContentValues toContentValues(@Nullable final NetworkAttributes attributes) {
+        final ContentValues values = new ContentValues();
+        if (null == attributes) return values;
+        if (null != attributes.assignedV4Address) {
+            values.put(NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS,
+                    NetworkUtils.inet4AddressToIntHTH(attributes.assignedV4Address));
+        }
+        if (null != attributes.groupHint) {
+            values.put(NetworkAttributesContract.COLNAME_GROUPHINT, attributes.groupHint);
+        }
+        if (null != attributes.dnsAddresses) {
+            values.put(NetworkAttributesContract.COLNAME_DNSADDRESSES,
+                    encodeAddressList(attributes.dnsAddresses));
+        }
+        if (null != attributes.mtu) {
+            values.put(NetworkAttributesContract.COLNAME_MTU, attributes.mtu);
+        }
+        return values;
+    }
+
     // Convert a NetworkAttributes object to content values to store them in a table compliant
     // with the contract defined in NetworkAttributesContract.
     @NonNull
     private static ContentValues toContentValues(@NonNull final String key,
             @Nullable final NetworkAttributes attributes, final long expiry) {
-        final ContentValues values = new ContentValues();
+        final ContentValues values = toContentValues(attributes);
         values.put(NetworkAttributesContract.COLNAME_L2KEY, key);
         values.put(NetworkAttributesContract.COLNAME_EXPIRYDATE, expiry);
-        if (null != attributes) {
-            if (null != attributes.assignedV4Address) {
-                values.put(NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS,
-                        NetworkUtils.inet4AddressToIntHTH(attributes.assignedV4Address));
-            }
-            if (null != attributes.groupHint) {
-                values.put(NetworkAttributesContract.COLNAME_GROUPHINT, attributes.groupHint);
-            }
-            if (null != attributes.dnsAddresses) {
-                values.put(NetworkAttributesContract.COLNAME_DNSADDRESSES,
-                        encodeAddressList(attributes.dnsAddresses));
-            }
-            if (null != attributes.mtu) {
-                values.put(NetworkAttributesContract.COLNAME_MTU, attributes.mtu);
-            }
-        }
         return values;
     }
 
@@ -228,6 +240,32 @@
         return values;
     }
 
+    @Nullable
+    private static NetworkAttributes readNetworkAttributesLine(@NonNull final Cursor cursor) {
+        // Make sure the data hasn't expired
+        final long expiry = getLong(cursor, NetworkAttributesContract.COLNAME_EXPIRYDATE, -1L);
+        if (expiry < System.currentTimeMillis()) return null;
+
+        final NetworkAttributes.Builder builder = new NetworkAttributes.Builder();
+        final int assignedV4AddressInt = getInt(cursor,
+                NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS, 0);
+        final String groupHint = getString(cursor, NetworkAttributesContract.COLNAME_GROUPHINT);
+        final byte[] dnsAddressesBlob =
+                getBlob(cursor, NetworkAttributesContract.COLNAME_DNSADDRESSES);
+        final int mtu = getInt(cursor, NetworkAttributesContract.COLNAME_MTU, -1);
+        if (0 != assignedV4AddressInt) {
+            builder.setAssignedV4Address(NetworkUtils.intToInet4AddressHTH(assignedV4AddressInt));
+        }
+        builder.setGroupHint(groupHint);
+        if (null != dnsAddressesBlob) {
+            builder.setDnsAddresses(decodeAddressList(dnsAddressesBlob));
+        }
+        if (mtu >= 0) {
+            builder.setMtu(mtu);
+        }
+        return builder.build();
+    }
+
     private static final String[] EXPIRY_COLUMN = new String[] {
         NetworkAttributesContract.COLNAME_EXPIRYDATE
     };
@@ -313,32 +351,9 @@
         // result here. 0 results means the key was not found.
         if (cursor.getCount() != 1) return null;
         cursor.moveToFirst();
-
-        // Make sure the data hasn't expired
-        final long expiry = cursor.getLong(
-                cursor.getColumnIndexOrThrow(NetworkAttributesContract.COLNAME_EXPIRYDATE));
-        if (expiry < System.currentTimeMillis()) return null;
-
-        final NetworkAttributes.Builder builder = new NetworkAttributes.Builder();
-        final int assignedV4AddressInt = getInt(cursor,
-                NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS, 0);
-        final String groupHint = getString(cursor, NetworkAttributesContract.COLNAME_GROUPHINT);
-        final byte[] dnsAddressesBlob =
-                getBlob(cursor, NetworkAttributesContract.COLNAME_DNSADDRESSES);
-        final int mtu = getInt(cursor, NetworkAttributesContract.COLNAME_MTU, -1);
+        final NetworkAttributes attributes = readNetworkAttributesLine(cursor);
         cursor.close();
-
-        if (0 != assignedV4AddressInt) {
-            builder.setAssignedV4Address(NetworkUtils.intToInet4AddressHTH(assignedV4AddressInt));
-        }
-        builder.setGroupHint(groupHint);
-        if (null != dnsAddressesBlob) {
-            builder.setDnsAddresses(decodeAddressList(dnsAddressesBlob));
-        }
-        if (mtu >= 0) {
-            builder.setMtu(mtu);
-        }
-        return builder.build();
+        return attributes;
     }
 
     private static final String[] DATA_COLUMN = new String[] {
@@ -365,17 +380,134 @@
         return result;
     }
 
+    /**
+     * The following is a horrible hack that is necessary because the Android SQLite API does not
+     * have a way to query a binary blob. This, almost certainly, is an overlook.
+     *
+     * The Android SQLite API has two family of methods : one for query that returns data, and
+     * one for more general SQL statements that can execute any statement but may not return
+     * anything. All the query methods, however, take only String[] for the arguments.
+     *
+     * In principle it is simple to write a function that will encode the binary blob in the
+     * way SQLite expects it. However, because the API forces the argument to be coerced into a
+     * String, the SQLiteQuery object generated by the default query methods will bind all
+     * arguments as Strings and SQL will *sanitize* them. This works okay for numeric types,
+     * but the format for blobs is x'<hex string>'. Note the presence of quotes, which will
+     * be sanitized, changing the contents of the field, and the query will fail to match the
+     * blob.
+     *
+     * As far as I can tell, there are two possible ways around this problem. The first one
+     * is to put the data in the query string and eschew it being an argument. This would
+     * require doing the sanitizing by hand. The other is to call bindBlob directly on the
+     * generated SQLiteQuery object, which not only is a lot less dangerous than rolling out
+     * sanitizing, but also will do the right thing if the underlying format ever changes.
+     *
+     * But none of the methods that take an SQLiteQuery object can return data ; this *must*
+     * be called with SQLiteDatabase#query. This object is not accessible from outside.
+     * However, there is a #query version that accepts a CursorFactory and this is pretty
+     * straightforward to implement as all the arguments are coming in and the SQLiteCursor
+     * class is public API.
+     * With this, it's possible to intercept the SQLiteQuery object, and assuming the args
+     * are available, to bind them directly and work around the API's oblivious coercion into
+     * Strings.
+     *
+     * This is really sad, but I don't see another way of having this work than this or the
+     * hand-rolled sanitizing, and this is the lesser evil.
+     */
+    private static class CustomCursorFactory implements SQLiteDatabase.CursorFactory {
+        @NonNull
+        private final ArrayList<Object> mArgs;
+        CustomCursorFactory(@NonNull final ArrayList<Object> args) {
+            mArgs = args;
+        }
+        @Override
+        public Cursor newCursor(final SQLiteDatabase db, final SQLiteCursorDriver masterQuery,
+                final String editTable,
+                final SQLiteQuery query) {
+            int index = 1; // bind is 1-indexed
+            for (final Object arg : mArgs) {
+                if (arg instanceof String) {
+                    query.bindString(index++, (String) arg);
+                } else if (arg instanceof Long) {
+                    query.bindLong(index++, (Long) arg);
+                } else if (arg instanceof Integer) {
+                    query.bindLong(index++, Long.valueOf((Integer) arg));
+                } else if (arg instanceof byte[]) {
+                    query.bindBlob(index++, (byte[]) arg);
+                } else {
+                    throw new IllegalStateException("Unsupported type CustomCursorFactory "
+                            + arg.getClass().toString());
+                }
+            }
+            return new SQLiteCursor(masterQuery, editTable, query);
+        }
+    }
+
+    // Returns the l2key of the closest match, if and only if it matches
+    // closely enough (as determined by group-closeness).
+    @Nullable
+    static String findClosestAttributes(@NonNull final SQLiteDatabase db,
+            @NonNull final NetworkAttributes attr) {
+        if (attr.isEmpty()) return null;
+        final ContentValues values = toContentValues(attr);
+
+        // Build the selection and args. To cut down on the number of lines to search, limit
+        // the search to those with at least one argument equals to the requested attributes.
+        // This works only because null attributes match only will not result in group-closeness.
+        final StringJoiner sj = new StringJoiner(" OR ");
+        final ArrayList<Object> args = new ArrayList<>();
+        args.add(System.currentTimeMillis());
+        for (final String field : values.keySet()) {
+            sj.add(field + " = ?");
+            args.add(values.get(field));
+        }
+
+        final String selection = NetworkAttributesContract.COLNAME_EXPIRYDATE + " > ? AND ("
+                + sj.toString() + ")";
+        final Cursor cursor = db.queryWithFactory(new CustomCursorFactory(args),
+                false, // distinct
+                NetworkAttributesContract.TABLENAME,
+                null, // columns, null means everything
+                selection, // selection
+                null, // selectionArgs, horrendously passed to the cursor factory instead
+                null, // groupBy
+                null, // having
+                null, // orderBy
+                null); // limit
+        if (cursor.getCount() <= 0) return null;
+        cursor.moveToFirst();
+        String bestKey = null;
+        float bestMatchConfidence = GROUPCLOSE_CONFIDENCE; // Never return a match worse than this.
+        while (!cursor.isAfterLast()) {
+            final NetworkAttributes read = readNetworkAttributesLine(cursor);
+            final float confidence = read.getNetworkGroupSamenessConfidence(attr);
+            if (confidence > bestMatchConfidence) {
+                bestKey = getString(cursor, NetworkAttributesContract.COLNAME_L2KEY);
+                bestMatchConfidence = confidence;
+            }
+            cursor.moveToNext();
+        }
+        cursor.close();
+        return bestKey;
+    }
+
     // Helper methods
-    static String getString(final Cursor cursor, final String columnName) {
+    private static String getString(final Cursor cursor, final String columnName) {
         final int columnIndex = cursor.getColumnIndex(columnName);
         return (columnIndex >= 0) ? cursor.getString(columnIndex) : null;
     }
-    static byte[] getBlob(final Cursor cursor, final String columnName) {
+    private static byte[] getBlob(final Cursor cursor, final String columnName) {
         final int columnIndex = cursor.getColumnIndex(columnName);
         return (columnIndex >= 0) ? cursor.getBlob(columnIndex) : null;
     }
-    static int getInt(final Cursor cursor, final String columnName, final int defaultValue) {
+    private static int getInt(final Cursor cursor, final String columnName,
+            final int defaultValue) {
         final int columnIndex = cursor.getColumnIndex(columnName);
         return (columnIndex >= 0) ? cursor.getInt(columnIndex) : defaultValue;
     }
+    private static long getLong(final Cursor cursor, final String columnName,
+            final long defaultValue) {
+        final int columnIndex = cursor.getColumnIndex(columnName);
+        return (columnIndex >= 0) ? cursor.getLong(columnIndex) : defaultValue;
+    }
 }
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
index 8b521f4..d43dc6a 100644
--- a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
@@ -250,9 +250,26 @@
      * Through the listener, returns the L2 key if one matched, or null.
      */
     @Override
-    public void findL2Key(@NonNull final NetworkAttributesParcelable attributes,
-            @NonNull final IOnL2KeyResponseListener listener) {
-        // TODO : implement this.
+    public void findL2Key(@Nullable final NetworkAttributesParcelable attributes,
+            @Nullable final IOnL2KeyResponseListener listener) {
+        if (null == listener) return;
+        mExecutor.execute(() -> {
+            try {
+                if (null == attributes) {
+                    listener.onL2KeyResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
+                    return;
+                }
+                if (null == mDb) {
+                    listener.onL2KeyResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
+                    return;
+                }
+                final String key = IpMemoryStoreDatabase.findClosestAttributes(mDb,
+                        new NetworkAttributes(attributes));
+                listener.onL2KeyResponse(makeStatus(SUCCESS), key);
+            } catch (final RemoteException e) {
+                // Client at the other end died
+            }
+        });
     }
 
     /**
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 98385c9..623990b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -136,6 +136,7 @@
 import com.android.server.statusbar.StatusBarManagerService;
 import com.android.server.storage.DeviceStorageMonitorService;
 import com.android.server.telecom.TelecomLoaderService;
+import com.android.server.testharness.TestHarnessModeService;
 import com.android.server.textclassifier.TextClassificationManagerService;
 import com.android.server.textservices.TextServicesManagerService;
 import com.android.server.trust.TrustManagerService;
@@ -1157,6 +1158,10 @@
                 traceBeginAndSlog("StartPersistentDataBlock");
                 mSystemServiceManager.startService(PersistentDataBlockService.class);
                 traceEnd();
+
+                traceBeginAndSlog("StartTestHarnessMode");
+                mSystemServiceManager.startService(TestHarnessModeService.class);
+                traceEnd();
             }
 
             if (hasPdb || OemLockService.isHalPresent()) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 1a16e56..53d72bb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -1376,6 +1376,45 @@
         verifyLightStateConditions(LIGHT_STATE_ACTIVE);
     }
 
+    @Test
+    public void testStepToIdleMode() {
+        float delta = mDeviceIdleController.MIN_PRE_IDLE_FACTOR_CHANGE;
+        for (int mode = PowerManager.PRE_IDLE_TIMEOUT_MODE_NORMAL;
+                mode <= PowerManager.PRE_IDLE_TIMEOUT_MODE_LONG;
+                mode++) {
+            int ret = mDeviceIdleController.setPreIdleTimeoutMode(mode);
+            if (mode == PowerManager.PRE_IDLE_TIMEOUT_MODE_NORMAL) {
+                assertEquals("setPreIdleTimeoutMode: " + mode + " failed.",
+                        mDeviceIdleController.SET_IDLE_FACTOR_RESULT_IGNORED, ret);
+            } else {
+                assertEquals("setPreIdleTimeoutMode: " + mode + " failed.",
+                        mDeviceIdleController.SET_IDLE_FACTOR_RESULT_OK, ret);
+            }
+            //TODO(b/123045185): Mocked Handler of DeviceIdleController to make message loop
+            //workable in this test class
+            mDeviceIdleController.updatePreIdleFactor();
+            float expectedfactor = mDeviceIdleController.getPreIdleTimeoutByMode(mode);
+            float curfactor = mDeviceIdleController.getPreIdleTimeoutFactor();
+            assertEquals("Pre idle time factor of mode [" + mode + "].",
+                    expectedfactor, curfactor, delta);
+            mDeviceIdleController.resetPreIdleTimeoutMode();
+            mDeviceIdleController.updatePreIdleFactor();
+
+            checkNextAlarmTimeWithNewPreIdleFactor(expectedfactor, STATE_INACTIVE);
+            checkNextAlarmTimeWithNewPreIdleFactor(expectedfactor, STATE_IDLE_PENDING);
+
+            checkNextAlarmTimeWithNewPreIdleFactor(expectedfactor, STATE_SENSING);
+            checkNextAlarmTimeWithNewPreIdleFactor(expectedfactor, STATE_LOCATING);
+            checkNextAlarmTimeWithNewPreIdleFactor(expectedfactor, STATE_QUICK_DOZE_DELAY);
+            checkNextAlarmTimeWithNewPreIdleFactor(expectedfactor, STATE_IDLE_MAINTENANCE);
+            checkNextAlarmTimeWithNewPreIdleFactor(expectedfactor, STATE_IDLE);
+            checkMaybeDoAnImmediateMaintenance(expectedfactor);
+        }
+        float curfactor = mDeviceIdleController.getPreIdleTimeoutFactor();
+        assertEquals("Pre idle time factor of mode default.",
+                1.0f, curfactor, delta);
+    }
+
     private void enterDeepState(int state) {
         switch (state) {
             case STATE_ACTIVE:
@@ -1599,4 +1638,84 @@
                 fail("Conditions for " + lightStateToString(expectedLightState) + " unknown.");
         }
     }
+
+    private void checkNextAlarmTimeWithNewPreIdleFactor(float factor, int state) {
+        final long errorTolerance = 1000;
+        enterDeepState(state);
+        long now = SystemClock.elapsedRealtime();
+        long alarm = mDeviceIdleController.getNextAlarmTime();
+        if (state == STATE_INACTIVE || state == STATE_IDLE_PENDING) {
+            int ret = mDeviceIdleController.setPreIdleTimeoutFactor(factor);
+            if (Float.compare(factor, 1.0f) == 0) {
+                assertEquals("setPreIdleTimeoutMode: " + factor + " failed.",
+                        mDeviceIdleController.SET_IDLE_FACTOR_RESULT_IGNORED, ret);
+            } else {
+                assertEquals("setPreIdleTimeoutMode: " + factor + " failed.",
+                        mDeviceIdleController.SET_IDLE_FACTOR_RESULT_OK, ret);
+            }
+            if (ret == mDeviceIdleController.SET_IDLE_FACTOR_RESULT_OK) {
+                mDeviceIdleController.updatePreIdleFactor();
+                long newAlarm = mDeviceIdleController.getNextAlarmTime();
+                long newDelay = (long) ((alarm - now) * factor);
+                assertTrue("setPreIdleTimeoutFactor: " + factor,
+                        Math.abs(newDelay - (newAlarm - now)) <  errorTolerance);
+                mDeviceIdleController.resetPreIdleTimeoutMode();
+                mDeviceIdleController.updatePreIdleFactor();
+                mDeviceIdleController.maybeDoImmediateMaintenance();
+                newAlarm = mDeviceIdleController.getNextAlarmTime();
+                assertTrue("resetPreIdleTimeoutMode from: " + factor,
+                        Math.abs(newAlarm - alarm) < errorTolerance);
+                mDeviceIdleController.setPreIdleTimeoutFactor(factor);
+                now = SystemClock.elapsedRealtime();
+                enterDeepState(state);
+                newAlarm = mDeviceIdleController.getNextAlarmTime();
+                assertTrue("setPreIdleTimeoutFactor: " + factor + " before step to idle",
+                        Math.abs(newDelay - (newAlarm - now)) <  errorTolerance);
+                mDeviceIdleController.resetPreIdleTimeoutMode();
+                mDeviceIdleController.updatePreIdleFactor();
+                mDeviceIdleController.maybeDoImmediateMaintenance();
+            }
+        } else {
+            mDeviceIdleController.setPreIdleTimeoutFactor(factor);
+            mDeviceIdleController.updatePreIdleFactor();
+            long newAlarm = mDeviceIdleController.getNextAlarmTime();
+            assertTrue("setPreIdleTimeoutFactor: " + factor
+                    + " shounld not change next alarm" ,
+                    (newAlarm == alarm));
+            mDeviceIdleController.resetPreIdleTimeoutMode();
+            mDeviceIdleController.updatePreIdleFactor();
+            mDeviceIdleController.maybeDoImmediateMaintenance();
+        }
+    }
+
+    private void checkMaybeDoAnImmediateMaintenance(float factor) {
+        int ret = mDeviceIdleController.setPreIdleTimeoutFactor(factor);
+        final long minuteInMillis = 60 * 1000;
+        if (Float.compare(factor, 1.0f) == 0) {
+            assertEquals("setPreIdleTimeoutMode: " + factor + " failed.",
+                    mDeviceIdleController.SET_IDLE_FACTOR_RESULT_IGNORED, ret);
+        } else {
+            assertEquals("setPreIdleTimeoutMode: " + factor + " failed.",
+                    mDeviceIdleController.SET_IDLE_FACTOR_RESULT_OK, ret);
+        }
+        if (ret == mDeviceIdleController.SET_IDLE_FACTOR_RESULT_OK) {
+            enterDeepState(STATE_IDLE);
+            long now = SystemClock.elapsedRealtime();
+            long alarm = mDeviceIdleController.getNextAlarmTime();
+            mDeviceIdleController.setIdleStartTimeForTest(
+                    now - (long) (mConstants.IDLE_TIMEOUT * 0.6));
+            mDeviceIdleController.maybeDoImmediateMaintenance();
+            long newAlarm = mDeviceIdleController.getNextAlarmTime();
+            assertTrue("maintenance not reschedule IDLE_TIMEOUT * 0.6",
+                    newAlarm == alarm);
+            mDeviceIdleController.setIdleStartTimeForTest(
+                    now - (long) (mConstants.IDLE_TIMEOUT * 1.2));
+            mDeviceIdleController.maybeDoImmediateMaintenance();
+            newAlarm = mDeviceIdleController.getNextAlarmTime();
+            assertTrue("maintenance not reschedule IDLE_TIMEOUT * 1.2",
+                    (newAlarm - now) < minuteInMillis);
+            mDeviceIdleController.resetPreIdleTimeoutMode();
+            mDeviceIdleController.updatePreIdleFactor();
+        }
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 57ee6dc..cad71a2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -25,6 +25,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
 import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
+import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
 import static com.android.server.job.JobSchedulerService.RARE_INDEX;
 import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
 
@@ -370,16 +371,19 @@
         mQuotaController.saveTimingSession(0, "com.android.test.stay", one);
 
         ExecutionStats expectedStats = new ExecutionStats();
-        expectedStats.invalidTimeElapsed = now + 24 * HOUR_IN_MILLIS;
+        expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
         expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
 
-        mQuotaController.onAppRemovedLocked("com.android.test.remove", 10001);
+        final int uid = 10001;
+        mQuotaController.onAppRemovedLocked("com.android.test.remove", uid);
         assertNull(mQuotaController.getTimingSessions(0, "com.android.test.remove"));
         assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test.stay"));
         assertEquals(expectedStats,
                 mQuotaController.getExecutionStatsLocked(0, "com.android.test.remove", RARE_INDEX));
         assertNotEquals(expectedStats,
                 mQuotaController.getExecutionStatsLocked(0, "com.android.test.stay", RARE_INDEX));
+
+        assertFalse(mQuotaController.getForegroundUids().get(uid));
     }
 
     @Test
@@ -405,7 +409,7 @@
         mQuotaController.saveTimingSession(10, "com.android.test", one);
 
         ExecutionStats expectedStats = new ExecutionStats();
-        expectedStats.invalidTimeElapsed = now + 24 * HOUR_IN_MILLIS;
+        expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
         expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
 
         mQuotaController.onUserRemovedLocked(0);
@@ -440,14 +444,14 @@
 
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 12 * HOUR_IN_MILLIS;
         // Invalid time is now +24 hours since there are no sessions at all for the app.
-        expectedStats.invalidTimeElapsed = now + 24 * HOUR_IN_MILLIS;
+        expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test.not.run", inputStats);
         assertEquals(expectedStats, inputStats);
 
         inputStats.windowSizeMs = expectedStats.windowSizeMs = MINUTE_IN_MILLIS;
         // Invalid time is now +18 hours since there are no sessions in the window but the earliest
         // session is 6 hours ago.
-        expectedStats.invalidTimeElapsed = now + 18 * HOUR_IN_MILLIS;
+        expectedStats.expirationTimeElapsed = now + 18 * HOUR_IN_MILLIS;
         expectedStats.executionTimeInWindowMs = 0;
         expectedStats.bgJobCountInWindow = 0;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -457,7 +461,7 @@
 
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 3 * MINUTE_IN_MILLIS;
         // Invalid time is now since the session straddles the window cutoff time.
-        expectedStats.invalidTimeElapsed = now;
+        expectedStats.expirationTimeElapsed = now;
         expectedStats.executionTimeInWindowMs = 2 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 3;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -468,7 +472,7 @@
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 5 * MINUTE_IN_MILLIS;
         // Invalid time is now since the start of the session is at the very edge of the window
         // cutoff time.
-        expectedStats.invalidTimeElapsed = now;
+        expectedStats.expirationTimeElapsed = now;
         expectedStats.executionTimeInWindowMs = 4 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 3;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -479,7 +483,7 @@
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 49 * MINUTE_IN_MILLIS;
         // Invalid time is now +44 minutes since the earliest session in the window is now-5
         // minutes.
-        expectedStats.invalidTimeElapsed = now + 44 * MINUTE_IN_MILLIS;
+        expectedStats.expirationTimeElapsed = now + 44 * MINUTE_IN_MILLIS;
         expectedStats.executionTimeInWindowMs = 4 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 3;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -489,7 +493,7 @@
 
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 50 * MINUTE_IN_MILLIS;
         // Invalid time is now since the session is at the very edge of the window cutoff time.
-        expectedStats.invalidTimeElapsed = now;
+        expectedStats.expirationTimeElapsed = now;
         expectedStats.executionTimeInWindowMs = 5 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 4;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -500,7 +504,7 @@
         inputStats.windowSizeMs = expectedStats.windowSizeMs = HOUR_IN_MILLIS;
         // Invalid time is now since the start of the session is at the very edge of the window
         // cutoff time.
-        expectedStats.invalidTimeElapsed = now;
+        expectedStats.expirationTimeElapsed = now;
         expectedStats.executionTimeInWindowMs = 6 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 5;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -510,7 +514,7 @@
 
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
         // Invalid time is now since the session straddles the window cutoff time.
-        expectedStats.invalidTimeElapsed = now;
+        expectedStats.expirationTimeElapsed = now;
         expectedStats.executionTimeInWindowMs = 11 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 10;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -523,7 +527,7 @@
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 3 * HOUR_IN_MILLIS;
         // Invalid time is now +59 minutes since the earliest session in the window is now-121
         // minutes.
-        expectedStats.invalidTimeElapsed = now + 59 * MINUTE_IN_MILLIS;
+        expectedStats.expirationTimeElapsed = now + 59 * MINUTE_IN_MILLIS;
         expectedStats.executionTimeInWindowMs = 12 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 10;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -536,7 +540,7 @@
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 6 * HOUR_IN_MILLIS;
         // Invalid time is now since the start of the session is at the very edge of the window
         // cutoff time.
-        expectedStats.invalidTimeElapsed = now;
+        expectedStats.expirationTimeElapsed = now;
         expectedStats.executionTimeInWindowMs = 22 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 15;
         expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -546,14 +550,14 @@
         mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
         assertEquals(expectedStats, inputStats);
 
-        // Make sure invalidTimeElapsed is set correctly when it's dependent on the max period.
+        // Make sure expirationTimeElapsed is set correctly when it's dependent on the max period.
         mQuotaController.getTimingSessions(0, "com.android.test")
                 .add(0,
                         createTimingSession(now - (23 * HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 3));
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
         // Invalid time is now +1 hour since the earliest session in the max period is 1 hour
         // before the end of the max period cutoff time.
-        expectedStats.invalidTimeElapsed = now + HOUR_IN_MILLIS;
+        expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
         expectedStats.executionTimeInWindowMs = 22 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 15;
         expectedStats.executionTimeInMaxPeriodMs = 23 * MINUTE_IN_MILLIS;
@@ -569,7 +573,7 @@
                                 2 * MINUTE_IN_MILLIS, 2));
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
         // Invalid time is now since the earlist session straddles the max period cutoff time.
-        expectedStats.invalidTimeElapsed = now;
+        expectedStats.expirationTimeElapsed = now;
         expectedStats.executionTimeInWindowMs = 22 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 15;
         expectedStats.executionTimeInMaxPeriodMs = 24 * MINUTE_IN_MILLIS;
@@ -599,7 +603,7 @@
 
         // Active
         expectedStats.windowSizeMs = 10 * MINUTE_IN_MILLIS;
-        expectedStats.invalidTimeElapsed = now + 4 * MINUTE_IN_MILLIS;
+        expectedStats.expirationTimeElapsed = now + 4 * MINUTE_IN_MILLIS;
         expectedStats.executionTimeInWindowMs = 3 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 5;
         expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
@@ -609,7 +613,7 @@
 
         // Working
         expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
-        expectedStats.invalidTimeElapsed = now;
+        expectedStats.expirationTimeElapsed = now;
         expectedStats.executionTimeInWindowMs = 13 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 10;
         expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
@@ -621,7 +625,7 @@
 
         // Frequent
         expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
-        expectedStats.invalidTimeElapsed = now + HOUR_IN_MILLIS;
+        expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
         expectedStats.executionTimeInWindowMs = 23 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 15;
         expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
@@ -633,7 +637,7 @@
 
         // Rare
         expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
-        expectedStats.invalidTimeElapsed = now + HOUR_IN_MILLIS;
+        expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
         expectedStats.executionTimeInWindowMs = 33 * MINUTE_IN_MILLIS;
         expectedStats.bgJobCountInWindow = 20;
         expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
@@ -675,7 +679,7 @@
 
         ExecutionStats expectedStats = new ExecutionStats();
         expectedStats.windowSizeMs = originalStatsActive.windowSizeMs;
-        expectedStats.invalidTimeElapsed = originalStatsActive.invalidTimeElapsed;
+        expectedStats.expirationTimeElapsed = originalStatsActive.expirationTimeElapsed;
         expectedStats.executionTimeInWindowMs = originalStatsActive.executionTimeInWindowMs;
         expectedStats.bgJobCountInWindow = originalStatsActive.bgJobCountInWindow;
         expectedStats.executionTimeInMaxPeriodMs = originalStatsActive.executionTimeInMaxPeriodMs;
@@ -688,7 +692,7 @@
         assertEquals(expectedStats, newStatsActive);
 
         expectedStats.windowSizeMs = originalStatsWorking.windowSizeMs;
-        expectedStats.invalidTimeElapsed = originalStatsWorking.invalidTimeElapsed;
+        expectedStats.expirationTimeElapsed = originalStatsWorking.expirationTimeElapsed;
         expectedStats.executionTimeInWindowMs = originalStatsWorking.executionTimeInWindowMs;
         expectedStats.bgJobCountInWindow = originalStatsWorking.bgJobCountInWindow;
         expectedStats.quotaCutoffTimeElapsed = originalStatsWorking.quotaCutoffTimeElapsed;
@@ -698,7 +702,7 @@
         assertNotEquals(expectedStats, newStatsWorking);
 
         expectedStats.windowSizeMs = originalStatsFrequent.windowSizeMs;
-        expectedStats.invalidTimeElapsed = originalStatsFrequent.invalidTimeElapsed;
+        expectedStats.expirationTimeElapsed = originalStatsFrequent.expirationTimeElapsed;
         expectedStats.executionTimeInWindowMs = originalStatsFrequent.executionTimeInWindowMs;
         expectedStats.bgJobCountInWindow = originalStatsFrequent.bgJobCountInWindow;
         expectedStats.quotaCutoffTimeElapsed = originalStatsFrequent.quotaCutoffTimeElapsed;
@@ -708,7 +712,7 @@
         assertNotEquals(expectedStats, newStatsFrequent);
 
         expectedStats.windowSizeMs = originalStatsRare.windowSizeMs;
-        expectedStats.invalidTimeElapsed = originalStatsRare.invalidTimeElapsed;
+        expectedStats.expirationTimeElapsed = originalStatsRare.expirationTimeElapsed;
         expectedStats.executionTimeInWindowMs = originalStatsRare.executionTimeInWindowMs;
         expectedStats.bgJobCountInWindow = originalStatsRare.bgJobCountInWindow;
         expectedStats.quotaCutoffTimeElapsed = originalStatsRare.quotaCutoffTimeElapsed;
@@ -719,6 +723,77 @@
     }
 
     @Test
+    public void testIsWithinQuotaLocked_NeverApp() {
+        assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test.never", NEVER_INDEX));
+    }
+
+    @Test
+    public void testIsWithinQuotaLocked_Charging() {
+        setCharging();
+        assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX));
+    }
+
+    @Test
+    public void testIsWithinQuotaLocked_UnderDuration_UnderJobCount() {
+        setDischarging();
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.incrementJobCount(0, "com.android.test", 5);
+        assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+    }
+
+    @Test
+    public void testIsWithinQuotaLocked_UnderDuration_OverJobCount() {
+        setDischarging();
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        final int jobCount = mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME;
+        mQuotaController.saveTimingSession(0, "com.android.test.spam",
+                createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25));
+        mQuotaController.saveTimingSession(0, "com.android.test.spam",
+                createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount));
+        mQuotaController.incrementJobCount(0, "com.android.test.spam", jobCount);
+        assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test.spam",
+                WORKING_INDEX));
+
+        mQuotaController.saveTimingSession(0, "com.android.test.frequent",
+                createTimingSession(now - (2 * HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 2000));
+        mQuotaController.saveTimingSession(0, "com.android.test.frequent",
+                createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 500));
+        assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test.frequent",
+                FREQUENT_INDEX));
+    }
+
+    @Test
+    public void testIsWithinQuotaLocked_OverDuration_UnderJobCount() {
+        setDischarging();
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.incrementJobCount(0, "com.android.test", 5);
+        assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+    }
+
+    @Test
+    public void testIsWithinQuotaLocked_OverDuration_OverJobCount() {
+        setDischarging();
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        final int jobCount = mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME;
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount));
+        mQuotaController.incrementJobCount(0, "com.android.test", jobCount);
+        assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+    }
+
+    @Test
     public void testMaybeScheduleCleanupAlarmLocked() {
         // No sessions saved yet.
         mQuotaController.maybeScheduleCleanupAlarmLocked();
@@ -752,6 +827,7 @@
 
         // Active window size is 10 minutes.
         final int standbyBucket = ACTIVE_INDEX;
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
 
         // No sessions saved yet.
         mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -1016,11 +1092,37 @@
                 .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
 
         mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX);
-        inOrder.verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
-                any());
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
         inOrder.verify(mAlarmManager, times(1)).cancel(any(AlarmManager.OnAlarmListener.class));
     }
 
+    @Test
+    public void testMaybeScheduleStartAlarmLocked_JobCount_AllowedTime() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        final int standbyBucket = WORKING_INDEX;
+        ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
+                SOURCE_PACKAGE, standbyBucket);
+        stats.jobCountInAllowedTime =
+                mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME + 2;
+
+        // Invalid time in the past, so the count shouldn't be used.
+        stats.jobCountExpirationTimeElapsed =
+                now - mQuotaController.getAllowedTimePerPeriodMs() / 2;
+        mQuotaController.maybeScheduleStartAlarmLocked(
+                SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Invalid time in the future, so the count should be used.
+        stats.jobCountExpirationTimeElapsed =
+                now + mQuotaController.getAllowedTimePerPeriodMs() / 2;
+        final long expectedWorkingAlarmTime =
+                stats.jobCountExpirationTimeElapsed + mQuotaController.getAllowedTimePerPeriodMs();
+        mQuotaController.maybeScheduleStartAlarmLocked(
+                SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+        verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+    }
 
     /**
      * Tests that the start alarm is properly rescheduled if the earliest session that contributes
@@ -1172,6 +1274,11 @@
         mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 45 * MINUTE_IN_MILLIS;
         mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = 60 * MINUTE_IN_MILLIS;
         mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = 3 * HOUR_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = 5000;
+        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = 4000;
+        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = 3000;
+        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = 2000;
+        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = 500;
 
         mQuotaController.onConstantsUpdatedLocked();
 
@@ -1183,11 +1290,16 @@
                 mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
         assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
         assertEquals(3 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
+        assertEquals(500, mQuotaController.getMaxJobCountPerAllowedTime());
+        assertEquals(5000, mQuotaController.getBucketMaxJobCounts()[ACTIVE_INDEX]);
+        assertEquals(4000, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
+        assertEquals(3000, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
+        assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
     }
 
     @Test
     public void testConstantsUpdating_InvalidValues() {
-        // Test negatives
+        // Test negatives/too low.
         mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = -MINUTE_IN_MILLIS;
         mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = -MINUTE_IN_MILLIS;
         mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = -MINUTE_IN_MILLIS;
@@ -1195,6 +1307,11 @@
         mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = -MINUTE_IN_MILLIS;
         mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = -MINUTE_IN_MILLIS;
         mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = -MINUTE_IN_MILLIS;
+        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = -1;
+        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = 1;
+        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = 1;
+        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = 1;
+        mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = 0;
 
         mQuotaController.onConstantsUpdatedLocked();
 
@@ -1205,6 +1322,11 @@
         assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
         assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
         assertEquals(HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
+        assertEquals(10, mQuotaController.getMaxJobCountPerAllowedTime());
+        assertEquals(100, mQuotaController.getBucketMaxJobCounts()[ACTIVE_INDEX]);
+        assertEquals(100, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
+        assertEquals(100, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
+        assertEquals(100, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
 
         // Test larger than a day. Controller should cap at one day.
         mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = 25 * HOUR_IN_MILLIS;
@@ -1246,6 +1368,7 @@
     @Test
     public void testTimerTracking_Discharging() {
         setDischarging();
+        setProcessState(ActivityManager.PROCESS_STATE_BACKUP);
 
         JobStatus jobStatus = createJobStatus("testTimerTracking_Discharging", 1);
         mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
@@ -1293,6 +1416,8 @@
      */
     @Test
     public void testTimerTracking_ChargingAndDischarging() {
+        setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+
         JobStatus jobStatus = createJobStatus("testTimerTracking_ChargingAndDischarging", 1);
         mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
         JobStatus jobStatus2 = createJobStatus("testTimerTracking_ChargingAndDischarging", 2);
@@ -1363,6 +1488,7 @@
     @Test
     public void testTimerTracking_AllBackground() {
         setDischarging();
+        setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
 
         JobStatus jobStatus = createJobStatus("testTimerTracking_AllBackground", 1);
         mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
@@ -1503,6 +1629,64 @@
     }
 
     /**
+     * Tests that Timers don't track job counts while in the foreground.
+     */
+    @Test
+    public void testTimerTracking_JobCount_Foreground() {
+        setDischarging();
+
+        final int standbyBucket = ACTIVE_INDEX;
+        JobStatus jobFg1 = createJobStatus("testTimerTracking_JobCount_Foreground", 1);
+        JobStatus jobFg2 = createJobStatus("testTimerTracking_JobCount_Foreground", 2);
+
+        mQuotaController.maybeStartTrackingJobLocked(jobFg1, null);
+        mQuotaController.maybeStartTrackingJobLocked(jobFg2, null);
+        assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+        ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
+                SOURCE_PACKAGE, standbyBucket);
+        assertEquals(0, stats.jobCountInAllowedTime);
+
+        setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        mQuotaController.prepareForExecutionLocked(jobFg1);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.prepareForExecutionLocked(jobFg2);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobFg2, null, false);
+        assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+        assertEquals(0, stats.jobCountInAllowedTime);
+    }
+
+    /**
+     * Tests that Timers properly track job counts while in the background.
+     */
+    @Test
+    public void testTimerTracking_JobCount_Background() {
+        final int standbyBucket = WORKING_INDEX;
+        JobStatus jobBg1 = createJobStatus("testTimerTracking_JobCount_Background", 1);
+        JobStatus jobBg2 = createJobStatus("testTimerTracking_JobCount_Background", 2);
+        mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+        mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+
+        ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
+                SOURCE_PACKAGE, standbyBucket);
+        assertEquals(0, stats.jobCountInAllowedTime);
+
+        setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+        mQuotaController.prepareForExecutionLocked(jobBg1);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.prepareForExecutionLocked(jobBg2);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobBg1, null, false);
+        advanceElapsedClock(10 * SECOND_IN_MILLIS);
+        mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+
+        assertEquals(2, stats.jobCountInAllowedTime);
+    }
+
+    /**
      * Tests that Timers properly track overlapping top and background jobs.
      */
     @Test
@@ -1680,6 +1864,7 @@
         JobStatus jobStatus = createJobStatus("testTracking_OutOfQuota", 1);
         mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
         setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window
+        setProcessState(ActivityManager.PROCESS_STATE_HOME);
         // Now the package only has two seconds to run.
         final long remainingTimeMs = 2 * SECOND_IN_MILLIS;
         mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -1707,6 +1892,7 @@
         JobStatus jobStatus = createJobStatus("testTracking_OutOfQuota", 1);
         mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
         setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window
+        setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
         Handler handler = mQuotaController.getHandler();
         spyOn(handler);
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
index 5083110..d91ce39 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
@@ -308,6 +308,24 @@
         assertZoomsImmediatelyOnSwipeFrom(STATE_SHORTCUT_TRIGGERED);
     }
 
+    @Test
+    public void testMultiTap_outOfDistanceSlop_shouldInIdle() {
+        // All delay motion events should be sent, if multi-tap with out of distance slop.
+        // STATE_IDLE will check if tapCount() < 2.
+        allowEventDelegation();
+        assertStaysIn(STATE_IDLE, () -> {
+            tap();
+            tap(DEFAULT_X * 2, DEFAULT_Y * 2);
+        });
+        assertStaysIn(STATE_IDLE, () -> {
+            tap();
+            tap(DEFAULT_X * 2, DEFAULT_Y * 2);
+            tap();
+            tap(DEFAULT_X * 2, DEFAULT_Y * 2);
+            tap();
+        });
+    }
+
     private void assertZoomsImmediatelyOnSwipeFrom(int state) {
         goFromStateIdleTo(state);
         swipeAndHold();
@@ -531,6 +549,11 @@
         send(upEvent());
     }
 
+    private void tap(float x, float y) {
+        send(downEvent(x, y));
+        send(upEvent(x, y));
+    }
+
     private void swipe() {
         swipeAndHold();
         send(upEvent());
@@ -572,18 +595,26 @@
     }
 
     private MotionEvent downEvent() {
+        return downEvent(DEFAULT_X, DEFAULT_Y);
+    }
+
+    private MotionEvent downEvent(float x, float y) {
         mLastDownTime = mClock.now();
         return fromTouchscreen(MotionEvent.obtain(mLastDownTime, mLastDownTime,
-                ACTION_DOWN, DEFAULT_X, DEFAULT_Y, 0));
+                ACTION_DOWN, x, y, 0));
     }
 
     private MotionEvent upEvent() {
-        return upEvent(mLastDownTime);
+        return upEvent(DEFAULT_X, DEFAULT_Y, mLastDownTime);
     }
 
-    private MotionEvent upEvent(long downTime) {
+    private MotionEvent upEvent(float x, float y) {
+        return upEvent(x, y, mLastDownTime);
+    }
+
+    private MotionEvent upEvent(float x, float y, long downTime) {
         return fromTouchscreen(MotionEvent.obtain(downTime, mClock.now(),
-                MotionEvent.ACTION_UP, DEFAULT_X, DEFAULT_Y, 0));
+                MotionEvent.ACTION_UP, x, y, 0));
     }
 
     private MotionEvent pointerEvent(int action, float x, float y) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 38e8ac2..0813e6fa 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -206,6 +206,8 @@
         mAdmin1Context.binder.callingUid = DpmMockContext.CALLER_UID;
 
         setUpUserManager();
+
+        when(getServices().lockPatternUtils.hasSecureLockScreen()).thenReturn(true);
     }
 
     private TransferOwnershipMetadataManager getMockTransferMetadataManager() {
@@ -836,6 +838,7 @@
                 MockUtils.checkIntent(intent),
                 MockUtils.checkUserHandle(MANAGED_PROFILE_USER_ID));
     }
+
     /**
      * Test for: {@link DevicePolicyManager#setDeviceOwner} DO on system user installs successfully.
      */
@@ -2618,6 +2621,7 @@
         when(getServices().lockPatternUtils
                 .isSeparateProfileChallengeEnabled(eq(PROFILE_USER))).thenReturn(false);
         dpmi.reportSeparateProfileChallengeChanged(PROFILE_USER);
+        when(getServices().lockPatternUtils.hasSecureLockScreen()).thenReturn(true);
 
         verifyScreenTimeoutCall(Long.MAX_VALUE, PROFILE_USER);
         verifyScreenTimeoutCall(10L , UserHandle.USER_SYSTEM);
@@ -4233,6 +4237,41 @@
         assertTrue(dpm.isActivePasswordSufficient());
     }
 
+    public void testIsActivePasswordSufficient_noLockScreen() throws Exception {
+        // If there is no lock screen, the password is considered empty no matter what, because
+        // it provides no security.
+        when(getServices().lockPatternUtils.hasSecureLockScreen()).thenReturn(false);
+
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        mContext.packageName = admin1.getPackageName();
+        setupDeviceOwner();
+
+        // If no password requirements are set, isActivePasswordSufficient should succeed.
+        assertTrue(dpm.isActivePasswordSufficient());
+
+        // Now set some password quality requirements.
+        dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
+
+        reset(mContext.spiedContext);
+        final int userHandle = UserHandle.getUserId(mContext.binder.callingUid);
+        PasswordMetrics passwordMetricsNoSymbols = new PasswordMetrics(
+                DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, 9,
+                8, 2,
+                6, 1,
+                0, 1);
+        // This should be ignored, as there is no lock screen.
+        dpm.setActivePasswordState(passwordMetricsNoSymbols, userHandle);
+        dpm.reportPasswordChanged(userHandle);
+
+        // No broadcast should be sent.
+        verify(mContext.spiedContext, times(0)).sendBroadcastAsUser(
+                MockUtils.checkIntentAction(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED),
+                MockUtils.checkUserHandle(userHandle));
+
+        // The active (nonexistent) password doesn't comply with the requirements.
+        assertFalse(dpm.isActivePasswordSufficient());
+    }
+
     private void setActivePasswordState(PasswordMetrics passwordMetrics)
             throws Exception {
         final int userHandle = UserHandle.getUserId(mContext.binder.callingUid);
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
index 0328621..8afc3d3 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
@@ -22,7 +22,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -58,8 +57,7 @@
         // Make sure that there is a short-circuit for DEFAULT_DISPLAY.
         assertEquals(DEFAULT_DISPLAY,
                 InputMethodManagerService.computeImeDisplayIdForTarget(
-                        DEFAULT_DISPLAY, false /* isVrImeStarted */,
-                        sMustNotBeCalledChecker));
+                        DEFAULT_DISPLAY, sMustNotBeCalledChecker));
     }
 
     @Test
@@ -67,17 +65,7 @@
         // Make sure that there is a short-circuit for INVALID_DISPLAY.
         assertEquals(DEFAULT_DISPLAY,
                 InputMethodManagerService.computeImeDisplayIdForTarget(
-                        INVALID_DISPLAY, false /* isVrImeStarted */,
-                        sMustNotBeCalledChecker));
-    }
-
-    @Test
-    public void testComputeImeDisplayId_VrIme() {
-        // Make sure that there is a short-circuit for VR IME.
-        assertEquals(DEFAULT_DISPLAY,
-                InputMethodManagerService.computeImeDisplayIdForTarget(
-                        SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, true /* isVrImeStarted */,
-                        sMustNotBeCalledChecker));
+                        INVALID_DISPLAY, sMustNotBeCalledChecker));
     }
 
     @Test
@@ -86,8 +74,7 @@
         // Make sure IME displayId is DEFAULT_DISPLAY.
         assertEquals(DEFAULT_DISPLAY,
                 InputMethodManagerService.computeImeDisplayIdForTarget(
-                        NO_SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, false /* isVrImeStarted */,
-                        sChecker));
+                        NO_SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, sChecker));
     }
 
     @Test
@@ -96,7 +83,6 @@
         // Make sure IME displayId is the same display.
         assertEquals(SYSTEM_DECORATION_SUPPORT_DISPLAY_ID,
                 InputMethodManagerService.computeImeDisplayIdForTarget(
-                        SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, false /* isVrImeStarted */,
-                        sChecker));
+                        SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, sChecker));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index 2dc3510..cf89cb8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -37,8 +37,8 @@
 import android.os.IProgressListener;
 import android.os.RemoteException;
 import android.os.UserManager;
-import android.os.storage.StorageManager;
 import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
 import android.security.KeyStore;
 import android.test.AndroidTestCase;
 
@@ -46,6 +46,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockSettingsInternal;
 import com.android.server.LocalServices;
+import com.android.server.wm.WindowManagerInternal;
 
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
@@ -85,6 +86,8 @@
     KeyStore mKeyStore;
     MockSyntheticPasswordManager mSpManager;
     IAuthSecret mAuthSecretService;
+    WindowManagerInternal mMockWindowManager;
+    protected boolean mHasSecureLockScreen;
 
     @Override
     protected void setUp() throws Exception {
@@ -97,10 +100,13 @@
         mActivityManager = mock(IActivityManager.class);
         mDevicePolicyManager = mock(DevicePolicyManager.class);
         mDevicePolicyManagerInternal = mock(DevicePolicyManagerInternal.class);
+        mMockWindowManager = mock(WindowManagerInternal.class);
 
         LocalServices.removeServiceForTest(LockSettingsInternal.class);
         LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+        LocalServices.removeServiceForTest(WindowManagerInternal.class);
         LocalServices.addService(DevicePolicyManagerInternal.class, mDevicePolicyManagerInternal);
+        LocalServices.addService(WindowManagerInternal.class, mMockWindowManager);
 
         mContext = new MockLockSettingsContext(getContext(), mUserManager, mNotificationManager,
                 mDevicePolicyManager, mock(StorageManager.class), mock(TrustManager.class),
@@ -114,11 +120,17 @@
             storageDir.mkdirs();
         }
 
+        mHasSecureLockScreen = true;
         mLockPatternUtils = new LockPatternUtils(mContext) {
             @Override
             public ILockSettings getLockSettings() {
                 return mService;
             }
+
+            @Override
+            public boolean hasSecureLockScreen() {
+                return mHasSecureLockScreen;
+            }
         };
         mSpManager = new MockSyntheticPasswordManager(mContext, mStorage, mGateKeeperService,
                 mUserManager);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index e12f6d3..5124803 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -26,13 +26,12 @@
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
 
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.service.gatekeeper.GateKeeperResponse;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.VerifyCredentialResponse;
-import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
 import com.android.server.locksettings.FakeGateKeeperService.VerifyHandle;
+import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
 
 /**
  * runtest frameworks-services -c com.android.server.locksettings.LockSettingsServiceTests
@@ -54,11 +53,21 @@
                 PASSWORD_QUALITY_ALPHABETIC);
     }
 
+    public void testCreatePasswordFailsWithoutLockScreen() throws RemoteException {
+        testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, "password",
+                CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_ALPHABETIC);
+    }
+
     public void testCreatePatternPrimaryUser() throws RemoteException {
         testCreateCredential(PRIMARY_USER_ID, "123456789", CREDENTIAL_TYPE_PATTERN,
                 PASSWORD_QUALITY_SOMETHING);
     }
 
+    public void testCreatePatternFailsWithoutLockScreen() throws RemoteException {
+        testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, "123456789",
+                CREDENTIAL_TYPE_PATTERN, PASSWORD_QUALITY_SOMETHING);
+    }
+
     public void testChangePasswordPrimaryUser() throws RemoteException {
         testChangeCredentials(PRIMARY_USER_ID, "78963214", CREDENTIAL_TYPE_PATTERN,
                 "asdfghjk", CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_ALPHABETIC);
@@ -198,6 +207,21 @@
         assertVerifyCredentials(userId, credential, type, -1);
     }
 
+    private void testCreateCredentialFailsWithoutLockScreen(
+            int userId, String credential, int type, int quality) throws RemoteException {
+        mHasSecureLockScreen = false;
+
+        try {
+            mService.setLockCredential(credential, type, null, quality, userId);
+            fail("An exception should have been thrown.");
+        } catch (UnsupportedOperationException e) {
+            // Success - the exception was expected.
+        }
+
+        assertFalse(mService.havePassword(userId));
+        assertFalse(mService.havePattern(userId));
+    }
+
     private void testChangeCredentials(int userId, String newCredential, int newType,
             String oldCredential, int oldType, int quality) throws RemoteException {
         final long sid = 1234;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
index a28a5a1..929c3b5 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
@@ -77,6 +78,7 @@
         final Context context = InstrumentationRegistry.getTargetContext();
         mUserId = ActivityManager.getCurrentUser();
         mCommand = new LockSettingsShellCommand(mLockPatternUtils);
+        when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(true);
     }
 
     @Test
@@ -103,6 +105,16 @@
     }
 
     @Test
+    public void testChangePin_noLockScreen() throws Exception {
+        when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
+        assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
+                new String[] { "set-pin", "--old", "1234", "4321" },
+                mShellCallback, mResultReceiver));
+        verify(mLockPatternUtils).hasSecureLockScreen();
+        verifyNoMoreInteractions(mLockPatternUtils);
+    }
+
+    @Test
     public void testChangePassword() throws Exception {
         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
@@ -115,6 +127,16 @@
     }
 
     @Test
+    public void testChangePassword_noLockScreen() throws Exception {
+        when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
+        assertEquals(-1,  mCommand.exec(new Binder(), in, out, err,
+                new String[] { "set-password", "--old", "1234", "4321" },
+                mShellCallback, mResultReceiver));
+        verify(mLockPatternUtils).hasSecureLockScreen();
+        verifyNoMoreInteractions(mLockPatternUtils);
+    }
+
+    @Test
     public void testChangePattern() throws Exception {
         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
@@ -126,6 +148,16 @@
     }
 
     @Test
+    public void testChangePattern_noLockScreen() throws Exception {
+        when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
+        assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
+                new String[] { "set-pattern", "--old", "1234", "4321" },
+                mShellCallback, mResultReceiver));
+        verify(mLockPatternUtils).hasSecureLockScreen();
+        verifyNoMoreInteractions(mLockPatternUtils);
+    }
+
+    @Test
     public void testClear() throws Exception {
         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 94e02bc..0595a5b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -40,10 +40,10 @@
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
 import com.android.server.locksettings.SyntheticPasswordManager.PasswordData;
 
-import java.util.ArrayList;
-
 import org.mockito.ArgumentCaptor;
 
+import java.util.ArrayList;
+
 
 /**
  * runtest frameworks-services -c com.android.server.locksettings.SyntheticPasswordTests
@@ -448,6 +448,37 @@
         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
     }
 
+    public void testSetLockCredentialWithTokenFailsWithoutLockScreen() throws Exception {
+        final String password = "password";
+        final String pattern = "123654";
+        final String token = "some-high-entropy-secure-token";
+
+        mHasSecureLockScreen = false;
+        enableSyntheticPassword();
+        long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID);
+        assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
+
+        try {
+            mLocalService.setLockCredentialWithToken(password,
+                    LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, token.getBytes(),
+                    PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+            fail("An exception should have been thrown.");
+        } catch (UnsupportedOperationException e) {
+            // Success - the exception was expected.
+        }
+        assertFalse(mService.havePassword(PRIMARY_USER_ID));
+
+        try {
+            mLocalService.setLockCredentialWithToken(pattern,
+                    LockPatternUtils.CREDENTIAL_TYPE_PATTERN, handle, token.getBytes(),
+                    PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+            fail("An exception should have been thrown.");
+        } catch (UnsupportedOperationException e) {
+            // Success - the exception was expected.
+        }
+        assertFalse(mService.havePattern(PRIMARY_USER_ID));
+    }
+
     public void testgetHashFactorPrimaryUser() throws RemoteException {
         final String password = "password";
         mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
index f817e8e..6da202b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
@@ -16,8 +16,6 @@
 
 package com.android.server.pm.dex;
 
-import static com.android.server.pm.dex.PackageDynamicCodeLoading.FILE_TYPE_DEX;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.atMost;
@@ -26,10 +24,12 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
 
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
+import android.os.UserHandle;
 import android.os.storage.StorageManager;
 
 import androidx.test.filters.SmallTest;
@@ -56,40 +56,44 @@
 public class DexLoggerTests {
     private static final String OWNING_PACKAGE_NAME = "package.name";
     private static final String VOLUME_UUID = "volUuid";
-    private static final String DEX_PATH = "/bar/foo.jar";
+    private static final String FILE_PATH = "/bar/foo.jar";
     private static final int STORAGE_FLAGS = StorageManager.FLAG_STORAGE_DE;
     private static final int OWNER_UID = 43;
     private static final int OWNER_USER_ID = 44;
 
     // Obtained via: echo -n "foo.jar" | sha256sum
-    private static final String DEX_FILENAME_HASH =
+    private static final String FILENAME_HASH =
             "91D7B844D7CC9673748FF057D8DC83972280FC28537D381AA42015A9CF214B9F";
 
-    private static final byte[] CONTENT_HASH_BYTES = new byte[] {
-        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
-        17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
+    private static final byte[] CONTENT_HASH_BYTES = new byte[]{
+            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+            17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
     };
     private static final String CONTENT_HASH =
             "0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20";
     private static final byte[] EMPTY_BYTES = {};
 
-    @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+    private static final String EXPECTED_MESSAGE_WITHOUT_CONTENT_HASH =
+            "dcl:" + FILENAME_HASH;
+    private static final String EXPECTED_MESSAGE_WITH_CONTENT_HASH =
+            EXPECTED_MESSAGE_WITHOUT_CONTENT_HASH + " " + CONTENT_HASH;
+    private static final String EXPECTED_MESSAGE_NATIVE_WITH_CONTENT_HASH =
+            "dcln:" + FILENAME_HASH + " " + CONTENT_HASH;
+
+    @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.LENIENT);
 
     @Mock IPackageManager mPM;
     @Mock Installer mInstaller;
 
-    private PackageDynamicCodeLoading mPackageDynamicCodeLoading;
     private DexLogger mDexLogger;
 
     private final ListMultimap<Integer, String> mMessagesForUid = ArrayListMultimap.create();
     private boolean mWriteTriggered = false;
-    private static final String EXPECTED_MESSAGE_WITH_CONTENT_HASH =
-            DEX_FILENAME_HASH + " " + CONTENT_HASH;
 
     @Before
     public void setup() throws Exception {
         // Disable actually attempting to do file writes.
-        mPackageDynamicCodeLoading = new PackageDynamicCodeLoading() {
+        PackageDynamicCodeLoading packageDynamicCodeLoading = new PackageDynamicCodeLoading() {
             @Override
             void maybeWriteAsync() {
                 mWriteTriggered = true;
@@ -102,13 +106,13 @@
         };
 
         // For test purposes capture log messages as well as sending to the event log.
-        mDexLogger = new DexLogger(mPM, mInstaller, mPackageDynamicCodeLoading) {
+        mDexLogger = new DexLogger(mPM, mInstaller, packageDynamicCodeLoading) {
             @Override
-                void writeDclEvent(int uid, String message) {
-                    super.writeDclEvent(uid, message);
-                    mMessagesForUid.put(uid, message);
-                }
-            };
+            void writeDclEvent(String subtag, int uid, String message) {
+                super.writeDclEvent(subtag, uid, message);
+                mMessagesForUid.put(uid, subtag + ":" + message);
+            }
+        };
 
         // Make the owning package exist in our mock PackageManager.
         ApplicationInfo appInfo = new ApplicationInfo();
@@ -124,9 +128,9 @@
 
     @Test
     public void testOneLoader_ownFile_withFileHash() throws Exception {
-        whenFileIsHashed(DEX_PATH, doReturn(CONTENT_HASH_BYTES));
+        whenFileIsHashed(FILE_PATH, doReturn(CONTENT_HASH_BYTES));
 
-        recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+        recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
         mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
@@ -139,13 +143,13 @@
 
     @Test
     public void testOneLoader_ownFile_noFileHash() throws Exception {
-        whenFileIsHashed(DEX_PATH, doReturn(EMPTY_BYTES));
+        whenFileIsHashed(FILE_PATH, doReturn(EMPTY_BYTES));
 
-        recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+        recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
         mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
-        assertThat(mMessagesForUid).containsEntry(OWNER_UID, DEX_FILENAME_HASH);
+        assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITHOUT_CONTENT_HASH);
 
         // File should be removed from the DCL list, since we can't hash it.
         assertThat(mWriteTriggered).isTrue();
@@ -154,13 +158,14 @@
 
     @Test
     public void testOneLoader_ownFile_hashingFails() throws Exception {
-        whenFileIsHashed(DEX_PATH, doThrow(new InstallerException("Intentional failure for test")));
+        whenFileIsHashed(FILE_PATH,
+                doThrow(new InstallerException("Intentional failure for test")));
 
-        recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+        recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
         mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
-        assertThat(mMessagesForUid).containsEntry(OWNER_UID, DEX_FILENAME_HASH);
+        assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITHOUT_CONTENT_HASH);
 
         // File should be removed from the DCL list, since we can't hash it.
         assertThat(mWriteTriggered).isTrue();
@@ -178,11 +183,23 @@
     }
 
     @Test
+    public void testOneLoader_pathTraversal() throws Exception {
+        String filePath = "/bar/../secret/foo.jar";
+        whenFileIsHashed(filePath, doReturn(CONTENT_HASH_BYTES));
+        setPackageUid(OWNING_PACKAGE_NAME, -1);
+
+        recordLoad(OWNING_PACKAGE_NAME, filePath);
+        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+
+        assertThat(mMessagesForUid).isEmpty();
+    }
+
+    @Test
     public void testOneLoader_differentOwner() throws Exception {
-        whenFileIsHashed(DEX_PATH, doReturn(CONTENT_HASH_BYTES));
+        whenFileIsHashed(FILE_PATH, doReturn(CONTENT_HASH_BYTES));
         setPackageUid("other.package.name", 1001);
 
-        recordLoad("other.package.name", DEX_PATH);
+        recordLoad("other.package.name", FILE_PATH);
         mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid.keys()).containsExactly(1001);
@@ -192,10 +209,10 @@
 
     @Test
     public void testOneLoader_differentOwner_uninstalled() throws Exception {
-        whenFileIsHashed(DEX_PATH, doReturn(CONTENT_HASH_BYTES));
+        whenFileIsHashed(FILE_PATH, doReturn(CONTENT_HASH_BYTES));
         setPackageUid("other.package.name", -1);
 
-        recordLoad("other.package.name", DEX_PATH);
+        recordLoad("other.package.name", FILE_PATH);
         mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid).isEmpty();
@@ -203,22 +220,38 @@
     }
 
     @Test
+    public void testNativeCodeLoad() throws Exception {
+        whenFileIsHashed(FILE_PATH, doReturn(CONTENT_HASH_BYTES));
+
+        recordLoadNative(FILE_PATH);
+        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+
+        assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
+        assertThat(mMessagesForUid)
+                .containsEntry(OWNER_UID, EXPECTED_MESSAGE_NATIVE_WITH_CONTENT_HASH);
+
+        assertThat(mWriteTriggered).isFalse();
+        assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading())
+                .containsExactly(OWNING_PACKAGE_NAME);
+    }
+
+    @Test
     public void testMultipleLoadersAndFiles() throws Exception {
         String otherDexPath = "/bar/nosuchdir/foo.jar";
-        whenFileIsHashed(DEX_PATH, doReturn(CONTENT_HASH_BYTES));
+        whenFileIsHashed(FILE_PATH, doReturn(CONTENT_HASH_BYTES));
         whenFileIsHashed(otherDexPath, doReturn(EMPTY_BYTES));
         setPackageUid("other.package.name1", 1001);
         setPackageUid("other.package.name2", 1002);
 
-        recordLoad("other.package.name1", DEX_PATH);
+        recordLoad("other.package.name1", FILE_PATH);
         recordLoad("other.package.name1", otherDexPath);
-        recordLoad("other.package.name2", DEX_PATH);
-        recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+        recordLoad("other.package.name2", FILE_PATH);
+        recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
         mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid.keys()).containsExactly(1001, 1001, 1002, OWNER_UID);
         assertThat(mMessagesForUid).containsEntry(1001, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
-        assertThat(mMessagesForUid).containsEntry(1001, DEX_FILENAME_HASH);
+        assertThat(mMessagesForUid).containsEntry(1001, EXPECTED_MESSAGE_WITHOUT_CONTENT_HASH);
         assertThat(mMessagesForUid).containsEntry(1002, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
         assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
 
@@ -233,7 +266,7 @@
     @Test
     public void testUnknownOwner() {
         reset(mPM);
-        recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+        recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
         mDexLogger.logDynamicCodeLoading("other.package.name");
 
         assertThat(mMessagesForUid).isEmpty();
@@ -244,7 +277,7 @@
     @Test
     public void testUninstalledPackage() {
         reset(mPM);
-        recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+        recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
         mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid).isEmpty();
@@ -262,7 +295,16 @@
     }
 
     private void recordLoad(String loadingPackageName, String dexPath) {
-        mPackageDynamicCodeLoading.record(
-                OWNING_PACKAGE_NAME, dexPath, FILE_TYPE_DEX, OWNER_USER_ID, loadingPackageName);
+        mDexLogger.recordDex(OWNER_USER_ID, dexPath, OWNING_PACKAGE_NAME, loadingPackageName);
+        mWriteTriggered = false;
+    }
+
+    private void recordLoadNative(String nativePath) throws Exception {
+        int loadingUid = UserHandle.getUid(OWNER_USER_ID, OWNER_UID);
+        String[] packageNames = { OWNING_PACKAGE_NAME };
+        when(mPM.getPackagesForUid(loadingUid)).thenReturn(packageNames);
+
+        mDexLogger.recordNative(loadingUid, nativePath);
+        mWriteTriggered = false;
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 319ffed..8be63fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -31,7 +31,6 @@
 import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
 import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
 
@@ -76,9 +75,6 @@
         mStack = (TestActivityStack) new StackBuilder(mRootActivityContainer).build();
         mTask = mStack.getChildAt(0);
         mActivity = mTask.getTopActivity();
-
-        doReturn(false).when(mService).isBooting();
-        doReturn(true).when(mService).isBooted();
     }
 
     @Test
@@ -121,23 +117,22 @@
 
         mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
 
-        // The activity is in the focused stack so it should be resumed.
+        // The activity is in the focused stack so it should not move to paused.
         mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
-        assertTrue(mActivity.isState(RESUMED));
+        assertTrue(mActivity.isState(STOPPED));
         assertFalse(pauseFound.value);
 
-        // Make the activity non focusable
-        mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
-        doReturn(false).when(mActivity).isFocusable();
+        // Clear focused stack
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+        when(display.getFocusedStack()).thenReturn(null);
 
-        // If the activity is not focusable, it should move to paused.
+        // In the unfocused stack, the activity should move to paused.
         mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
         assertTrue(mActivity.isState(PAUSING));
         assertTrue(pauseFound.value);
 
         // Make sure that the state does not change for current non-stopping states.
         mActivity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped");
-        doReturn(true).when(mActivity).isFocusable();
 
         mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index ea8f33f..68df87e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -55,7 +55,6 @@
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.PowerManager;
 import android.os.Process;
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
@@ -426,7 +425,6 @@
             doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
             // allow background activity starts by default
             doReturn(true).when(this).isBackgroundActivityStartsEnabled();
-            doNothing().when(this).updateCpuStats();
         }
 
         void setup(IntentFirewall intentFirewall, PendingIntentController intentController,
@@ -582,8 +580,6 @@
             doNothing().when(this).acquireLaunchWakelock();
             doReturn(mKeyguardController).when(this).getKeyguardController();
 
-            mLaunchingActivity = mock(PowerManager.WakeLock.class);
-
             initialize();
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index d0b9225..ea5ab7b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -70,9 +70,9 @@
 
         mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         verify(mTransaction).reparent(eq(mToken.getSurfaceControl()),
-                eq(mToken.mSurfaceAnimator.mLeash.getHandle()));
+                eq(mToken.mSurfaceAnimator.mLeash));
         verify(mTransaction).reparent(eq(mToken.mSurfaceAnimator.mLeash),
-                eq(mToken.mAnimationBoundsLayer.getHandle()));
+                eq(mToken.mAnimationBoundsLayer));
     }
 
     @Test
@@ -111,7 +111,7 @@
 
         mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         verify(mTransaction).reparent(eq(mToken.getSurfaceControl()),
-                eq(mToken.mSurfaceAnimator.mLeash.getHandle()));
+                eq(mToken.mSurfaceAnimator.mLeash));
         assertThat(mToken.mAnimationBoundsLayer).isNull();
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index ad80cd6..9b84215 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -90,7 +90,7 @@
         final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
                 OnAnimationFinishedCallback.class);
         assertAnimating(mAnimatable);
-        verify(mTransaction).reparent(eq(mAnimatable.mSurface), eq(mAnimatable.mLeash.getHandle()));
+        verify(mTransaction).reparent(eq(mAnimatable.mSurface), eq(mAnimatable.mLeash));
         verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
 
         callbackCaptor.getValue().onAnimationFinished(mSpec);
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 6c4b1af8..0fe5e08 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -793,15 +793,17 @@
      * <p>
      * Apps must be prepared for this method to return {@code null}, indicating that there currently
      * exists no user-chosen default {@code PhoneAccount}.
+     * <p>
+     * The default dialer has access to use this method.
      *
      * @return The user outgoing phone account selected by the user.
-     * @hide
      */
-    @UnsupportedAppUsage
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
         try {
             if (isServiceConnected()) {
-                return getTelecomService().getUserSelectedOutgoingPhoneAccount();
+                return getTelecomService().getUserSelectedOutgoingPhoneAccount(
+                        mContext.getOpPackageName());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelecomService#getUserSelectedOutgoingPhoneAccount", e);
@@ -810,10 +812,14 @@
     }
 
     /**
-     * Sets the user-chosen default for making outgoing phone calls.
+     * Sets the user-chosen default {@link PhoneAccountHandle} for making outgoing phone calls.
+     *
+     * @param accountHandle The {@link PhoneAccountHandle} which will be used by default for making
+     *                      outgoing voice calls.
      * @hide
      */
-    @UnsupportedAppUsage
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @SystemApi
     public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
         try {
             if (isServiceConnected()) {
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 954a709..e1d5c17 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -45,7 +45,7 @@
     /**
      * @see TelecomServiceImpl#getUserSelectedOutgoingPhoneAccount
      */
-    PhoneAccountHandle getUserSelectedOutgoingPhoneAccount();
+    PhoneAccountHandle getUserSelectedOutgoingPhoneAccount(String callingPackage);
 
     /**
      * @see TelecomServiceImpl#setUserSelectedOutgoingPhoneAccount
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 0e5c71d..0fa1b41 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -129,6 +129,66 @@
             "android.telephony.euicc.action.RESOLVE_ERROR";
 
     /**
+     * Intent action sent by system apps (such as the Settings app) to the Telephony framework to
+     * enable or disable a subscription. Must be accompanied with {@link #EXTRA_SUBSCRIPTION_ID} and
+     * {@link #EXTRA_ENABLE_SUBSCRIPTION}.
+     *
+     * <p>Unlike {@link #switchToSubscription(int, PendingIntent)}, using this action allows the
+     * underlying eUICC service (i.e. the LPA app) to control the UI experience during this
+     * operation. The action is received by the Telephony framework, which in turn selects and
+     * launches an appropriate LPA activity to present UI to the user. For example, the activity may
+     * show a confirmation dialog, a progress dialog, or an error dialog when necessary.
+     *
+     * <p>The launched activity will immediately finish with
+     * {@link android.app.Activity#RESULT_CANCELED} if {@link #isEnabled} is false.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED =
+            "android.telephony.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
+
+    /**
+     * Intent action sent by system apps (such as the Settings app) to the Telephony framework to
+     * delete a subscription. Must be accompanied with {@link #EXTRA_SUBSCRIPTION_ID}.
+     *
+     * <p>Unlike {@link #deleteSubscription(int, PendingIntent)}, using this action allows the
+     * underlying eUICC service (i.e. the LPA app) to control the UI experience during this
+     * operation. The action is received by the Telephony framework, which in turn selects and
+     * launches an appropriate LPA activity to present UI to the user. For example, the activity may
+     * show a confirmation dialog, a progress dialog, or an error dialog when necessary.
+     *
+     * <p>The launched activity will immediately finish with
+     * {@link android.app.Activity#RESULT_CANCELED} if {@link #isEnabled} is false.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED =
+            "android.telephony.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
+
+    /**
+     * Intent action sent by system apps (such as the Settings app) to the Telephony framework to
+     * rename a subscription. Must be accompanied with {@link #EXTRA_SUBSCRIPTION_ID} and
+     * {@link #EXTRA_SUBSCRIPTION_NICKNAME}.
+     *
+     * <p>Unlike {@link #updateSubscriptionNickname(int, String, PendingIntent)}, using this action
+     * allows the the underlying eUICC service (i.e. the LPA app) to control the UI experience
+     * during this operation. The action is received by the Telephony framework, which in turn
+     * selects and launches an appropriate LPA activity to present UI to the user. For example, the
+     * activity may show a confirmation dialog, a progress dialog, or an error dialog when
+     * necessary.
+     *
+     * <p>The launched activity will immediately finish with
+     * {@link android.app.Activity#RESULT_CANCELED} if {@link #isEnabled} is false.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED =
+            "android.telephony.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
+
+    /**
      * Result code for an operation indicating that the operation succeeded.
      */
     public static final int EMBEDDED_SUBSCRIPTION_RESULT_OK = 0;
@@ -219,6 +279,37 @@
             "android.telephony.euicc.extra.FORCE_PROVISION";
 
     /**
+     * Key for an extra set on privileged actions {@link #ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED},
+     * {@link #ACTION_DELETE_SUBSCRIPTION_PRIVILEGED}, and
+     * {@link #ACTION_RENAME_SUBSCRIPTION_PRIVILEGED} providing the ID of the targeted subscription.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_SUBSCRIPTION_ID =
+            "android.telephony.euicc.extra.SUBSCRIPTION_ID";
+
+    /**
+     * Key for an extra set on {@link #ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED} providing a boolean
+     * value of whether to enable or disable the targeted subscription.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_ENABLE_SUBSCRIPTION =
+            "android.telephony.euicc.extra.ENABLE_SUBSCRIPTION";
+
+    /**
+     * Key for an extra set on {@link #ACTION_RENAME_SUBSCRIPTION_PRIVILEGED} providing a new
+     * nickname for the targeted subscription.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_SUBSCRIPTION_NICKNAME =
+            "android.telephony.euicc.extra.SUBSCRIPTION_NICKNAME";
+
+    /**
      * Optional meta-data attribute for a carrier app providing an icon to use to represent the
      * carrier. If not provided, the app's launcher icon will be used as a fallback.
      */
diff --git a/tests/DexLoggerIntegrationTests/Android.mk b/tests/DexLoggerIntegrationTests/Android.mk
index ee2ec0a..979d13a 100644
--- a/tests/DexLoggerIntegrationTests/Android.mk
+++ b/tests/DexLoggerIntegrationTests/Android.mk
@@ -29,6 +29,35 @@
 dexloggertest_jar := $(LOCAL_BUILT_MODULE)
 
 
+# Also build a native library that the test app can dynamically load
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE := DexLoggerNativeTestLibrary
+LOCAL_MULTILIB := first
+LOCAL_SRC_FILES := src/cpp/com_android_dcl_Jni.cpp
+LOCAL_C_INCLUDES += \
+    $(JNI_H_INCLUDE)
+LOCAL_SDK_VERSION := 28
+LOCAL_NDK_STL_VARIANT := c++_static
+
+include $(BUILD_SHARED_LIBRARY)
+
+dexloggertest_so := $(LOCAL_BUILT_MODULE)
+
+# And a standalone native executable that we can exec.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE := DexLoggerNativeExecutable
+LOCAL_SRC_FILES := src/cpp/test_executable.cpp
+
+include $(BUILD_EXECUTABLE)
+
+dexloggertest_executable := $(LOCAL_BUILT_MODULE)
+
 # Build the test app itself
 
 include $(CLEAR_VARS)
@@ -37,14 +66,18 @@
 LOCAL_PACKAGE_NAME := DexLoggerIntegrationTests
 LOCAL_SDK_VERSION := current
 LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
+LOCAL_CERTIFICATE := shared
 LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/server/pm)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
     truth-prebuilt \
 
-# This gets us the javalib.jar built by DexLoggerTestLibrary above.
-LOCAL_JAVA_RESOURCE_FILES := $(dexloggertest_jar)
+# This gets us the javalib.jar built by DexLoggerTestLibrary above as well as the various
+# native binaries.
+LOCAL_JAVA_RESOURCE_FILES := \
+    $(dexloggertest_jar) \
+    $(dexloggertest_so) \
+    $(dexloggertest_executable)
 
 include $(BUILD_PACKAGE)
diff --git a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java
index 75ee089..d68769b 100644
--- a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java
+++ b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java
@@ -17,6 +17,7 @@
 package com.android.server.pm.dex;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.app.UiAutomation;
 import android.content.Context;
@@ -25,6 +26,7 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.LargeTest;
 import android.util.EventLog;
+import android.util.EventLog.Event;
 
 import dalvik.system.DexClassLoader;
 
@@ -65,14 +67,13 @@
     // Event log tag used for SNET related events
     private static final int SNET_TAG = 0x534e4554;
 
-    // Subtag used to distinguish dynamic code loading events
-    private static final String DCL_SUBTAG = "dcl";
+    // Subtags used to distinguish dynamic code loading events
+    private static final String DCL_DEX_SUBTAG = "dcl";
+    private static final String DCL_NATIVE_SUBTAG = "dcln";
 
-    // All the tags we care about
-    private static final int[] TAG_LIST = new int[] { SNET_TAG };
-
-    // This is {@code DynamicCodeLoggingService#JOB_ID}
-    private static final int DYNAMIC_CODE_LOGGING_JOB_ID = 2030028;
+    // These are job IDs from DynamicCodeLoggingService
+    private static final int IDLE_LOGGING_JOB_ID = 2030028;
+    private static final int AUDIT_WATCHING_JOB_ID = 203142925;
 
     private static Context sContext;
     private static int sMyUid;
@@ -89,15 +90,20 @@
         // Without this the first test passes and others don't - we don't see new events in the
         // log. The exact reason is unclear.
         EventLog.writeEvent(SNET_TAG, "Dummy event");
+
+        // Audit log messages are throttled by the kernel (at the request of logd) to 5 per
+        // second, so running the tests too quickly in sequence means we lose some and get
+        // spurious failures. Sigh.
+        SystemClock.sleep(1000);
     }
 
     @Test
-    public void testDexLoggerGeneratesEvents() throws Exception {
-        File privateCopyFile = fileForJar("copied.jar");
+    public void testDexLoggerGeneratesEvents_standardClassLoader() throws Exception {
+        File privateCopyFile = privateFile("copied.jar");
         // Obtained via "echo -n copied.jar | sha256sum"
         String expectedNameHash =
                 "1B6C71DB26F36582867432CCA12FB6A517470C9F9AABE9198DD4C5C030D6DC0C";
-        String expectedContentHash = copyAndHashJar(privateCopyFile);
+        String expectedContentHash = copyAndHashResource("/javalib.jar", privateCopyFile);
 
         // Feed the jar to a class loader and make sure it contains what we expect.
         ClassLoader parentClassLoader = sContext.getClass().getClassLoader();
@@ -107,18 +113,18 @@
 
         // And make sure we log events about it
         long previousEventNanos = mostRecentEventTimeNanos();
-        runDexLogger();
+        runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
 
-        assertDclLoggedSince(previousEventNanos, expectedNameHash, expectedContentHash);
+        assertDclLoggedSince(previousEventNanos, DCL_DEX_SUBTAG,
+                expectedNameHash, expectedContentHash);
     }
 
     @Test
-
     public void testDexLoggerGeneratesEvents_unknownClassLoader() throws Exception {
-        File privateCopyFile = fileForJar("copied2.jar");
+        File privateCopyFile = privateFile("copied2.jar");
         String expectedNameHash =
                 "202158B6A3169D78F1722487205A6B036B3F2F5653FDCFB4E74710611AC7EB93";
-        String expectedContentHash = copyAndHashJar(privateCopyFile);
+        String expectedContentHash = copyAndHashResource("/javalib.jar", privateCopyFile);
 
         // This time make sure an unknown class loader is an ancestor of the class loader we use.
         ClassLoader knownClassLoader = sContext.getClass().getClassLoader();
@@ -129,22 +135,185 @@
 
         // And make sure we log events about it
         long previousEventNanos = mostRecentEventTimeNanos();
-        runDexLogger();
+        runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
 
-        assertDclLoggedSince(previousEventNanos, expectedNameHash, expectedContentHash);
+        assertDclLoggedSince(previousEventNanos, DCL_DEX_SUBTAG,
+                expectedNameHash, expectedContentHash);
     }
 
-    private static File fileForJar(String name) {
-        return new File(sContext.getDir("jars", Context.MODE_PRIVATE), name);
+    @Test
+    public void testDexLoggerGeneratesEvents_nativeLibrary() throws Exception {
+        File privateCopyFile = privateFile("copied.so");
+        String expectedNameHash =
+                "996223BAD4B4FE75C57A3DEC61DB9C0B38E0A7AD479FC95F33494F4BC55A0F0E";
+        String expectedContentHash =
+                copyAndHashResource("/DexLoggerNativeTestLibrary.so", privateCopyFile);
+
+        System.load(privateCopyFile.toString());
+
+        // Run the job to scan generated audit log entries
+        runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID);
+
+        // And then make sure we log events about it
+        long previousEventNanos = mostRecentEventTimeNanos();
+        runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
+
+        assertDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG,
+                expectedNameHash, expectedContentHash);
     }
 
-    private static String copyAndHashJar(File copyTo) throws Exception {
+    @Test
+    public void testDexLoggerGeneratesEvents_nativeLibrary_escapedName() throws Exception {
+        // A file name with a space will be escaped in the audit log; verify we un-escape it
+        // correctly.
+        File privateCopyFile = privateFile("second copy.so");
+        String expectedNameHash =
+                "8C39990C560B4F36F83E208E279F678746FE23A790E4C50F92686584EA2041CA";
+        String expectedContentHash =
+                copyAndHashResource("/DexLoggerNativeTestLibrary.so", privateCopyFile);
+
+        System.load(privateCopyFile.toString());
+
+        // Run the job to scan generated audit log entries
+        runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID);
+
+        // And then make sure we log events about it
+        long previousEventNanos = mostRecentEventTimeNanos();
+        runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
+
+        assertDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG,
+                expectedNameHash, expectedContentHash);
+    }
+
+    @Test
+    public void testDexLoggerGeneratesEvents_nativeExecutable() throws Exception {
+        File privateCopyFile = privateFile("test_executable");
+        String expectedNameHash =
+                "3FBEC3F925A132D18F347F11AE9A5BB8DE1238828F8B4E064AA86EB68BD46DCF";
+        String expectedContentHash =
+                copyAndHashResource("/DexLoggerNativeExecutable", privateCopyFile);
+        assertThat(privateCopyFile.setExecutable(true)).isTrue();
+
+        Process process = Runtime.getRuntime().exec(privateCopyFile.toString());
+        int exitCode = process.waitFor();
+        assertThat(exitCode).isEqualTo(0);
+
+        // Run the job to scan generated audit log entries
+        runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID);
+
+        // And then make sure we log events about it
+        long previousEventNanos = mostRecentEventTimeNanos();
+        runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
+
+        assertDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG,
+                expectedNameHash, expectedContentHash);
+    }
+
+    @Test
+    public void testDexLoggerGeneratesEvents_spoofed_validFile() throws Exception {
+        File privateCopyFile = privateFile("spoofed");
+
+        String expectedContentHash =
+                copyAndHashResource("/DexLoggerNativeExecutable", privateCopyFile);
+
+        EventLog.writeEvent(EventLog.getTagCode("auditd"),
+                "type=1400 avc: granted { execute_no_trans } "
+                        + "path=\"" + privateCopyFile + "\" "
+                        + "scontext=u:r:untrusted_app_27: "
+                        + "tcontext=u:object_r:app_data_file: "
+                        + "tclass=file ");
+
+        String expectedNameHash =
+                "1CF36F503A02877BB775DC23C1C5A47A95F2684B6A1A83B11795B856D88861E3";
+
+        // Run the job to scan generated audit log entries
+        runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID);
+
+        // And then make sure we log events about it
+        long previousEventNanos = mostRecentEventTimeNanos();
+        runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
+
+        assertDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG,
+                expectedNameHash, expectedContentHash);
+    }
+
+    @Test
+    public void testDexLoggerGeneratesEvents_spoofed_pathTraversal() throws Exception {
+        File privateDir = privateFile("x").getParentFile();
+
+        // Transform /a/b/c -> /a/b/c/../../.. so we get back to the root
+        File pathTraversalToRoot = privateDir;
+        File root = new File("/");
+        while (!privateDir.equals(root)) {
+            pathTraversalToRoot = new File(pathTraversalToRoot, "..");
+            privateDir = privateDir.getParentFile();
+        }
+
+        File spoofedFile = new File(pathTraversalToRoot, "dev/urandom");
+
+        assertWithMessage("Expected " + spoofedFile + " to be readable")
+                .that(spoofedFile.canRead()).isTrue();
+
+        EventLog.writeEvent(EventLog.getTagCode("auditd"),
+                "type=1400 avc: granted { execute_no_trans } "
+                        + "path=\"" + spoofedFile + "\" "
+                        + "scontext=u:r:untrusted_app_27: "
+                        + "tcontext=u:object_r:app_data_file: "
+                        + "tclass=file ");
+
+        String expectedNameHash =
+                "65528FE876BD676B0DFCC9A8ACA8988E026766F99EEC1E1FB48F46B2F635E225";
+
+        // Run the job to scan generated audit log entries
+        runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID);
+
+        // And then trigger generating DCL events
+        long previousEventNanos = mostRecentEventTimeNanos();
+        runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
+
+        assertNoDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG, expectedNameHash);
+    }
+
+    @Test
+    public void testDexLoggerGeneratesEvents_spoofed_otherAppFile() throws Exception {
+        File ourPath = sContext.getDatabasePath("android_pay");
+        File targetPath = new File(ourPath.toString()
+                .replace("com.android.frameworks.dexloggertest", "com.google.android.gms"));
+
+        assertWithMessage("Expected " + targetPath + " to not be readable")
+                .that(targetPath.canRead()).isFalse();
+
+        EventLog.writeEvent(EventLog.getTagCode("auditd"),
+                "type=1400 avc: granted { execute_no_trans } "
+                        + "path=\"" + targetPath + "\" "
+                        + "scontext=u:r:untrusted_app_27: "
+                        + "tcontext=u:object_r:app_data_file: "
+                        + "tclass=file ");
+
+        String expectedNameHash =
+                "CBE04E8AB9E7199FC19CBAAF9C774B88E56B3B19E823F2251693380AD6F515E6";
+
+        // Run the job to scan generated audit log entries
+        runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID);
+
+        // And then trigger generating DCL events
+        long previousEventNanos = mostRecentEventTimeNanos();
+        runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
+
+        assertNoDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG, expectedNameHash);
+    }
+
+    private static File privateFile(String name) {
+        return new File(sContext.getDir("dcl", Context.MODE_PRIVATE), name);
+    }
+
+    private static String copyAndHashResource(String resourcePath, File copyTo) throws Exception {
         MessageDigest hasher = MessageDigest.getInstance("SHA-256");
 
         // Copy the jar from our Java resources to a private data directory
         Class<?> thisClass = DexLoggerIntegrationTests.class;
-        try (InputStream input = thisClass.getResourceAsStream("/javalib.jar");
-                OutputStream output = new FileOutputStream(copyTo)) {
+        try (InputStream input = thisClass.getResourceAsStream(resourcePath);
+             OutputStream output = new FileOutputStream(copyTo)) {
             byte[] buffer = new byte[1024];
             while (true) {
                 int numRead = input.read(buffer);
@@ -166,24 +335,18 @@
         return formatter.toString();
     }
 
-    private static long mostRecentEventTimeNanos() throws Exception {
-        List<EventLog.Event> events = new ArrayList<>();
-
-        EventLog.readEvents(TAG_LIST, events);
-        return events.isEmpty() ? 0 : events.get(events.size() - 1).getTimeNanos();
-    }
-
-    private static void runDexLogger() throws Exception {
-        // This forces {@code DynamicCodeLoggingService} to start now.
-        runCommand("cmd jobscheduler run -f android " + DYNAMIC_CODE_LOGGING_JOB_ID);
+    private static void runDynamicCodeLoggingJob(int jobId) throws Exception {
+        // This forces the DynamicCodeLoggingService job to start now.
+        runCommand("cmd jobscheduler run -f android " + jobId);
         // Wait for the job to have run.
         long startTime = SystemClock.elapsedRealtime();
         while (true) {
             String response = runCommand(
-                    "cmd jobscheduler get-job-state android " + DYNAMIC_CODE_LOGGING_JOB_ID);
+                    "cmd jobscheduler get-job-state android " + jobId);
             if (!response.contains("pending") && !response.contains("active")) {
                 break;
             }
+            // Don't wait forever - if it's taken > 10s then something is very wrong.
             if (SystemClock.elapsedRealtime() - startTime > TimeUnit.SECONDS.toMillis(10)) {
                 throw new AssertionError("Job has not completed: " + response);
             }
@@ -208,37 +371,68 @@
         return response.toString("UTF-8");
     }
 
-    private static void assertDclLoggedSince(long previousEventNanos, String expectedNameHash,
-            String expectedContentHash) throws Exception {
-        List<EventLog.Event> events = new ArrayList<>();
-        EventLog.readEvents(TAG_LIST, events);
-        int found = 0;
-        for (EventLog.Event event : events) {
+    private static long mostRecentEventTimeNanos() throws Exception {
+        List<Event> events = readSnetEvents();
+        return events.isEmpty() ? 0 : events.get(events.size() - 1).getTimeNanos();
+    }
+
+    private static void assertDclLoggedSince(long previousEventNanos, String expectedSubTag,
+            String expectedNameHash, String expectedContentHash) throws Exception {
+        List<String> messages =
+                findMatchingEvents(previousEventNanos, expectedSubTag, expectedNameHash);
+
+        assertWithMessage("Expected exactly one matching log entry").that(messages).hasSize(1);
+        assertThat(messages.get(0)).endsWith(expectedContentHash);
+    }
+
+    private static void assertNoDclLoggedSince(long previousEventNanos, String expectedSubTag,
+            String expectedNameHash) throws Exception {
+        List<String> messages =
+                findMatchingEvents(previousEventNanos, expectedSubTag, expectedNameHash);
+
+        assertWithMessage("Expected no matching log entries").that(messages).isEmpty();
+    }
+
+    private static List<String> findMatchingEvents(long previousEventNanos, String expectedSubTag,
+            String expectedNameHash) throws Exception {
+        List<String> messages = new ArrayList<>();
+
+        for (Event event : readSnetEvents()) {
             if (event.getTimeNanos() <= previousEventNanos) {
                 continue;
             }
-            Object[] data = (Object[]) event.getData();
 
-            // We only care about DCL events that we generated.
-            String subTag = (String) data[0];
-            if (!DCL_SUBTAG.equals(subTag)) {
+            Object data = event.getData();
+            if (!(data instanceof Object[])) {
                 continue;
             }
-            int uid = (int) data[1];
+            Object[] fields = (Object[]) data;
+
+            // We only care about DCL events that we generated.
+            String subTag = (String) fields[0];
+            if (!expectedSubTag.equals(subTag)) {
+                continue;
+            }
+            int uid = (int) fields[1];
             if (uid != sMyUid) {
                 continue;
             }
 
-            String message = (String) data[2];
+            String message = (String) fields[2];
             if (!message.startsWith(expectedNameHash)) {
                 continue;
             }
 
-            assertThat(message).endsWith(expectedContentHash);
-            ++found;
+            messages.add(message);
+            //assertThat(message).endsWith(expectedContentHash);
         }
+        return messages;
+    }
 
-        assertThat(found).isEqualTo(1);
+    private static List<Event> readSnetEvents() throws Exception {
+        List<Event> events = new ArrayList<>();
+        EventLog.readEvents(new int[] { SNET_TAG }, events);
+        return events;
     }
 
     /**
diff --git a/tests/DexLoggerIntegrationTests/src/cpp/com_android_dcl_Jni.cpp b/tests/DexLoggerIntegrationTests/src/cpp/com_android_dcl_Jni.cpp
new file mode 100644
index 0000000..0608883
--- /dev/null
+++ b/tests/DexLoggerIntegrationTests/src/cpp/com_android_dcl_Jni.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+
+extern "C" jint JNI_OnLoad(JavaVM* /* vm */, void* /* reserved */)
+{
+    return JNI_VERSION_1_6;
+}
diff --git a/tests/DexLoggerIntegrationTests/src/cpp/test_executable.cpp b/tests/DexLoggerIntegrationTests/src/cpp/test_executable.cpp
new file mode 100644
index 0000000..ad025e6
--- /dev/null
+++ b/tests/DexLoggerIntegrationTests/src/cpp/test_executable.cpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+int main() {
+    // This program just has to run, it doesn't need to do anything. So we don't.
+    return 0;
+}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index c2e735e..ec6f4b5 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -38,7 +38,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -470,7 +469,7 @@
      * Test that app user data is rolled back.
      * TODO: Stop ignoring this test once user data rollback is supported.
      */
-    @Ignore @Test
+    @Test
     public void testUserDataRollback() throws Exception {
         try {
             RollbackTestUtils.adoptShellPermissionIdentity(
@@ -479,9 +478,9 @@
                     Manifest.permission.MANAGE_ROLLBACKS);
 
             RollbackTestUtils.uninstall(TEST_APP_A);
-            RollbackTestUtils.install("RollbackTestAppV1.apk", false);
+            RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
             processUserData(TEST_APP_A);
-            RollbackTestUtils.install("RollbackTestAppV2.apk", true);
+            RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
             processUserData(TEST_APP_A);
 
             RollbackManager rm = RollbackTestUtils.getRollbackManager();
diff --git a/tests/net/java/android/net/DnsPacketTest.java b/tests/net/java/android/net/DnsPacketTest.java
new file mode 100644
index 0000000..032e526
--- /dev/null
+++ b/tests/net/java/android/net/DnsPacketTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DnsPacketTest {
+    private void assertHeaderParses(DnsPacket.DnsHeader header, int id, int flag,
+            int qCount, int aCount, int nsCount, int arCount) {
+        assertEquals(header.id, id);
+        assertEquals(header.flags, flag);
+        assertEquals(header.getSectionCount(DnsPacket.QDSECTION), qCount);
+        assertEquals(header.getSectionCount(DnsPacket.ANSECTION), aCount);
+        assertEquals(header.getSectionCount(DnsPacket.NSSECTION), nsCount);
+        assertEquals(header.getSectionCount(DnsPacket.ARSECTION), arCount);
+    }
+
+    private void assertSectionParses(DnsPacket.DnsSection section, String dname,
+            int dtype, int dclass, int ttl, byte[] rr) {
+        assertEquals(section.dName, dname);
+        assertEquals(section.nsType, dtype);
+        assertEquals(section.nsClass, dclass);
+        assertEquals(section.ttl, ttl);
+        assertTrue(Arrays.equals(section.getRR(), rr));
+    }
+
+    class TestDnsPacket extends DnsPacket {
+        TestDnsPacket(byte[] data) throws ParseException {
+            super(data);
+        }
+
+        public DnsHeader getHeader() {
+            return mHeader;
+        }
+        public List<DnsSection> getSectionList(int secType) {
+            return mSections[secType];
+        }
+    }
+
+    @Test
+    public void testNullDisallowed() {
+        try {
+            new TestDnsPacket(null);
+            fail("Exception not thrown for null byte array");
+        } catch (DnsPacket.ParseException e) {
+        }
+    }
+
+    @Test
+    public void testV4Answer() throws Exception {
+        final byte[] v4blob = new byte[] {
+            /* Header */
+            0x55, 0x66, /* Transaction ID */
+            (byte) 0x81, (byte) 0x80, /* Flags */
+            0x00, 0x01, /* Questions */
+            0x00, 0x01, /* Answer RRs */
+            0x00, 0x00, /* Authority RRs */
+            0x00, 0x00, /* Additional RRs */
+            /* Queries */
+            0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
+            0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+            0x00, 0x01, /* Type */
+            0x00, 0x01, /* Class */
+            /* Answers */
+            (byte) 0xc0, 0x0c, /* Name */
+            0x00, 0x01, /* Type */
+            0x00, 0x01, /* Class */
+            0x00, 0x00, 0x01, 0x2b, /* TTL */
+            0x00, 0x04, /* Data length */
+            (byte) 0xac, (byte) 0xd9, (byte) 0xa1, (byte) 0x84 /* Address */
+        };
+        TestDnsPacket packet = new TestDnsPacket(v4blob);
+
+        // Header part
+        assertHeaderParses(packet.getHeader(), 0x5566, 0x8180, 1, 1, 0, 0);
+
+        // Section part
+        List<DnsPacket.DnsSection> qdSectionList =
+                packet.getSectionList(DnsPacket.QDSECTION);
+        assertEquals(qdSectionList.size(), 1);
+        assertSectionParses(qdSectionList.get(0), "www.google.com", 1, 1, 0, null);
+
+        List<DnsPacket.DnsSection> anSectionList =
+                packet.getSectionList(DnsPacket.ANSECTION);
+        assertEquals(anSectionList.size(), 1);
+        assertSectionParses(anSectionList.get(0), "www.google.com", 1, 1, 0x12b,
+                new byte[]{ (byte) 0xac, (byte) 0xd9, (byte) 0xa1, (byte) 0x84 });
+    }
+
+    @Test
+    public void testV6Answer() throws Exception {
+        final byte[] v6blob = new byte[] {
+            /* Header */
+            0x77, 0x22, /* Transaction ID */
+            (byte) 0x81, (byte) 0x80, /* Flags */
+            0x00, 0x01, /* Questions */
+            0x00, 0x01, /* Answer RRs */
+            0x00, 0x00, /* Authority RRs */
+            0x00, 0x00, /* Additional RRs */
+            /* Queries */
+            0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
+            0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+            0x00, 0x1c, /* Type */
+            0x00, 0x01, /* Class */
+            /* Answers */
+            (byte) 0xc0, 0x0c, /* Name */
+            0x00, 0x1c, /* Type */
+            0x00, 0x01, /* Class */
+            0x00, 0x00, 0x00, 0x37, /* TTL */
+            0x00, 0x10, /* Data length */
+            0x24, 0x04, 0x68, 0x00, 0x40, 0x05, 0x08, 0x0d,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04 /* Address */
+        };
+        TestDnsPacket packet = new TestDnsPacket(v6blob);
+
+        // Header part
+        assertHeaderParses(packet.getHeader(), 0x7722, 0x8180, 1, 1, 0, 0);
+
+        // Section part
+        List<DnsPacket.DnsSection> qdSectionList =
+                packet.getSectionList(DnsPacket.QDSECTION);
+        assertEquals(qdSectionList.size(), 1);
+        assertSectionParses(qdSectionList.get(0), "www.google.com", 28, 1, 0, null);
+
+        List<DnsPacket.DnsSection> anSectionList =
+                packet.getSectionList(DnsPacket.ANSECTION);
+        assertEquals(anSectionList.size(), 1);
+        assertSectionParses(anSectionList.get(0), "www.google.com", 28, 1, 0x37,
+                new byte[]{ 0x24, 0x04, 0x68, 0x00, 0x40, 0x05, 0x08, 0x0d,
+                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04 });
+    }
+}
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
index c748d0f..f2ecef9 100644
--- a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
+++ b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -27,6 +27,7 @@
 import android.content.Context;
 import android.net.ipmemorystore.Blob;
 import android.net.ipmemorystore.IOnBlobRetrievedListener;
+import android.net.ipmemorystore.IOnL2KeyResponseListener;
 import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
 import android.net.ipmemorystore.IOnSameNetworkResponseListener;
 import android.net.ipmemorystore.IOnStatusListener;
@@ -67,7 +68,14 @@
     private static final String TEST_CLIENT_ID = "testClientId";
     private static final String TEST_DATA_NAME = "testData";
 
-    private static final String[] FAKE_KEYS = { "fakeKey1", "fakeKey2", "fakeKey3", "fakeKey4" };
+    private static final int FAKE_KEY_COUNT = 20;
+    private static final String[] FAKE_KEYS;
+    static {
+        FAKE_KEYS = new String[FAKE_KEY_COUNT];
+        for (int i = 0; i < FAKE_KEYS.length; ++i) {
+            FAKE_KEYS[i] = "fakeKey" + i;
+        }
+    }
 
     @Mock
     private Context mMockContext;
@@ -170,6 +178,25 @@
         };
     }
 
+    /** Helper method to make an IOnL2KeyResponseListener */
+    private interface OnL2KeyResponseListener {
+        void onL2KeyResponse(Status status, String key);
+    }
+    private IOnL2KeyResponseListener onL2KeyResponse(final OnL2KeyResponseListener functor) {
+        return new IOnL2KeyResponseListener() {
+            @Override
+            public void onL2KeyResponse(final StatusParcelable status, final String key)
+                    throws RemoteException {
+                functor.onL2KeyResponse(new Status(status), key);
+            }
+
+            @Override
+            public IBinder asBinder() {
+                return null;
+            }
+        };
+    }
+
     // Helper method to factorize some boilerplate
     private void doLatched(final String timeoutMessage, final Consumer<CountDownLatch> functor) {
         final CountDownLatch latch = new CountDownLatch(1);
@@ -195,12 +222,9 @@
     }
 
     @Test
-    public void testNetworkAttributes() {
+    public void testNetworkAttributes() throws UnknownHostException {
         final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
-        try {
-            na.setAssignedV4Address(
-                    (Inet4Address) Inet4Address.getByAddress(new byte[]{1, 2, 3, 4}));
-        } catch (UnknownHostException e) { /* Can't happen */ }
+        na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
         na.setGroupHint("hint1");
         na.setMtu(219);
         final String l2Key = FAKE_KEYS[0];
@@ -218,10 +242,8 @@
                         })));
 
         final NetworkAttributes.Builder na2 = new NetworkAttributes.Builder();
-        try {
-            na.setDnsAddresses(Arrays.asList(
-                    new InetAddress[] {Inet6Address.getByName("0A1C:2E40:480A::1CA6")}));
-        } catch (UnknownHostException e) { /* Still can't happen */ }
+        na.setDnsAddresses(Arrays.asList(
+                new InetAddress[] {Inet6Address.getByName("0A1C:2E40:480A::1CA6")}));
         final NetworkAttributes attributes2 = na2.build();
         storeAttributes("Did not complete storing attributes 2", l2Key, attributes2);
 
@@ -333,8 +355,93 @@
     }
 
     @Test
-    public void testFindL2Key() {
-        // TODO : implement this
+    public void testFindL2Key() throws UnknownHostException {
+        final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
+        na.setGroupHint("hint0");
+        storeAttributes(FAKE_KEYS[0], na.build());
+
+        na.setDnsAddresses(Arrays.asList(
+                new InetAddress[] {Inet6Address.getByName("8D56:9AF1::08EE:20F1")}));
+        na.setMtu(219);
+        storeAttributes(FAKE_KEYS[1], na.build());
+        na.setMtu(null);
+        na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
+        na.setDnsAddresses(Arrays.asList(
+                new InetAddress[] {Inet6Address.getByName("0A1C:2E40:480A::1CA6")}));
+        na.setGroupHint("hint1");
+        storeAttributes(FAKE_KEYS[2], na.build());
+        na.setMtu(219);
+        storeAttributes(FAKE_KEYS[3], na.build());
+        na.setMtu(240);
+        storeAttributes(FAKE_KEYS[4], na.build());
+        na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("5.6.7.8"));
+        storeAttributes(FAKE_KEYS[5], na.build());
+
+        // Matches key 5 exactly
+        doLatched("Did not finish finding L2Key", latch ->
+                mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+                    assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+                            status.isSuccess());
+                    assertEquals(FAKE_KEYS[5], key);
+                })));
+
+        // MTU matches key 4 but v4 address matches key 5. The latter is stronger.
+        na.setMtu(240);
+        doLatched("Did not finish finding L2Key", latch ->
+                mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+                    assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+                            status.isSuccess());
+                    assertEquals(FAKE_KEYS[5], key);
+                })));
+
+        // Closest to key 3 (indeed, identical)
+        na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
+        na.setMtu(219);
+        doLatched("Did not finish finding L2Key", latch ->
+                mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+                    assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+                            status.isSuccess());
+                    assertEquals(FAKE_KEYS[3], key);
+                })));
+
+        // Group hint alone must not be strong enough to override the rest
+        na.setGroupHint("hint0");
+        doLatched("Did not finish finding L2Key", latch ->
+                mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+                    assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+                            status.isSuccess());
+                    assertEquals(FAKE_KEYS[3], key);
+                })));
+
+        // Still closest to key 3, though confidence is lower
+        na.setGroupHint("hint1");
+        na.setDnsAddresses(null);
+        doLatched("Did not finish finding L2Key", latch ->
+                mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+                    assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+                            status.isSuccess());
+                    assertEquals(FAKE_KEYS[3], key);
+                })));
+
+        // But changing the MTU makes this closer to key 4
+        na.setMtu(240);
+        doLatched("Did not finish finding L2Key", latch ->
+                mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+                    assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+                            status.isSuccess());
+                    assertEquals(FAKE_KEYS[4], key);
+                })));
+
+        // MTU alone not strong enough to make this group-close
+        na.setGroupHint(null);
+        na.setDnsAddresses(null);
+        na.setAssignedV4Address(null);
+        doLatched("Did not finish finding L2Key", latch ->
+                mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+                    assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+                            status.isSuccess());
+                    assertNull(key);
+                })));
     }
 
     private void assertNetworksSameness(final String key1, final String key2, final int sameness) {
@@ -349,7 +456,7 @@
     @Test
     public void testIsSameNetwork() throws UnknownHostException {
         final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
-        na.setAssignedV4Address((Inet4Address) Inet4Address.getByAddress(new byte[]{1, 2, 3, 4}));
+        na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
         na.setGroupHint("hint1");
         na.setMtu(219);
         na.setDnsAddresses(Arrays.asList(Inet6Address.getByName("0A1C:2E40:480A::1CA6")));
diff --git a/tests/utils/testutils/java/com/android/test/filters/SelectTest.java b/tests/utils/testutils/java/com/android/test/filters/SelectTest.java
new file mode 100644
index 0000000..d0350af
--- /dev/null
+++ b/tests/utils/testutils/java/com/android/test/filters/SelectTest.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.filters;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.junit.runner.Description;
+import org.junit.runner.manipulation.Filter;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringJoiner;
+
+/**
+ * JUnit filter to select tests.
+ *
+ * <p>This filter selects tests specified by package name, class name, and method name. With this
+ * filter, the package and the class options of AndroidJUnitRunner can be superseded. Also the
+ * restriction that prevents using the package and the class options can be mitigated.
+ *
+ * <p><b>Select out tests from Java packages:</b> this option supersedes {@code -e package} option.
+ * <pre>
+ * adb shell am instrument -w \
+ *     -e filter com.android.test.filters.SelectTest \
+ *     -e selectTest package1.,package2. \
+ *     com.tests.pkg/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
+ * Note that the ending {@code .} in package name is mandatory.
+ *
+ * <p><b>Select out test classes:</b> this option supersedes {@code -e class} option.
+ * <pre>
+ * adb shell am instrument -w \
+ *     -e filter com.android.test.filters.SelectTest      \
+ *     -e selectTest package1.ClassA,package2.ClassB \
+ *     com.tests.pkg/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
+ *
+ * <p><b>Select out test methods from Java classes:</b>
+ * <pre>
+ * adb shell am instrument -w \
+ *     -e filter com.android.test.filters.SelectTest                      \
+ *     -e selectTest package1.ClassA#methodX,package2.ClassB#methodY \
+ *     com.tests.pkg/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
+ *
+ * Those options can be used simultaneously. For example
+ * <pre>
+ * adb shell am instrument -w \
+ *     -e filter com.android.test.filters.SelectTest                        \
+ *     -e selectTest package1.,package2.classA,package3.ClassB#methodZ \
+ *     com.tests.pkg/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
+ * will select out all tests in package1, all tests in classA, and ClassB#methodZ test.
+ *
+ * <p>Note that when this option is specified with either {@code -e package} or {@code -e class}
+ * option, filtering behaves as logically conjunction. Other options, such as {@code -e notPackage},
+ * {@code -e notClass}, {@code -e annotation}, and {@code -e notAnnotation}, should work as expected
+ * with this SelectTest option.
+ *
+ * <p>When specified with {@code -e selectTest_verbose true} option, {@link SelectTest} verbosely
+ * logs to logcat while parsing {@code -e selectTest} option.
+ */
+public class SelectTest extends Filter {
+
+    private static final String TAG = SelectTest.class.getSimpleName();
+
+    @VisibleForTesting
+    static final String OPTION_SELECT_TEST = "selectTest";
+    @VisibleForTesting
+    static final String OPTION_SELECT_TEST_VERBOSE = OPTION_SELECT_TEST + "_verbose";
+
+    private static final String ARGUMENT_ITEM_SEPARATOR = ",";
+    private static final String PACKAGE_NAME_SEPARATOR = ".";
+    private static final String METHOD_SEPARATOR = "#";
+
+    @Nullable
+    private final PackageSet mPackageSet;
+
+    /**
+     * Construct {@link SelectTest} filter from instrumentation arguments in {@link Bundle}.
+     *
+     * @param testArgs instrumentation test arguments.
+     */
+    public SelectTest(@NonNull Bundle testArgs) {
+        mPackageSet = parseSelectTest(testArgs);
+    }
+
+    @Override
+    public boolean shouldRun(Description description) {
+        if (mPackageSet == null) {
+            // Accept all tests because this filter is disabled.
+            return true;
+        }
+        String testClassName = description.getClassName();
+        String testMethodName = description.getMethodName();
+        return mPackageSet.accept(testClassName, testMethodName);
+    }
+
+    @Override
+    public String describe() {
+        return OPTION_SELECT_TEST + "=" + mPackageSet;
+    }
+
+    /**
+     * Create {@link #OPTION_SELECT_TEST} argument and add it to {@code testArgs}.
+     *
+     * <p>This method is intended to be used at constructor of extended {@link Filter} class.
+     *
+     * @param testArgs instrumentation test arguments.
+     * @param selectTests array of class name to be selected to run.
+     * @return modified instrumentation test arguments.
+     */
+    @NonNull
+    protected static Bundle addSelectTest(
+            @NonNull Bundle testArgs, @NonNull String... selectTests) {
+        if (selectTests.length == 0) {
+            return testArgs;
+        }
+        testArgs.putString(OPTION_SELECT_TEST, join(Arrays.asList(selectTests)));
+        return testArgs;
+    }
+
+    /**
+     * Parse {@code -e selectTest} argument.
+     * @param testArgs instrumentation test arguments.
+     * @return {@link PackageSet} that will filter tests. Returns {@code null} when no
+     *     {@code -e selectTest} option is specified, thus this filter gets disabled.
+     */
+    @Nullable
+    private static PackageSet parseSelectTest(Bundle testArgs) {
+        final String selectTestArgs = testArgs.getString(OPTION_SELECT_TEST);
+        if (selectTestArgs == null) {
+            Log.w(TAG, "Disabled because no " + OPTION_SELECT_TEST + " option specified");
+            return null;
+        }
+
+        final boolean verbose = new Boolean(testArgs.getString(OPTION_SELECT_TEST_VERBOSE));
+        final PackageSet packageSet = new PackageSet(verbose);
+        for (String selectTestArg : selectTestArgs.split(ARGUMENT_ITEM_SEPARATOR)) {
+            packageSet.add(selectTestArg);
+        }
+        return packageSet;
+    }
+
+    private static String getPackageName(String selectTestArg) {
+        int endPackagePos = selectTestArg.lastIndexOf(PACKAGE_NAME_SEPARATOR);
+        return (endPackagePos < 0) ? "" : selectTestArg.substring(0, endPackagePos);
+    }
+
+    @Nullable
+    private static String getClassName(String selectTestArg) {
+        if (selectTestArg.endsWith(PACKAGE_NAME_SEPARATOR)) {
+            return null;
+        }
+        int methodSepPos = selectTestArg.indexOf(METHOD_SEPARATOR);
+        return (methodSepPos < 0) ? selectTestArg : selectTestArg.substring(0, methodSepPos);
+    }
+
+    @Nullable
+    private static String getMethodName(String selectTestArg) {
+        int methodSepPos = selectTestArg.indexOf(METHOD_SEPARATOR);
+        return (methodSepPos < 0) ? null : selectTestArg.substring(methodSepPos + 1);
+    }
+
+    /** Package level filter */
+    private static class PackageSet {
+        private final boolean mVerbose;
+        /**
+         * Java package name to {@link ClassSet} map. To represent package filtering, a map value
+         * can be {@code null}.
+         */
+        private final Map<String, ClassSet> mClassSetMap = new LinkedHashMap<>();
+
+        PackageSet(boolean verbose) {
+            mVerbose = verbose;
+        }
+
+        void add(final String selectTestArg) {
+            final String packageName = getPackageName(selectTestArg);
+            final String className = getClassName(selectTestArg);
+
+            if (className == null) {
+                ClassSet classSet = mClassSetMap.put(packageName, null); // package filtering.
+                if (mVerbose) {
+                    logging("Select package " + selectTestArg, classSet != null,
+                            "; supersede " + classSet);
+                }
+                return;
+            }
+
+            ClassSet classSet = mClassSetMap.get(packageName);
+            if (classSet == null) {
+                if (mClassSetMap.containsKey(packageName)) {
+                    if (mVerbose) {
+                        logging("Select package " + packageName + PACKAGE_NAME_SEPARATOR, true,
+                                " ignore " + selectTestArg);
+                    }
+                    return;
+                }
+                classSet = new ClassSet(mVerbose);
+                mClassSetMap.put(packageName, classSet);
+            }
+            classSet.add(selectTestArg);
+        }
+
+        boolean accept(String className, @Nullable String methodName) {
+            String packageName = getPackageName(className);
+            if (!mClassSetMap.containsKey(packageName)) {
+                return false;
+            }
+            ClassSet classSet = mClassSetMap.get(packageName);
+            return classSet == null || classSet.accept(className, methodName);
+        }
+
+        @Override
+        public String toString() {
+            StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR);
+            for (String packageName : mClassSetMap.keySet()) {
+                ClassSet classSet = mClassSetMap.get(packageName);
+                joiner.add(classSet == null
+                        ? packageName + PACKAGE_NAME_SEPARATOR : classSet.toString());
+            }
+            return joiner.toString();
+        }
+    }
+
+    /** Class level filter */
+    private static class ClassSet {
+        private final boolean mVerbose;
+        /**
+         * Java class name to set of method names map. To represent class filtering, a map value
+         * can be {@code null}.
+         */
+        private final Map<String, Set<String>> mMethodSetMap = new LinkedHashMap<>();
+
+        ClassSet(boolean verbose) {
+            mVerbose = verbose;
+        }
+
+        void add(String selectTestArg) {
+            final String className = getClassName(selectTestArg);
+            final String methodName = getMethodName(selectTestArg);
+
+            if (methodName == null) {
+                Set<String> methodSet = mMethodSetMap.put(className, null); // class filtering.
+                if (mVerbose) {
+                    logging("Select class " + selectTestArg, methodSet != null,
+                            "; supersede " + toString(className, methodSet));
+                }
+                return;
+            }
+
+            Set<String> methodSet = mMethodSetMap.get(className);
+            if (methodSet == null) {
+                if (mMethodSetMap.containsKey(className)) {
+                    if (mVerbose) {
+                        logging("Select class " + className, true, "; ignore " + selectTestArg);
+                    }
+                    return;
+                }
+                methodSet = new LinkedHashSet<>();
+                mMethodSetMap.put(className, methodSet);
+            }
+
+            methodSet.add(methodName);
+            if (mVerbose) {
+                logging("Select method " + selectTestArg, false, null);
+            }
+        }
+
+        boolean accept(String className, @Nullable String methodName) {
+            if (!mMethodSetMap.containsKey(className)) {
+                return false;
+            }
+            Set<String> methodSet = mMethodSetMap.get(className);
+            return methodName == null || methodSet == null || methodSet.contains(methodName);
+        }
+
+        @Override
+        public String toString() {
+            StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR);
+            for (String className : mMethodSetMap.keySet()) {
+                joiner.add(toString(className, mMethodSetMap.get(className)));
+            }
+            return joiner.toString();
+        }
+
+        private static String toString(String className, @Nullable Set<String> methodSet) {
+            if (methodSet == null) {
+                return className;
+            }
+            StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR);
+            for (String methodName : methodSet) {
+                joiner.add(className + METHOD_SEPARATOR + methodName);
+            }
+            return joiner.toString();
+        }
+    }
+
+    private static void logging(String infoLog, boolean isWarning, String warningLog) {
+        if (isWarning) {
+            Log.w(TAG, infoLog + warningLog);
+        } else {
+            Log.i(TAG, infoLog);
+        }
+    }
+
+    private static String join(Collection<String> list) {
+        StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR);
+        for (String text : list) {
+            joiner.add(text);
+        }
+        return joiner.toString();
+    }
+}
diff --git a/tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java b/tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java
new file mode 100644
index 0000000..a6b0102
--- /dev/null
+++ b/tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.filters;
+
+import static com.android.test.filters.SelectTest.OPTION_SELECT_TEST;
+import static com.android.test.filters.SelectTest.OPTION_SELECT_TEST_VERBOSE;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Bundle;
+import android.util.ArraySet;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.manipulation.Filter;
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.StringJoiner;
+
+public class SelectTestTests {
+
+    private static final String PACKAGE_A = "packageA.";
+    private static final String PACKAGE_B = "packageB.";
+    private static final String PACKAGE_C = "packageC.";
+    private static final String CLASS_A1 = PACKAGE_A + "Class1";
+    private static final String CLASS_A2 = PACKAGE_A + "Class2";
+    private static final String CLASS_B3 = PACKAGE_B + "Class3";
+    private static final String CLASS_B4 = PACKAGE_B + "Class4";
+    private static final String CLASS_C5 = PACKAGE_C + "Class5";
+    private static final String CLASS_C6 = PACKAGE_C + "Class6";
+    private static final String METHOD_A1K = CLASS_A1 + "#methodK";
+    private static final String METHOD_A1L = CLASS_A1 + "#methodL";
+    private static final String METHOD_A2M = CLASS_A2 + "#methodM";
+    private static final String METHOD_A2N = CLASS_A2 + "#methodN";
+    private static final String METHOD_B3P = CLASS_B3 + "#methodP";
+    private static final String METHOD_B3Q = CLASS_B3 + "#methodQ";
+    private static final String METHOD_B4R = CLASS_B4 + "#methodR";
+    private static final String METHOD_B4S = CLASS_B4 + "#methodS";
+    private static final String METHOD_C5W = CLASS_C5 + "#methodW";
+    private static final String METHOD_C5X = CLASS_C5 + "#methodX";
+    private static final String METHOD_C6Y = CLASS_C6 + "#methodY";
+    private static final String METHOD_C6Z = CLASS_C6 + "#methodZ";
+
+    private static final Set<Description> TEST_METHOD_A1K = methodTest(METHOD_A1K);
+    private static final Set<Description> TEST_METHOD_A1L = methodTest(METHOD_A1L);
+    private static final Set<Description> TEST_METHOD_A2M = methodTest(METHOD_A2M);
+    private static final Set<Description> TEST_METHOD_A2N = methodTest(METHOD_A2N);
+    private static final Set<Description> TEST_METHOD_B3P = methodTest(METHOD_B3P);
+    private static final Set<Description> TEST_METHOD_B3Q = methodTest(METHOD_B3Q);
+    private static final Set<Description> TEST_METHOD_B4R = methodTest(METHOD_B4R);
+    private static final Set<Description> TEST_METHOD_B4S = methodTest(METHOD_B4S);
+    private static final Set<Description> TEST_METHOD_C5W = methodTest(METHOD_C5W);
+    private static final Set<Description> TEST_METHOD_C5X = methodTest(METHOD_C5X);
+    private static final Set<Description> TEST_METHOD_C6Y = methodTest(METHOD_C6Y);
+    private static final Set<Description> TEST_METHOD_C6Z = methodTest(METHOD_C6Z);
+    private static final Set<Description> TEST_CLASS_A1 = merge(TEST_METHOD_A1K, TEST_METHOD_A1L);
+    private static final Set<Description> TEST_CLASS_A2 = merge(TEST_METHOD_A2M, TEST_METHOD_A2N);
+    private static final Set<Description> TEST_CLASS_B3 = merge(TEST_METHOD_B3P, TEST_METHOD_B3Q);
+    private static final Set<Description> TEST_CLASS_B4 = merge(TEST_METHOD_B4R, TEST_METHOD_B4S);
+    private static final Set<Description> TEST_CLASS_C5 = merge(TEST_METHOD_C5W, TEST_METHOD_C5X);
+    private static final Set<Description> TEST_CLASS_C6 = merge(TEST_METHOD_C6Y, TEST_METHOD_C6Z);
+    private static final Set<Description> TEST_PACKAGE_A = merge(TEST_CLASS_A1, TEST_CLASS_A2);
+    private static final Set<Description> TEST_PACKAGE_B = merge(TEST_CLASS_B3, TEST_CLASS_B4);
+    private static final Set<Description> TEST_PACKAGE_C = merge(TEST_CLASS_C5, TEST_CLASS_C6);
+    private static final Set<Description> TEST_ALL =
+            merge(TEST_PACKAGE_A, TEST_PACKAGE_B, TEST_PACKAGE_C);
+
+    private SelectTestBuilder mBuilder;
+
+    @Before
+    public void setUp() {
+        mBuilder = new SelectTestBuilder();
+    }
+
+    private static class SelectTestBuilder {
+        private final Bundle mTestArgs = new Bundle();
+
+        Filter build() {
+            mTestArgs.putString(OPTION_SELECT_TEST_VERBOSE, Boolean.TRUE.toString());
+            return new SelectTest(mTestArgs);
+        }
+
+        SelectTestBuilder withSelectTest(String... selectTestArgs) {
+            putTestOption(OPTION_SELECT_TEST, selectTestArgs);
+            return this;
+        }
+
+        private void putTestOption(String option, String... args) {
+            if (args.length > 0) {
+                StringJoiner joiner = new StringJoiner(",");
+                for (String arg : args) {
+                    joiner.add(arg);
+                }
+                mTestArgs.putString(option, joiner.toString());
+            }
+        }
+    }
+
+    private static Set<Description> methodTest(String testName) {
+        int methodSep = testName.indexOf("#");
+        String className = testName.substring(0, methodSep);
+        String methodName = testName.substring(methodSep + 1);
+        final Set<Description> tests = new ArraySet<>();
+        tests.add(Description.createSuiteDescription(className));
+        tests.add(Description.createTestDescription(className, methodName));
+        return Collections.unmodifiableSet(tests);
+    }
+
+    @SafeVarargs
+    private static Set<Description> merge(Set<Description>... testSpecs) {
+        final Set<Description> merged = new LinkedHashSet<>();
+        for (Set<Description> testSet : testSpecs) {
+            merged.addAll(testSet);
+        }
+        return Collections.unmodifiableSet(merged);
+    }
+
+    @SafeVarargs
+    private static void acceptTests(Filter filter, Set<Description>... testSpecs) {
+        final Set<Description> accepts = merge(testSpecs);
+        for (Description test : TEST_ALL) {
+            if (accepts.contains(test)) {
+                assertTrue("accept " + test, filter.shouldRun(test));
+            } else {
+                assertFalse("reject " + test, filter.shouldRun(test));
+            }
+        }
+    }
+
+    @Test
+    public void testFilterDisabled() {
+        final Filter filter = mBuilder.build();
+        acceptTests(filter, TEST_ALL);
+    }
+
+    @Test
+    public void testSelectPackage() {
+        final Filter filter = mBuilder.withSelectTest(PACKAGE_A, PACKAGE_B).build();
+        acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_B);
+    }
+
+    @Test
+    public void testSelectClass() {
+        final Filter filter = mBuilder.withSelectTest(CLASS_A1, CLASS_A2, CLASS_B3).build();
+        acceptTests(filter, TEST_CLASS_A1, TEST_CLASS_A2, TEST_CLASS_B3);
+    }
+
+    @Test
+    public void testSelectMethod() {
+        final Filter filter = mBuilder
+                .withSelectTest(METHOD_A1K, METHOD_A2M, METHOD_A2N, METHOD_B3P).build();
+        acceptTests(filter, TEST_METHOD_A1K, TEST_METHOD_A2M, TEST_METHOD_A2N, TEST_METHOD_B3P);
+    }
+
+    @Test
+    public void testSelectClassAndPackage() {
+        final Filter filter = mBuilder.withSelectTest(CLASS_A1, PACKAGE_B, CLASS_C5).build();
+        acceptTests(filter, TEST_CLASS_A1, TEST_PACKAGE_B, TEST_CLASS_C5);
+    }
+
+    @Test
+    public void testSelectMethodAndPackage() {
+        final Filter filter = mBuilder.withSelectTest(METHOD_A1K, PACKAGE_B, METHOD_C5W).build();
+        acceptTests(filter, TEST_METHOD_A1K, TEST_PACKAGE_B, TEST_METHOD_C5W);
+    }
+
+    @Test
+    public void testSelectMethodAndClass() {
+        final Filter filter = mBuilder.withSelectTest(METHOD_A1K, CLASS_C5, METHOD_B3P).build();
+        acceptTests(filter, TEST_METHOD_A1K, TEST_CLASS_C5, TEST_METHOD_B3P);
+    }
+
+    @Test
+    public void testSelectClassAndSamePackage() {
+        final Filter filter = mBuilder.withSelectTest(CLASS_A1, CLASS_A2, PACKAGE_A,
+                CLASS_C5, CLASS_C6, PACKAGE_C).build();
+        acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_C);
+    }
+
+    @Test
+    public void testSelectMethodAndSameClass() {
+        final Filter filter = mBuilder.withSelectTest(METHOD_A1K, METHOD_A1L, METHOD_A2M, CLASS_A1,
+                CLASS_B3, METHOD_B3P, METHOD_B3Q, METHOD_B4R).build();
+        acceptTests(filter, TEST_CLASS_A1, TEST_METHOD_A2M, TEST_CLASS_B3, TEST_METHOD_B4R);
+    }
+
+    @Test
+    public void testSelectMethodAndSamePackage() {
+        final Filter filter = mBuilder.withSelectTest(METHOD_A1K, METHOD_A1L, METHOD_A2M, PACKAGE_A,
+                PACKAGE_C, METHOD_C5W, METHOD_C5X, METHOD_C6Y).build();
+        acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_C);
+    }
+
+    @Test
+    public void testSelectMethodAndClassAndPackage() {
+        final Filter filter = mBuilder.withSelectTest(
+                METHOD_A1K, CLASS_A1, METHOD_A1L, METHOD_A2M, PACKAGE_A,
+                PACKAGE_B, METHOD_B3Q, CLASS_B3, METHOD_B4R, METHOD_B3P).build();
+        acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_B);
+    }
+}