Merge changes If3a6eeb3,I2a29dc60

* changes:
  Factor out code for loading and saving rollback data.
  Store meta data for a rollback in a single file.
diff --git a/api/current.txt b/api/current.txt
index 96508c7..9053a00 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21650,7 +21650,7 @@
     ctor public JapaneseCalendar(int, int, int, int);
     ctor public JapaneseCalendar(int, int, int);
     ctor public JapaneseCalendar(int, int, int, int, int, int);
-    field public static final int CURRENT_ERA;
+    field @Deprecated public static final int CURRENT_ERA;
     field public static final int HEISEI;
     field public static final int MEIJI;
     field public static final int SHOWA;
@@ -27426,15 +27426,24 @@
   public final class MediaSessionManager {
     method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName);
     method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, @Nullable android.os.Handler);
+    method public void addOnSession2TokensChangedListener(@NonNull android.media.session.MediaSessionManager.OnSession2TokensChangedListener);
+    method public void addOnSession2TokensChangedListener(@NonNull android.media.session.MediaSessionManager.OnSession2TokensChangedListener, @NonNull android.os.Handler);
     method @NonNull public java.util.List<android.media.session.MediaController> getActiveSessions(@Nullable android.content.ComponentName);
+    method @NonNull public java.util.List<android.media.Session2Token> getSession2Tokens();
     method public boolean isTrustedForMediaControl(@NonNull android.media.session.MediaSessionManager.RemoteUserInfo);
+    method public void notifySession2Created(@NonNull android.media.Session2Token);
     method public void removeOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener);
+    method public void removeOnSession2TokensChangedListener(@NonNull android.media.session.MediaSessionManager.OnSession2TokensChangedListener);
   }
 
   public static interface MediaSessionManager.OnActiveSessionsChangedListener {
     method public void onActiveSessionsChanged(@Nullable java.util.List<android.media.session.MediaController>);
   }
 
+  public static interface MediaSessionManager.OnSession2TokensChangedListener {
+    method public void onSession2TokensChanged(@NonNull java.util.List<android.media.Session2Token>);
+  }
+
   public static final class MediaSessionManager.RemoteUserInfo {
     ctor public MediaSessionManager.RemoteUserInfo(@NonNull String, int, int);
     ctor public MediaSessionManager.RemoteUserInfo(String, int, int, android.os.IBinder);
@@ -34864,10 +34873,13 @@
     method public static final void setThreadPriority(int, int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
     method public static final void setThreadPriority(int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
     method @Deprecated public static final boolean supportsProcesses();
+    field public static final int BLUETOOTH_UID = 1002; // 0x3ea
     field public static final int FIRST_APPLICATION_UID = 10000; // 0x2710
     field public static final int INVALID_UID = -1; // 0xffffffff
     field public static final int LAST_APPLICATION_UID = 19999; // 0x4e1f
     field public static final int PHONE_UID = 1001; // 0x3e9
+    field public static final int ROOT_UID = 0; // 0x0
+    field public static final int SHELL_UID = 2000; // 0x7d0
     field public static final int SIGNAL_KILL = 9; // 0x9
     field public static final int SIGNAL_QUIT = 3; // 0x3
     field public static final int SIGNAL_USR1 = 10; // 0xa
diff --git a/api/system-current.txt b/api/system-current.txt
index 2088eaf..8cde52b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3962,14 +3962,30 @@
 
   public final class LinkProperties implements android.os.Parcelable {
     ctor public LinkProperties();
+    method public boolean addDnsServer(java.net.InetAddress);
     method public boolean addRoute(android.net.RouteInfo);
     method public void clear();
+    method public String getTcpBufferSizes();
+    method public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
+    method public boolean hasGlobalIPv6Address();
+    method public boolean hasIPv4Address();
+    method public boolean hasIPv6DefaultRoute();
+    method public boolean isIPv4Provisioned();
+    method public boolean isIPv6Provisioned();
+    method public boolean isProvisioned();
+    method public boolean isReachable(java.net.InetAddress);
+    method public boolean removeDnsServer(java.net.InetAddress);
+    method public boolean removeRoute(android.net.RouteInfo);
     method public void setDnsServers(java.util.Collection<java.net.InetAddress>);
     method public void setDomains(String);
     method public void setHttpProxy(android.net.ProxyInfo);
     method public void setInterfaceName(String);
     method public void setLinkAddresses(java.util.Collection<android.net.LinkAddress>);
     method public void setMtu(int);
+    method public void setPrivateDnsServerName(@Nullable String);
+    method public void setTcpBufferSizes(String);
+    method public void setUsePrivateDns(boolean);
+    method public void setValidatedPrivateDnsServers(java.util.Collection<java.net.InetAddress>);
   }
 
   public class Network implements android.os.Parcelable {
@@ -3978,6 +3994,8 @@
 
   public final class NetworkCapabilities implements android.os.Parcelable {
     method public int getSignalStrength();
+    method public int[] getTransportTypes();
+    method public boolean satisfiedByNetworkCapabilities(android.net.NetworkCapabilities);
     field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
   }
 
@@ -4153,6 +4171,7 @@
   public class IpConnectivityLog {
     method public boolean log(long, android.net.metrics.IpConnectivityLog.Event);
     method public boolean log(String, android.net.metrics.IpConnectivityLog.Event);
+    method public boolean log(android.net.Network, int[], android.net.metrics.IpConnectivityLog.Event);
     method public boolean log(int, int[], android.net.metrics.IpConnectivityLog.Event);
     method public boolean log(android.net.metrics.IpConnectivityLog.Event);
   }
@@ -4220,6 +4239,17 @@
 
 }
 
+package android.net.util {
+
+  public class SocketUtils {
+    method public static void bindSocketToInterface(java.io.FileDescriptor, String) throws android.system.ErrnoException;
+    method public static java.net.SocketAddress makeNetlinkSocketAddress(int, int);
+    method public static java.net.SocketAddress makePacketSocketAddress(short, int);
+    method public static java.net.SocketAddress makePacketSocketAddress(int, byte[]);
+  }
+
+}
+
 package android.net.wifi {
 
   public abstract class EasyConnectStatusCallback {
@@ -5053,6 +5083,12 @@
     method public void onResult(android.os.Bundle);
   }
 
+  public class ServiceSpecificException extends java.lang.RuntimeException {
+    ctor public ServiceSpecificException(int, String);
+    ctor public ServiceSpecificException(int);
+    field public final int errorCode;
+  }
+
   public final class StatsDimensionsValue implements android.os.Parcelable {
     method public int describeContents();
     method public boolean getBooleanValue();
@@ -9096,6 +9132,10 @@
     field public final android.content.pm.Signature[] signatures;
   }
 
+  public abstract class WebViewRenderer {
+    ctor public WebViewRenderer();
+  }
+
   public final class WebViewUpdateService {
     method public static android.webkit.WebViewProviderInfo[] getAllWebViewPackages();
     method public static String getCurrentWebViewPackageName();
diff --git a/api/test-current.txt b/api/test-current.txt
index 0d88937..26fe204 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -802,6 +802,25 @@
     method public boolean isSameAddressAs(android.net.LinkAddress);
   }
 
+  public final class LinkProperties implements android.os.Parcelable {
+    method public boolean addDnsServer(java.net.InetAddress);
+    method public String getTcpBufferSizes();
+    method public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
+    method public boolean hasGlobalIPv6Address();
+    method public boolean hasIPv4Address();
+    method public boolean hasIPv6DefaultRoute();
+    method public boolean isIPv4Provisioned();
+    method public boolean isIPv6Provisioned();
+    method public boolean isProvisioned();
+    method public boolean isReachable(java.net.InetAddress);
+    method public boolean removeDnsServer(java.net.InetAddress);
+    method public boolean removeRoute(android.net.RouteInfo);
+    method public void setPrivateDnsServerName(@Nullable String);
+    method public void setTcpBufferSizes(String);
+    method public void setUsePrivateDns(boolean);
+    method public void setValidatedPrivateDnsServers(java.util.Collection<java.net.InetAddress>);
+  }
+
   public class Network implements android.os.Parcelable {
     method public android.net.Network getPrivateDnsBypassingCopy();
   }
@@ -809,6 +828,7 @@
   public final class NetworkCapabilities implements android.os.Parcelable {
     method public int[] getCapabilities();
     method public int[] getTransportTypes();
+    method public boolean satisfiedByNetworkCapabilities(android.net.NetworkCapabilities);
   }
 
   public final class RouteInfo implements android.os.Parcelable {
@@ -900,6 +920,7 @@
   public class IpConnectivityLog {
     method public boolean log(long, android.net.metrics.IpConnectivityLog.Event);
     method public boolean log(String, android.net.metrics.IpConnectivityLog.Event);
+    method public boolean log(android.net.Network, int[], android.net.metrics.IpConnectivityLog.Event);
     method public boolean log(int, int[], android.net.metrics.IpConnectivityLog.Event);
     method public boolean log(android.net.metrics.IpConnectivityLog.Event);
   }
diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc
index 469c964..1b3c32b 100644
--- a/cmds/bootanimation/bootanim.rc
+++ b/cmds/bootanimation/bootanim.rc
@@ -2,6 +2,7 @@
     class core animation
     user graphics
     group graphics audio
+    updatable
     disabled
     oneshot
     writepid /dev/stune/top-app/tasks
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index f8825ac..79ef2ca 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -194,6 +194,14 @@
         GnssNfwNotificationReported gnss_nfw_notification_reported = 131;
         GnssConfigurationReported gnss_configuration_reported = 132;
         UsbPortOverheatEvent usb_port_overheat_event_reported = 133;
+        NfcErrorOccurred nfc_error_occurred = 134;
+        NfcStateChanged nfc_state_changed = 135;
+        NfcBeamOccurred nfc_beam_occurred = 136;
+        NfcCardemulationOccurred nfc_cardemulation_occurred = 137;
+        NfcTagOccurred nfc_tag_occurred = 138;
+        NfcHceTransactionOccurred nfc_hce_transaction_occurred = 139;
+        SeStateChanged se_state_changed = 140;
+        SeOmapiReported se_omapi_reported = 141;
     }
 
     // Pulled events will start at field 10000.
@@ -4309,3 +4317,138 @@
     // with spaces as separators.
     optional string enabled_proxy_app_package_name_list = 13;
 }
+
+/**
+ * Logs when a NFC device's error occurred.
+ * Logged from:
+ *     system/nfc/src/nfc/nfc/nfc_ncif.cc
+ *     packages/apps/Nfc/src/com/android/nfc/cardemulation/AidRoutingManager.java
+ */
+message NfcErrorOccurred {
+  enum Type {
+    UNKNOWN = 0;
+    CMD_TIMEOUT = 1;
+    ERROR_NOTIFICATION = 2;
+    AID_OVERFLOW = 3;
+  }
+  optional Type type = 1;
+  // If it's nci cmd timeout, log the timeout command.
+  optional uint32 nci_cmd = 2;
+
+  optional uint32 error_ntf_status_code = 3;
+}
+
+/**
+ * Logs when a NFC device's state changed event
+ * Logged from:
+ *     packages/apps/Nfc/src/com/android/nfc/NfcService.java
+ */
+message NfcStateChanged {
+  enum State {
+    UNKNOWN = 0;
+    OFF = 1;
+    ON = 2;
+    ON_LOCKED = 3; // Secure Nfc enabled.
+    CRASH_RESTART = 4; // NfcService watchdog timeout restart.
+  }
+  optional State state = 1;
+}
+
+/**
+ * Logs when a NFC Beam Transaction occurred.
+ * Logged from:
+ *     packages/apps/Nfc/src/com/android/nfc/P2pLinkManager.java
+ */
+message NfcBeamOccurred {
+  enum Operation {
+      UNKNOWN = 0;
+      SEND = 1;
+      RECEIVE = 2;
+  }
+  optional Operation operation = 1;
+}
+
+/**
+ * Logs when a NFC Card Emulation Transaction occurred.
+ * Logged from:
+ *     packages/apps/Nfc/src/com/android/nfc/cardemulation/HostEmulationManager.java
+ *     packages/apps/Nfc/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java
+ */
+message NfcCardemulationOccurred {
+  enum Category {
+      UNKNOWN = 0;
+      HCE_PAYMENT = 1;
+      HCE_OTHER = 2;
+      OFFHOST = 3;
+  }
+  // Transaction belongs to HCE payment or HCE other category, or offhost.
+  optional Category category = 1;
+  // SeName from transaction: SIMx, eSEx, HCE, HCEF.
+  optional string se_name = 2;
+}
+
+/**
+ * Logs when a NFC Tag event occurred.
+ * Logged from:
+ *     packages/apps/Nfc/src/com/android/nfc/NfcDispatcher.java
+ */
+message NfcTagOccurred {
+  enum Type {
+      UNKNOWN = 0;
+      URL = 1;
+      BT_PAIRING = 2;
+      PROVISION = 3;
+      WIFI_CONNECT = 4;
+      APP_LAUNCH = 5;
+      OTHERS = 6;
+  }
+  optional Type type = 1;
+}
+
+/**
+ * Logs when Hce transaction triggered
+ * Logged from:
+ *     system/nfc/src/nfc/nfc/nfc_ncif.cc
+ */
+message NfcHceTransactionOccurred {
+    // The latency period(in microseconds) it took for the first HCE data
+    // exchange.
+    optional uint32 latency_micros = 1;
+}
+
+/**
+ * Logs when SecureElement state event changed
+ * Logged from:
+ *     packages/apps/SecureElement/src/com/android/se/Terminal.java
+ */
+message SeStateChanged {
+  enum State {
+    UNKNOWN = 0;
+    INITIALIZED = 1;
+    DISCONNECTED = 2;
+    CONNECTED = 3;
+    HALCRASH = 4;
+  }
+  optional State state = 1;
+
+  optional string state_change_reason = 2;
+  // SIMx or eSEx.
+  optional string terminal = 3;
+}
+
+/**
+ * Logs when Omapi API used
+ * Logged from:
+ *     packages/apps/SecureElement/src/com/android/se/Terminal.java
+ */
+message SeOmapiReported {
+  enum Operation {
+    UNKNOWN = 0;
+    OPEN_CHANNEL = 1;
+  }
+  optional Operation operation = 1;
+  // SIMx or eSEx.
+  optional string terminal = 2;
+
+  optional string packageName = 3;
+}
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 700df64..3a4d904 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -921,7 +921,6 @@
 Landroid/os/PowerManager;->mService:Landroid/os/IPowerManager;
 Landroid/os/PowerManager;->validateWakeLockParameters(ILjava/lang/String;)V
 Landroid/os/PowerManager;->wakeUp(JLjava/lang/String;)V
-Landroid/os/Process;->BLUETOOTH_UID:I
 Landroid/os/Process;->DRM_UID:I
 Landroid/os/Process;->getFreeMemory()J
 Landroid/os/Process;->getParentPid(I)I
@@ -948,10 +947,8 @@
 Landroid/os/Process;->PROC_ZERO_TERM:I
 Landroid/os/Process;->readProcFile(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z
 Landroid/os/Process;->readProcLines(Ljava/lang/String;[Ljava/lang/String;[J)V
-Landroid/os/Process;->ROOT_UID:I
 Landroid/os/Process;->setArgV0(Ljava/lang/String;)V
 Landroid/os/Process;->setProcessGroup(II)V
-Landroid/os/Process;->SHELL_UID:I
 Landroid/os/Process;->VPN_UID:I
 Landroid/os/Process;->WIFI_UID:I
 Landroid/os/RecoverySystem;->verifyPackageCompatibility(Ljava/io/InputStream;)Z
@@ -993,8 +990,6 @@
 Landroid/os/ServiceManagerNative;->asInterface(Landroid/os/IBinder;)Landroid/os/IServiceManager;
 Landroid/os/ServiceManagerProxy;->getService(Ljava/lang/String;)Landroid/os/IBinder;
 Landroid/os/ServiceManagerProxy;->mRemote:Landroid/os/IBinder;
-Landroid/os/ServiceSpecificException;-><init>(ILjava/lang/String;)V
-Landroid/os/ServiceSpecificException;->errorCode:I
 Landroid/os/SharedMemory;->getFd()I
 Landroid/os/ShellCommand;->peekNextArg()Ljava/lang/String;
 Landroid/os/StatFs;->mStat:Landroid/system/StructStatVfs;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6d7c547..86db99bf 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9403,12 +9403,18 @@
     }
 
     /**
-     * Allows the device owner to enable or disable the backup service.
+     * Allows the device owner or profile owner to enable or disable the backup service.
      *
-     * <p> Backup service manages all backup and restore mechanisms on the device. Setting this to
-     * false will prevent data from being backed up or restored.
+     * <p> Each user has its own backup service which manages the backup and restore mechanisms in
+     * that user. Disabling the backup service will prevent data from being backed up or restored.
      *
-     * <p> Backup service is off by default when device owner is present.
+     * <p> Device owner calls this API to control backup services across all users on the device.
+     * Profile owner can use this API to enable or disable the profile's backup service. However,
+     * for a managed profile its backup functionality is only enabled if both the device owner
+     * and the profile owner have enabled the backup service.
+     *
+     * <p> By default, backup service is disabled on a device with device owner, and within a
+     * managed profile.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param enabled {@code true} to enable the backup service, {@code false} to disable it.
@@ -9424,7 +9430,12 @@
     }
 
     /**
-     * Return whether the backup service is enabled by the device owner.
+     * Return whether the backup service is enabled by the device owner or profile owner for the
+     * current user, as previously set by {@link #setBackupServiceEnabled(ComponentName, boolean)}.
+     *
+     * <p> Whether the backup functionality is actually enabled or not depends on settings from both
+     * the current user and the device owner, please see
+     * {@link #setBackupServiceEnabled(ComponentName, boolean)} for details.
      *
      * <p> Backup service manages all backup and restore mechanisms on the device.
      *
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 983ea9f..766c566 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -136,6 +136,17 @@
     public static final String EXTRA_PIN_ITEM_REQUEST =
             "android.content.pm.extra.PIN_ITEM_REQUEST";
 
+    /**
+     * Metadata key that specifies vouched certs, so any apps signed by a cert in vouched certs
+     * will not show hidden icon in launcher even it does not have a launcher visible activity.
+     *
+     * If an app has this metadata in manifest, it won't be eligible to hide its icon even if its
+     * cert is in vouched certs list.
+     *
+     * @hide
+     */
+    public static final String VOUCHED_CERTS_KEY = "vouched_certs";
+
     private final Context mContext;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final ILauncherApps mService;
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index a185c8a..88a240f 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -8494,6 +8494,18 @@
     public static PackageInfo generatePackageInfoFromApex(File apexFile, boolean collectCerts)
             throws PackageParserException {
         PackageInfo pi = new PackageInfo();
+
+        // TODO(b/123086053) properly fill in the ApplicationInfo with data from AndroidManifest
+        // Add ApplicationInfo to the PackageInfo.
+        ApplicationInfo ai = new ApplicationInfo();
+        ai.sourceDir = apexFile.getPath();
+        ai.flags = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
+        ai.enabled = true;
+        ai.targetSdkVersion = 28;
+        ai.targetSandboxVersion = 0;
+        pi.applicationInfo = ai;
+
+
         // TODO(b/123052859): We should avoid these repeated calls to parseApkLite each time
         // we want to generate information for APEX modules.
         PackageParser.ApkLite apk = PackageParser.parseApkLite(apexFile,
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index c2963fd..21b6a8e 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
@@ -368,7 +369,8 @@
      * @return true if the DNS server was added, false if it was already present.
      * @hide
      */
-    @UnsupportedAppUsage
+    @TestApi
+    @SystemApi
     public boolean addDnsServer(InetAddress dnsServer) {
         if (dnsServer != null && !mDnses.contains(dnsServer)) {
             mDnses.add(dnsServer);
@@ -384,7 +386,8 @@
      * @return true if the DNS server was removed, false if it did not exist.
      * @hide
      */
-    @UnsupportedAppUsage
+    @TestApi
+    @SystemApi
     public boolean removeDnsServer(InetAddress dnsServer) {
         if (dnsServer != null) {
             return mDnses.remove(dnsServer);
@@ -423,6 +426,8 @@
      * @param usePrivateDns The private DNS state.
      * @hide
      */
+    @TestApi
+    @SystemApi
     public void setUsePrivateDns(boolean usePrivateDns) {
         mUsePrivateDns = usePrivateDns;
     }
@@ -448,6 +453,8 @@
      * @param privateDnsServerName The private DNS server name.
      * @hide
      */
+    @TestApi
+    @SystemApi
     public void setPrivateDnsServerName(@Nullable String privateDnsServerName) {
         mPrivateDnsServerName = privateDnsServerName;
     }
@@ -510,6 +517,8 @@
      *        object.
      * @hide
      */
+    @TestApi
+    @SystemApi
     public void setValidatedPrivateDnsServers(Collection<InetAddress> dnsServers) {
         mValidatedPrivateDnses.clear();
         for (InetAddress dnsServer: dnsServers) {
@@ -525,6 +534,8 @@
      *         DNS servers on this link.
      * @hide
      */
+    @TestApi
+    @SystemApi
     public List<InetAddress> getValidatedPrivateDnsServers() {
         return Collections.unmodifiableList(mValidatedPrivateDnses);
     }
@@ -636,7 +647,8 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @TestApi
+    @SystemApi
     public void setTcpBufferSizes(String tcpBufferSizes) {
         mTcpBufferSizes = tcpBufferSizes;
     }
@@ -648,7 +660,8 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @TestApi
+    @SystemApi
     public String getTcpBufferSizes() {
         return mTcpBufferSizes;
     }
@@ -699,7 +712,8 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
+    @TestApi
+    @SystemApi
     public boolean removeRoute(RouteInfo route) {
         return route != null &&
                 Objects.equals(mIfaceName, route.getInterface()) &&
@@ -960,7 +974,8 @@
      * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
      * @hide
      */
-    @UnsupportedAppUsage
+    @TestApi
+    @SystemApi
     public boolean hasIPv4Address() {
         for (LinkAddress address : mLinkAddresses) {
             if (address.getAddress() instanceof Inet4Address) {
@@ -988,7 +1003,8 @@
      * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise.
      * @hide
      */
-    @UnsupportedAppUsage
+    @TestApi
+    @SystemApi
     public boolean hasGlobalIPv6Address() {
         for (LinkAddress address : mLinkAddresses) {
           if (address.getAddress() instanceof Inet6Address && address.isGlobalPreferred()) {
@@ -1020,7 +1036,8 @@
      * @return {@code true} if there is an IPv6 default route, {@code false} otherwise.
      * @hide
      */
-    @UnsupportedAppUsage
+    @TestApi
+    @SystemApi
     public boolean hasIPv6DefaultRoute() {
         for (RouteInfo r : mRoutes) {
             if (r.isIPv6Default()) {
@@ -1099,6 +1116,8 @@
      * @return {@code true} if the link is provisioned, {@code false} otherwise.
      * @hide
      */
+    @TestApi
+    @SystemApi
     public boolean isIPv4Provisioned() {
         return (hasIPv4Address() &&
                 hasIPv4DefaultRoute() &&
@@ -1112,7 +1131,8 @@
      * @return {@code true} if the link is provisioned, {@code false} otherwise.
      * @hide
      */
-    @UnsupportedAppUsage
+    @TestApi
+    @SystemApi
     public boolean isIPv6Provisioned() {
         return (hasGlobalIPv6Address() &&
                 hasIPv6DefaultRoute() &&
@@ -1126,7 +1146,8 @@
      * @return {@code true} if the link is provisioned, {@code false} otherwise.
      * @hide
      */
-    @UnsupportedAppUsage
+    @TestApi
+    @SystemApi
     public boolean isProvisioned() {
         return (isIPv4Provisioned() || isIPv6Provisioned());
     }
@@ -1138,7 +1159,8 @@
      *         {@code false} otherwise.
      * @hide
      */
-    @UnsupportedAppUsage
+    @TestApi
+    @SystemApi
     public boolean isReachable(InetAddress ip) {
         final List<RouteInfo> allRoutes = getAllRoutes();
         // If we don't have a route to this IP address, it's not reachable.
diff --git a/core/java/android/net/LinkPropertiesParcelable.aidl b/core/java/android/net/LinkPropertiesParcelable.aidl
index b153dc7..6b52239 100644
--- a/core/java/android/net/LinkPropertiesParcelable.aidl
+++ b/core/java/android/net/LinkPropertiesParcelable.aidl
@@ -35,5 +35,4 @@
     int mtu;
     String tcpBufferSizes;
     IpPrefixParcelable nat64Prefix;
-    LinkPropertiesParcelable[] stackedLinks;
 }
\ No newline at end of file
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 1b44c92..7e9bda1 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -712,6 +712,7 @@
      * @hide
      */
     @TestApi
+    @SystemApi
     public @Transport int[] getTransportTypes() {
         return BitUtils.unpackBits(mTransportTypes);
     }
@@ -1312,6 +1313,8 @@
      *
      * @hide
      */
+    @TestApi
+    @SystemApi
     public boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc) {
         return satisfiedByNetworkCapabilities(nc, false);
     }
diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java
index c04fcdc..16aea31b 100644
--- a/core/java/android/net/metrics/IpConnectivityLog.java
+++ b/core/java/android/net/metrics/IpConnectivityLog.java
@@ -21,6 +21,7 @@
 import android.annotation.UnsupportedAppUsage;
 import android.net.ConnectivityMetricsEvent;
 import android.net.IIpConnectivityMetrics;
+import android.net.Network;
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -128,6 +129,18 @@
 
     /**
      * Log an IpConnectivity event.
+     * @param network the network associated with the event.
+     * @param transports the current transports of the network associated with the event, as defined
+     * in NetworkCapabilities.
+     * @param data is a Parcelable instance representing the event.
+     * @return true if the event was successfully logged.
+     */
+    public boolean log(Network network, int[] transports, Event data) {
+        return log(network.netId, transports, data);
+    }
+
+    /**
+     * Log an IpConnectivity event.
      * @param netid the id of the network associated with the event.
      * @param transports the current transports of the network associated with the event, as defined
      * in NetworkCapabilities.
diff --git a/core/java/android/net/util/SocketUtils.java b/core/java/android/net/util/SocketUtils.java
new file mode 100644
index 0000000..de67cf5
--- /dev/null
+++ b/core/java/android/net/util/SocketUtils.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 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.util;
+
+import static android.system.OsConstants.SOL_SOCKET;
+import static android.system.OsConstants.SO_BINDTODEVICE;
+
+import android.annotation.SystemApi;
+import android.net.NetworkUtils;
+import android.system.ErrnoException;
+import android.system.NetlinkSocketAddress;
+import android.system.Os;
+import android.system.PacketSocketAddress;
+
+import java.io.FileDescriptor;
+import java.net.SocketAddress;
+
+/**
+ * Collection of utilities to interact with raw sockets.
+ * @hide
+ */
+@SystemApi
+public class SocketUtils {
+    /**
+     * Create a raw datagram socket that is bound to an interface.
+     *
+     * <p>Data sent through the socket will go directly to the underlying network, ignoring VPNs.
+     */
+    public static void bindSocketToInterface(FileDescriptor socket, String iface)
+            throws ErrnoException {
+        // SO_BINDTODEVICE actually takes a string. This works because the first member
+        // of struct ifreq is a NULL-terminated interface name.
+        // TODO: add a setsockoptString()
+        Os.setsockoptIfreq(socket, SOL_SOCKET, SO_BINDTODEVICE, iface);
+        NetworkUtils.protectFromVpn(socket);
+    }
+
+    /**
+     * Make a socket address to communicate with netlink.
+     */
+    public static SocketAddress makeNetlinkSocketAddress(int portId, int groupsMask) {
+        return new NetlinkSocketAddress(portId, groupsMask);
+    }
+
+    /**
+     * Make a socket address to bind to packet sockets.
+     */
+    public static SocketAddress makePacketSocketAddress(short protocol, int ifIndex) {
+        return new PacketSocketAddress(protocol, ifIndex);
+    }
+
+    /**
+     * Make a socket address to send raw packets.
+     */
+    public static SocketAddress makePacketSocketAddress(int ifIndex, byte[] hwAddr) {
+        return new PacketSocketAddress(ifIndex, hwAddr);
+    }
+
+    private SocketUtils() {}
+}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 760fef7..f2a9adb 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -48,7 +48,6 @@
 
     /**
      * Defines the root UID.
-     * @hide
      */
     public static final int ROOT_UID = 0;
 
@@ -64,7 +63,6 @@
 
     /**
      * Defines the UID/GID for the user shell.
-     * @hide
      */
     public static final int SHELL_UID = 2000;
 
@@ -118,7 +116,6 @@
 
     /**
      * Defines the UID/GID for the Bluetooth service process.
-     * @hide
      */
     public static final int BLUETOOTH_UID = 1002;
 
diff --git a/core/java/android/os/ServiceSpecificException.java b/core/java/android/os/ServiceSpecificException.java
index 3e0f6da..3b0f26ae 100644
--- a/core/java/android/os/ServiceSpecificException.java
+++ b/core/java/android/os/ServiceSpecificException.java
@@ -15,6 +15,8 @@
  */
 package android.os;
 
+import android.annotation.SystemApi;
+
 /**
  * An exception specific to a service.
  *
@@ -28,6 +30,7 @@
  *
  * @hide
  */
+@SystemApi
 public class ServiceSpecificException extends RuntimeException {
     public final int errorCode;
 
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index ce71b07..7c1465b 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -161,8 +161,9 @@
     private Insets getInsetsFromState(InsetsState state, Rect frame,
             @Nullable @InsetSide SparseIntArray typeSideMap) {
         return state.calculateInsets(frame, false /* isScreenRound */,
-                false /* alwaysConsumerNavBar */, null /* displayCutout */, typeSideMap)
-                .getInsets(mTypes);
+                false /* alwaysConsumerNavBar */, null /* displayCutout */,
+                null /* legacyContentInsets */, null /* legacyStableInsets */, typeSideMap)
+               .getInsets(mTypes);
     }
 
     private Insets sanitize(Insets insets) {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index c2ade76..4b1d1ec 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -56,6 +56,9 @@
 
     private final Runnable mAnimCallback;
 
+    private final Rect mLastLegacyContentInsets = new Rect();
+    private final Rect mLastLegacyStableInsets = new Rect();
+
     public InsetsController(ViewRootImpl viewRoot) {
         mViewRoot = viewRoot;
         mAnimCallback = () -> {
@@ -70,6 +73,7 @@
             }
             WindowInsets insets = state.calculateInsets(mFrame, mLastInsets.isRound(),
                     mLastInsets.shouldAlwaysConsumeNavBar(), mLastInsets.getDisplayCutout(),
+                    mLastLegacyContentInsets, mLastLegacyStableInsets,
                     null /* typeSideMap */);
             mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets);
         };
@@ -102,8 +106,12 @@
      */
     @VisibleForTesting
     public WindowInsets calculateInsets(boolean isScreenRound,
-            boolean alwaysConsumeNavBar, DisplayCutout cutout) {
+            boolean alwaysConsumeNavBar, DisplayCutout cutout, Rect legacyContentInsets,
+            Rect legacyStableInsets) {
+        mLastLegacyContentInsets.set(legacyContentInsets);
+        mLastLegacyStableInsets.set(legacyStableInsets);
         mLastInsets = mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
+                legacyContentInsets, legacyStableInsets,
                 null /* typeSideMap */);
         return mLastInsets;
     }
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index cf8c0707..529776e 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
 import static android.view.WindowInsets.Type.indexOf;
 
 import android.annotation.IntDef;
@@ -119,11 +120,17 @@
      */
     public WindowInsets calculateInsets(Rect frame, boolean isScreenRound,
             boolean alwaysConsumeNavBar, DisplayCutout cutout,
+            @Nullable Rect legacyContentInsets, @Nullable Rect legacyStableInsets,
             @Nullable @InsetSide SparseIntArray typeSideMap) {
         Insets[] typeInsetsMap = new Insets[Type.SIZE];
         Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
         final Rect relativeFrame = new Rect(frame);
         final Rect relativeFrameMax = new Rect(frame);
+        if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_IME
+                && legacyContentInsets != null && legacyStableInsets != null) {
+            WindowInsets.assignCompatInsets(typeInsetsMap, legacyContentInsets);
+            WindowInsets.assignCompatInsets(typeMaxInsetsMap, legacyStableInsets);
+        }
         for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
             InsetsSource source = mSources.get(type);
             if (source == null) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a1aa06e..2131e6d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
 import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
 
 import static java.lang.Math.max;
@@ -5123,7 +5124,7 @@
 
             sAcceptZeroSizeDragShadow = targetSdkVersion < Build.VERSION_CODES.P;
 
-            sBrokenInsetsDispatch = !ViewRootImpl.USE_NEW_INSETS
+            sBrokenInsetsDispatch = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
                     || targetSdkVersion < Build.VERSION_CODES.Q;
 
             sCompatibilityDone = true;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 27d4ea4..a031b70 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -163,13 +163,16 @@
     private static final boolean MT_RENDERER_AVAILABLE = true;
 
     /**
-     * If set to true, the view system will switch from using rectangles retrieved from window to
+     * If set to 2, the view system will switch from using rectangles retrieved from window to
      * dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets
      * directly from the full configuration, enabling richer information about the insets state, as
      * well as new APIs to control it frame-by-frame, and synchronize animations with it.
      * <p>
-     * Only switch this to true once the new insets system is productionized and the old APIs are
+     * Only set this to 2 once the new insets system is productionized and the old APIs are
      * fully migrated over.
+     * <p>
+     * If set to 1, this will switch to a mode where we only use the new approach for IME, but not
+     * for the status/navigation bar.
      */
     private static final String USE_NEW_INSETS_PROPERTY = "persist.wm.new_insets";
 
@@ -177,8 +180,26 @@
      * @see #USE_NEW_INSETS_PROPERTY
      * @hide
      */
-    public static final boolean USE_NEW_INSETS =
-            SystemProperties.getBoolean(USE_NEW_INSETS_PROPERTY, false);
+    public static final int sNewInsetsMode =
+            SystemProperties.getInt(USE_NEW_INSETS_PROPERTY, 0);
+
+    /**
+     * @see #USE_NEW_INSETS_PROPERTY
+     * @hide
+     */
+    public static final int NEW_INSETS_MODE_NONE = 0;
+
+    /**
+     * @see #USE_NEW_INSETS_PROPERTY
+     * @hide
+     */
+    public static final int NEW_INSETS_MODE_IME = 1;
+
+    /**
+     * @see #USE_NEW_INSETS_PROPERTY
+     * @hide
+     */
+    public static final int NEW_INSETS_MODE_FULL = 2;
 
     /**
      * Set this system property to true to force the view hierarchy to render
@@ -1367,7 +1388,7 @@
     }
 
     void notifyInsetsChanged() {
-        if (!USE_NEW_INSETS) {
+        if (sNewInsetsMode == NEW_INSETS_MODE_NONE) {
             return;
         }
         mApplyInsetsRequested = true;
@@ -1855,10 +1876,11 @@
             }
             contentInsets = ensureInsetsNonNegative(contentInsets, "content");
             stableInsets = ensureInsetsNonNegative(stableInsets, "stable");
-            if (USE_NEW_INSETS) {
+            if (sNewInsetsMode != NEW_INSETS_MODE_NONE) {
                 mLastWindowInsets = mInsetsController.calculateInsets(
                         mContext.getResources().getConfiguration().isScreenRound(),
-                        mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+                        mAttachInfo.mAlwaysConsumeNavBar, displayCutout,
+                        contentInsets, stableInsets);
             } else {
                 mLastWindowInsets = new WindowInsets(contentInsets, stableInsets,
                         mContext.getResources().getConfiguration().isScreenRound(),
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 9ac0ca9..e808830 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -217,7 +217,10 @@
         return typeInsetMap;
     }
 
-    private static void assignCompatInsets(Insets[] typeInsetMap, Rect insets) {
+    /**
+     * @hide
+     */
+    static void assignCompatInsets(Insets[] typeInsetMap, Rect insets) {
         typeInsetMap[indexOf(TOP_BAR)] = Insets.of(0, insets.top, 0, 0);
         typeInsetMap[indexOf(SIDE_BARS)] = Insets.of(insets.left, 0, insets.right, insets.bottom);
     }
diff --git a/core/java/android/webkit/WebViewRenderer.java b/core/java/android/webkit/WebViewRenderer.java
index 5328254..fc38cd9 100644
--- a/core/java/android/webkit/WebViewRenderer.java
+++ b/core/java/android/webkit/WebViewRenderer.java
@@ -16,6 +16,8 @@
 
 package android.webkit;
 
+import android.annotation.SystemApi;
+
 /**
  * WebViewRenderer provides an opaque handle to a {@link WebView} renderer.
  */
@@ -40,6 +42,7 @@
      * This class cannot be created by applications.
      * @hide
      */
+    @SystemApi
     public WebViewRenderer() {
     }
 }
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 1aa32cc..276cad9 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -53,7 +53,6 @@
     public static final int BASE_WIFI_PASSPOINT_MANAGER                             = 0x00028000;
     public static final int BASE_WIFI_PASSPOINT_SERVICE                             = 0x00028100;
     public static final int BASE_WIFI_LOGGER                                        = 0x00028300;
-    public static final int BASE_DHCP                                               = 0x00030000;
     public static final int BASE_DATA_CONNECTION                                    = 0x00040000;
     public static final int BASE_DATA_CONNECTION_AC                                 = 0x00041000;
     public static final int BASE_DATA_CONNECTION_TRACKER                            = 0x00042000;
@@ -62,7 +61,6 @@
     public static final int BASE_NETWORK_STATE_TRACKER                              = 0x00070000;
     public static final int BASE_CONNECTIVITY_MANAGER                               = 0x00080000;
     public static final int BASE_NETWORK_AGENT                                      = 0x00081000;
-    public static final int BASE_NETWORK_MONITOR                                    = 0x00082000;
     public static final int BASE_NETWORK_FACTORY                                    = 0x00083000;
     public static final int BASE_ETHERNET                                           = 0x00084000;
     public static final int BASE_LOWPAN                                             = 0x00085000;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 5be70ef..c5dfe6c 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -61,8 +61,11 @@
         "android_database_SQLiteConnection.cpp",
         "android_database_SQLiteGlobal.cpp",
         "android_database_SQLiteDebug.cpp",
+        "android_graphics_Canvas.cpp",
+        "android_graphics_ColorSpace.cpp",
         "android_graphics_drawable_AnimatedVectorDrawable.cpp",
         "android_graphics_drawable_VectorDrawable.cpp",
+        "android_graphics_Picture.cpp",
         "android_view_DisplayEventReceiver.cpp",
         "android_view_DisplayListCanvas.cpp",
         "android_view_TextureLayer.cpp",
@@ -117,8 +120,6 @@
         "android_util_StringBlock.cpp",
         "android_util_XmlBlock.cpp",
         "android_util_jar_StrictJarFile.cpp",
-        "android_graphics_Canvas.cpp",
-        "android_graphics_Picture.cpp",
         "android/graphics/AnimatedImageDrawable.cpp",
         "android/graphics/Bitmap.cpp",
         "android/graphics/BitmapFactory.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index a586dc1..5befab9 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -130,6 +130,7 @@
 extern int register_android_graphics_Canvas(JNIEnv* env);
 extern int register_android_graphics_CanvasProperty(JNIEnv* env);
 extern int register_android_graphics_ColorFilter(JNIEnv* env);
+extern int register_android_graphics_ColorSpace(JNIEnv* env);
 extern int register_android_graphics_DrawFilter(JNIEnv* env);
 extern int register_android_graphics_FontFamily(JNIEnv* env);
 extern int register_android_graphics_Matrix(JNIEnv* env);
@@ -1360,6 +1361,9 @@
     REG_JNI(register_android_os_VintfRuntimeInfo),
     REG_JNI(register_android_nio_utils),
     REG_JNI(register_android_graphics_Canvas),
+    // This needs to be before register_android_graphics_Graphics, or the latter
+    // will not be able to find the jmethodID for ColorSpace.get().
+    REG_JNI(register_android_graphics_ColorSpace),
     REG_JNI(register_android_graphics_Graphics),
     REG_JNI(register_android_view_DisplayEventReceiver),
     REG_JNI(register_android_view_RenderNode),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 876bd4f..ad51c47 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -334,7 +334,7 @@
 static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
                               jint offset, jint stride, jint width, jint height,
                               jint configHandle, jboolean isMutable,
-                              jfloatArray xyzD50, jobject transferParameters) {
+                              jlong colorSpacePtr) {
     SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle);
     if (NULL != jColors) {
         size_t n = env->GetArrayLength(jColors);
@@ -350,17 +350,8 @@
     }
 
     SkBitmap bitmap;
-    sk_sp<SkColorSpace> colorSpace;
-
-    if (xyzD50 == nullptr || transferParameters == nullptr) {
-        colorSpace = SkColorSpace::MakeSRGB();
-    } else {
-        skcms_TransferFunction p = GraphicsJNI::getNativeTransferParameters(env, transferParameters);
-        skcms_Matrix3x3 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50);
-        colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix);
-    }
-
-    bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType, colorSpace));
+    bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType,
+                GraphicsJNI::getNativeColorSpace(colorSpacePtr)));
 
     sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap);
     if (!nativeBitmap) {
@@ -582,17 +573,14 @@
     bitmapErase(skBitmap, SkColor4f::FromColor(color), SkColorSpace::MakeSRGB());
 }
 
-static void Bitmap_eraseLong(JNIEnv* env, jobject, jlong bitmapHandle, jobject jColorSpace,
-        jfloat r, jfloat g, jfloat b, jfloat a) {
-    sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace);
-    if (GraphicsJNI::hasException(env)) {
-        return;
-    }
-
+static void Bitmap_eraseLong(JNIEnv* env, jobject, jlong bitmapHandle,
+        jlong colorSpaceHandle, jlong colorLong) {
     LocalScopedBitmap bitmap(bitmapHandle);
     SkBitmap skBitmap;
     bitmap->getSkBitmap(&skBitmap);
-    SkColor4f color = SkColor4f{r, g, b, a};
+
+    SkColor4f color = GraphicsJNI::convertColorLong(colorLong);
+    sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
     bitmapErase(skBitmap, color, cs);
 }
 
@@ -1141,14 +1129,12 @@
 }
 
 static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject hardwareBuffer,
-                                               jfloatArray xyzD50, jobject transferParameters) {
-    skcms_TransferFunction p = GraphicsJNI::getNativeTransferParameters(env, transferParameters);
-    skcms_Matrix3x3 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50);
-    sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix);
+                                               jlong colorSpacePtr) {
     AHardwareBuffer* hwBuf = android_hardware_HardwareBuffer_getNativeHardwareBuffer(env,
         hardwareBuffer);
     sp<GraphicBuffer> buffer(AHardwareBuffer_to_GraphicBuffer(hwBuf));
-    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, colorSpace);
+    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer,
+            GraphicsJNI::getNativeColorSpace(colorSpacePtr));
     if (!bitmap.get()) {
         ALOGW("failed to create hardware bitmap from hardware buffer");
         return NULL;
@@ -1190,7 +1176,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 static const JNINativeMethod gBitmapMethods[] = {
-    {   "nativeCreate",             "([IIIIIIZ[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/Bitmap;",
+    {   "nativeCreate",             "([IIIIIIZJ)Landroid/graphics/Bitmap;",
         (void*)Bitmap_creator },
     {   "nativeCopy",               "(JIZ)Landroid/graphics/Bitmap;",
         (void*)Bitmap_copy },
@@ -1204,7 +1190,7 @@
     {   "nativeCompress",           "(JIILjava/io/OutputStream;[B)Z",
         (void*)Bitmap_compress },
     {   "nativeErase",              "(JI)V", (void*)Bitmap_erase },
-    {   "nativeErase",              "(JLandroid/graphics/ColorSpace;FFFF)V", (void*)Bitmap_eraseLong },
+    {   "nativeErase",              "(JJJ)V", (void*)Bitmap_eraseLong },
     {   "nativeRowBytes",           "(J)I", (void*)Bitmap_rowBytes },
     {   "nativeConfig",             "(J)I", (void*)Bitmap_config },
     {   "nativeHasAlpha",           "(J)Z", (void*)Bitmap_hasAlpha },
@@ -1236,7 +1222,7 @@
         (void*)Bitmap_copyPreserveInternalConfig },
     {   "nativeCreateHardwareBitmap", "(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap;",
         (void*) Bitmap_createHardwareBitmap },
-    {   "nativeWrapHardwareBufferBitmap", "(Landroid/hardware/HardwareBuffer;[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/Bitmap;",
+    {   "nativeWrapHardwareBufferBitmap", "(Landroid/hardware/HardwareBuffer;J)Landroid/graphics/Bitmap;",
         (void*) Bitmap_wrapHardwareBufferBitmap },
     {   "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;",
         (void*) Bitmap_createGraphicBufferHandle },
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 4b0ab5b..7d0d3d8 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -179,7 +179,7 @@
 }
 
 static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
-                        jobject padding, jobject options) {
+                        jobject padding, jobject options, jlong colorSpaceHandle) {
     // Set default values for the options parameters.
     int sampleSize = 1;
     bool onlyDecodeSize = false;
@@ -189,7 +189,7 @@
     float scale = 1.0f;
     bool requireUnpremultiplied = false;
     jobject javaBitmap = NULL;
-    sk_sp<SkColorSpace> prefColorSpace = nullptr;
+    sk_sp<SkColorSpace> prefColorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
 
     // Update with options supplied by the client.
     if (options != NULL) {
@@ -213,8 +213,6 @@
 
         jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
         prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
-        jobject jcolorSpace = env->GetObjectField(options, gOptions_colorSpaceFieldID);
-        prefColorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace);
         isHardware = GraphicsJNI::isHardwareConfig(env, jconfig);
         isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
         requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
@@ -515,7 +513,7 @@
 }
 
 static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
-        jobject padding, jobject options) {
+        jobject padding, jobject options, jlong colorSpaceHandle) {
 
     jobject bitmap = NULL;
     std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage));
@@ -524,13 +522,13 @@
         std::unique_ptr<SkStreamRewindable> bufferedStream(
                 SkFrontBufferedStream::Make(std::move(stream), SkCodec::MinBufferedBytesNeeded()));
         SkASSERT(bufferedStream.get() != NULL);
-        bitmap = doDecode(env, std::move(bufferedStream), padding, options);
+        bitmap = doDecode(env, std::move(bufferedStream), padding, options, colorSpaceHandle);
     }
     return bitmap;
 }
 
 static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor,
-        jobject padding, jobject bitmapFactoryOptions) {
+        jobject padding, jobject bitmapFactoryOptions, jlong colorSpaceHandle) {
 
     NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
 
@@ -566,7 +564,8 @@
     // If there is no offset for the file descriptor, we use SkFILEStream directly.
     if (::lseek(descriptor, 0, SEEK_CUR) == 0) {
         assert(isSeekable(dupDescriptor));
-        return doDecode(env, std::move(fileStream), padding, bitmapFactoryOptions);
+        return doDecode(env, std::move(fileStream), padding, bitmapFactoryOptions,
+                colorSpaceHandle);
     }
 
     // Use a buffered stream. Although an SkFILEStream can be rewound, this
@@ -575,24 +574,25 @@
     std::unique_ptr<SkStreamRewindable> stream(SkFrontBufferedStream::Make(std::move(fileStream),
             SkCodec::MinBufferedBytesNeeded()));
 
-    return doDecode(env, std::move(stream), padding, bitmapFactoryOptions);
+    return doDecode(env, std::move(stream), padding, bitmapFactoryOptions, colorSpaceHandle);
 }
 
 static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset,
-        jobject padding, jobject options) {
+        jobject padding, jobject options, jlong colorSpaceHandle) {
 
     Asset* asset = reinterpret_cast<Asset*>(native_asset);
     // since we know we'll be done with the asset when we return, we can
     // just use a simple wrapper
-    return doDecode(env, skstd::make_unique<AssetStreamAdaptor>(asset), padding, options);
+    return doDecode(env, skstd::make_unique<AssetStreamAdaptor>(asset), padding, options,
+            colorSpaceHandle);
 }
 
 static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
-        jint offset, jint length, jobject options) {
+        jint offset, jint length, jobject options, jlong colorSpaceHandle) {
 
     AutoJavaByteArray ar(env, byteArray);
     return doDecode(env, skstd::make_unique<SkMemoryStream>(ar.ptr() + offset, length, false),
-                    nullptr, options);
+                    nullptr, options, colorSpaceHandle);
 }
 
 static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
@@ -600,31 +600,26 @@
     return isSeekable(descriptor) ? JNI_TRUE : JNI_FALSE;
 }
 
-jobject decodeBitmap(JNIEnv* env, void* data, size_t size) {
-    return doDecode(env, skstd::make_unique<SkMemoryStream>(data, size),
-                    nullptr, nullptr);
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 static const JNINativeMethod gMethods[] = {
     {   "nativeDecodeStream",
-        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+        "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;",
         (void*)nativeDecodeStream
     },
 
     {   "nativeDecodeFileDescriptor",
-        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+        "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;",
         (void*)nativeDecodeFileDescriptor
     },
 
     {   "nativeDecodeAsset",
-        "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+        "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;",
         (void*)nativeDecodeAsset
     },
 
     {   "nativeDecodeByteArray",
-        "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+        "([BIILandroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;",
         (void*)nativeDecodeByteArray
     },
 
diff --git a/core/jni/android/graphics/BitmapFactory.h b/core/jni/android/graphics/BitmapFactory.h
index 1ee49fa..e37c98d 100644
--- a/core/jni/android/graphics/BitmapFactory.h
+++ b/core/jni/android/graphics/BitmapFactory.h
@@ -28,6 +28,4 @@
 
 jstring encodedFormatToString(JNIEnv* env, SkEncodedImageFormat format);
 
-jobject decodeBitmap(JNIEnv* env, void* data, size_t size);
-
 #endif  // _ANDROID_GRAPHICS_BITMAP_FACTORY_H_
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index f831c05..b4ba749 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -124,7 +124,7 @@
  * reportSizeToVM not supported
  */
 static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX,
-        jint inputY, jint inputWidth, jint inputHeight, jobject options) {
+        jint inputY, jint inputWidth, jint inputHeight, jobject options, jlong colorSpaceHandle) {
 
     // Set default options.
     int sampleSize = 1;
@@ -132,14 +132,12 @@
     bool requireUnpremul = false;
     jobject javaBitmap = NULL;
     bool isHardware = false;
-    sk_sp<SkColorSpace> colorSpace = nullptr;
+    sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
     // Update the default options with any options supplied by the client.
     if (NULL != options) {
         sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
         jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
         colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
-        jobject jcolorSpace = env->GetObjectField(options, gOptions_colorSpaceFieldID);
-        colorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace);
         isHardware = GraphicsJNI::isHardwareConfig(env, jconfig);
         requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
         javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
@@ -255,7 +253,7 @@
 
 static const JNINativeMethod gBitmapRegionDecoderMethods[] = {
     {   "nativeDecodeRegion",
-        "(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+        "(JIIIILandroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;",
         (void*)nativeDecodeRegion},
 
     {   "nativeGetHeight", "(J)I", (void*)nativeGetHeight},
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 9e74b88..6570992 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -178,23 +178,11 @@
 static jmethodID gVMRuntime_newNonMovableArray;
 static jmethodID gVMRuntime_addressOf;
 
-static jfieldID gTransferParams_aFieldID;
-static jfieldID gTransferParams_bFieldID;
-static jfieldID gTransferParams_cFieldID;
-static jfieldID gTransferParams_dFieldID;
-static jfieldID gTransferParams_eFieldID;
-static jfieldID gTransferParams_fFieldID;
-static jfieldID gTransferParams_gFieldID;
-
 static jclass gColorSpace_class;
-static jfieldID gColorSpace_IlluminantD50FieldID;
-static jmethodID gColorSpace_adaptMethodID;
 static jmethodID gColorSpace_getMethodID;
 static jmethodID gColorSpace_matchMethodID;
 
 static jclass gColorSpaceRGB_class;
-static jmethodID gColorSpaceRGB_getTransferParametersMethodID;
-static jmethodID gColorSpaceRGB_getTransformMethodID;
 static jmethodID gColorSpaceRGB_constructorMethodID;
 
 static jclass gColorSpace_Named_class;
@@ -424,63 +412,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-skcms_TransferFunction GraphicsJNI::getNativeTransferParameters(JNIEnv* env, jobject transferParams) {
-    skcms_TransferFunction p;
-    p.a = (float) env->GetDoubleField(transferParams, gTransferParams_aFieldID);
-    p.b = (float) env->GetDoubleField(transferParams, gTransferParams_bFieldID);
-    p.c = (float) env->GetDoubleField(transferParams, gTransferParams_cFieldID);
-    p.d = (float) env->GetDoubleField(transferParams, gTransferParams_dFieldID);
-    p.e = (float) env->GetDoubleField(transferParams, gTransferParams_eFieldID);
-    p.f = (float) env->GetDoubleField(transferParams, gTransferParams_fFieldID);
-    p.g = (float) env->GetDoubleField(transferParams, gTransferParams_gFieldID);
-    return p;
-}
-
-skcms_Matrix3x3 GraphicsJNI::getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) {
-    skcms_Matrix3x3 xyzMatrix;
-    jfloat* array = env->GetFloatArrayElements(xyzD50, NULL);
-    xyzMatrix.vals[0][0] = array[0];
-    xyzMatrix.vals[1][0] = array[1];
-    xyzMatrix.vals[2][0] = array[2];
-    xyzMatrix.vals[0][1] = array[3];
-    xyzMatrix.vals[1][1] = array[4];
-    xyzMatrix.vals[2][1] = array[5];
-    xyzMatrix.vals[0][2] = array[6];
-    xyzMatrix.vals[1][2] = array[7];
-    xyzMatrix.vals[2][2] = array[8];
-    env->ReleaseFloatArrayElements(xyzD50, array, 0);
-    return xyzMatrix;
-}
-
-sk_sp<SkColorSpace> GraphicsJNI::getNativeColorSpace(JNIEnv* env, jobject colorSpace) {
-    if (colorSpace == nullptr) return nullptr;
-    if (!env->IsInstanceOf(colorSpace, gColorSpaceRGB_class)) {
-        doThrowIAE(env, "The color space must be an RGB color space");
-        return nullptr;
-    }
-
-    jobject transferParams = env->CallObjectMethod(colorSpace,
-            gColorSpaceRGB_getTransferParametersMethodID);
-    if (transferParams == nullptr) {
-        doThrowIAE(env, "The color space must use an ICC parametric transfer function");
-        return nullptr;
-    }
-
-    jfloatArray illuminantD50 = (jfloatArray) env->GetStaticObjectField(gColorSpace_class,
-            gColorSpace_IlluminantD50FieldID);
-    jobject colorSpaceD50 = env->CallStaticObjectMethod(gColorSpace_class,
-            gColorSpace_adaptMethodID, colorSpace, illuminantD50);
-
-    jfloatArray xyzD50 = (jfloatArray) env->CallObjectMethod(colorSpaceD50,
-            gColorSpaceRGB_getTransformMethodID);
-
-    skcms_Matrix3x3 xyzMatrix = getNativeXYZMatrix(env, xyzD50);
-    skcms_TransferFunction transferFunction = getNativeTransferParameters(env, transferParams);
-
-    return SkColorSpace::MakeRGB(transferFunction, xyzMatrix);
-}
-
-
 jobject GraphicsJNI::getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace,
         SkColorType decodeColorType) {
     jobject colorSpace = nullptr;
@@ -712,20 +643,7 @@
                                                      "(Ljava/lang/Class;I)Ljava/lang/Object;");
     gVMRuntime_addressOf = GetMethodIDOrDie(env, gVMRuntime_class, "addressOf", "(Ljava/lang/Object;)J");
 
-    jclass transfer_params_class = FindClassOrDie(env, "android/graphics/ColorSpace$Rgb$TransferParameters");
-    gTransferParams_aFieldID = GetFieldIDOrDie(env, transfer_params_class, "a", "D");
-    gTransferParams_bFieldID = GetFieldIDOrDie(env, transfer_params_class, "b", "D");
-    gTransferParams_cFieldID = GetFieldIDOrDie(env, transfer_params_class, "c", "D");
-    gTransferParams_dFieldID = GetFieldIDOrDie(env, transfer_params_class, "d", "D");
-    gTransferParams_eFieldID = GetFieldIDOrDie(env, transfer_params_class, "e", "D");
-    gTransferParams_fFieldID = GetFieldIDOrDie(env, transfer_params_class, "f", "D");
-    gTransferParams_gFieldID = GetFieldIDOrDie(env, transfer_params_class, "g", "D");
-
     gColorSpace_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ColorSpace"));
-    gColorSpace_IlluminantD50FieldID = GetStaticFieldIDOrDie(env,
-            gColorSpace_class, "ILLUMINANT_D50", "[F");
-    gColorSpace_adaptMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, "adapt",
-            "(Landroid/graphics/ColorSpace;[F)Landroid/graphics/ColorSpace;");
     gColorSpace_getMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class,
             "get", "(Landroid/graphics/ColorSpace$Named;)Landroid/graphics/ColorSpace;");
     gColorSpace_matchMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, "match",
@@ -735,10 +653,6 @@
             FindClassOrDie(env, "android/graphics/ColorSpace$Rgb"));
     gColorSpaceRGB_constructorMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class,
             "<init>", "(Ljava/lang/String;[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)V");
-    gColorSpaceRGB_getTransferParametersMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class,
-            "getTransferParameters", "()Landroid/graphics/ColorSpace$Rgb$TransferParameters;");
-    gColorSpaceRGB_getTransformMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class,
-            "getTransform", "()[F");
 
     gColorSpace_Named_class = MakeGlobalRefOrDie(env,
             FindClassOrDie(env, "android/graphics/ColorSpace$Named"));
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 699d153..dc0d022 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -100,12 +100,27 @@
             int srcStride, int x, int y, int width, int height,
             SkBitmap* dstBitmap);
 
-    static skcms_TransferFunction getNativeTransferParameters(JNIEnv* env, jobject transferParams);
-    static skcms_Matrix3x3 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50);
-    static sk_sp<SkColorSpace> getNativeColorSpace(JNIEnv* env, jobject colorSpace);
+    /**
+     * Convert the native SkColorSpace retrieved from ColorSpace.Rgb.getNativeInstance().
+     *
+     * This will never throw an Exception. If the ColorSpace is one that Skia cannot
+     * use, ColorSpace.Rgb.getNativeInstance() would have thrown an Exception. It may,
+     * however, be nullptr, which may be acceptable.
+     */
+    static sk_sp<SkColorSpace> getNativeColorSpace(jlong colorSpaceHandle);
 
     static jobject getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace,
             SkColorType decodeColorType);
+
+    /**
+     * Convert from a Java @ColorLong to an SkColor4f that Skia can use directly.
+     *
+     * This ignores the encoded ColorSpace, besides checking to see if it is sRGB,
+     * which is encoded differently. The color space should be passed down separately
+     * via ColorSpace#getNativeInstance(), and converted with getNativeColorSpace(),
+     * above.
+     */
+    static SkColor4f convertColorLong(jlong color);
 };
 
 class HeapAllocator : public SkBRDAllocator {
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index df735ae..5f12665 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -202,7 +202,7 @@
                                           jint desiredWidth, jint desiredHeight, jobject jsubset,
                                           jboolean requireMutable, jint allocator,
                                           jboolean requireUnpremul, jboolean preferRamOverQuality,
-                                          jboolean asAlphaMask, jobject jcolorSpace) {
+                                          jboolean asAlphaMask, jlong colorSpaceHandle) {
     auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
     SkAndroidCodec* codec = decoder->mCodec.get();
     const SkISize desiredSize = SkISize::Make(desiredWidth, desiredHeight);
@@ -256,7 +256,7 @@
         // This is currently the only way to know that we should decode to F16.
         colorType = codec->computeOutputColorType(colorType);
     }
-    sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace);
+    sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
     colorSpace = codec->computeOutputColorSpace(colorType, colorSpace);
     decodeInfo = decodeInfo.makeColorType(colorType).makeColorSpace(colorSpace);
 
@@ -508,7 +508,7 @@
     { "nCreate",        "([BIILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
     { "nCreate",        "(Ljava/io/InputStream;[BLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
     { "nCreate",        "(Ljava/io/FileDescriptor;Landroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
-    { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZLandroid/graphics/ColorSpace;)Landroid/graphics/Bitmap;",
+    { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZJ)Landroid/graphics/Bitmap;",
                                                                  (void*) ImageDecoder_nDecodeBitmap },
     { "nGetSampledSize","(JI)Landroid/util/Size;",               (void*) ImageDecoder_nGetSampledSize },
     { "nGetPadding",    "(JLandroid/graphics/Rect;)V",           (void*) ImageDecoder_nGetPadding },
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index e2e3042..7679c5b 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -557,41 +557,6 @@
         return result;
     }
 
-    // FIXME: Make this CriticalNative when we no longer need to use JNIEnv. b/122514935 will allow
-    // passing the SkColorSpace directly from JNI.
-    static void setColor(JNIEnv* env, jobject clazz, jlong paintHandle, jobject jColorSpace,
-            jfloat r, jfloat g, jfloat b, jfloat a) {
-        sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace);
-        if (GraphicsJNI::hasException(env)) {
-            return;
-        }
-
-        SkColor4f color = SkColor4f{r, g, b, a};
-        reinterpret_cast<Paint*>(paintHandle)->setColor4f(color, cs.get());
-    }
-
-    // FIXME: Make this CriticalNative when we no longer need to use JNIEnv. b/122514935 will allow
-    // passing the SkColorSpace directly from JNI.
-    static void setShadowLayer(JNIEnv* env, jobject clazz, jlong paintHandle, jfloat radius,
-                               jfloat dx, jfloat dy, jobject jColorSpace,
-                               jfloat r, jfloat g, jfloat b, jfloat a) {
-        sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace);
-        if (GraphicsJNI::hasException(env)) {
-            return;
-        }
-
-        SkColor4f color = SkColor4f{r, g, b, a};
-
-        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        if (radius <= 0) {
-            paint->setLooper(nullptr);
-        }
-        else {
-            SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
-            paint->setLooper(SkBlurDrawLooper::Make(color, cs.get(), sigma, dx, dy));
-        }
-    }
-
     // ------------------ @FastNative ---------------------------
 
     static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
@@ -787,6 +752,13 @@
         obj->setStyle(style);
     }
 
+    static void setColor(jlong paintHandle, jlong colorSpaceHandle,
+            jfloat r, jfloat g, jfloat b, jfloat a) {
+        sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+        SkColor4f color = SkColor4f{r, g, b, a};
+        reinterpret_cast<Paint*>(paintHandle)->setColor4f(color, cs.get());
+    }
+
     static void setAlpha(jlong paintHandle, jint a) {
         reinterpret_cast<Paint*>(paintHandle)->setAlpha(a);
     }
@@ -1034,6 +1006,22 @@
         return SkScalarToFloat(Paint::kStdStrikeThru_Thickness * textSize);
     }
 
+    static void setShadowLayer(jlong paintHandle, jfloat radius,
+                               jfloat dx, jfloat dy, jlong colorSpaceHandle,
+                               jfloat r, jfloat g, jfloat b, jfloat a) {
+        sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+        SkColor4f color = SkColor4f{r, g, b, a};
+
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        if (radius <= 0) {
+            paint->setLooper(nullptr);
+        }
+        else {
+            SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
+            paint->setLooper(SkBlurDrawLooper::Make(color, cs.get(), sigma, dx, dy));
+        }
+    }
+
     static jboolean hasShadowLayer(jlong paintHandle) {
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr);
@@ -1082,9 +1070,6 @@
     {"nGetRunAdvance", "(J[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
     {"nGetOffsetForAdvance", "(J[CIIIIZF)I",
             (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
-    {"nSetColor","(JLandroid/graphics/ColorSpace;FFFF)V", (void*) PaintGlue::setColor},
-    {"nSetShadowLayer", "(JFFFLandroid/graphics/ColorSpace;FFFF)V",
-            (void*)PaintGlue::setShadowLayer},
 
     // --------------- @FastNative ----------------------
 
@@ -1114,6 +1099,7 @@
     {"nSetDither","(JZ)V", (void*) PaintGlue::setDither},
     {"nGetStyle","(J)I", (void*) PaintGlue::getStyle},
     {"nSetStyle","(JI)V", (void*) PaintGlue::setStyle},
+    {"nSetColor","(JJFFFF)V", (void*) PaintGlue::setColor},
     {"nSetAlpha","(JI)V", (void*) PaintGlue::setAlpha},
     {"nGetStrokeWidth","(J)F", (void*) PaintGlue::getStrokeWidth},
     {"nSetStrokeWidth","(JF)V", (void*) PaintGlue::setStrokeWidth},
@@ -1154,6 +1140,7 @@
     {"nGetUnderlineThickness","(J)F", (void*) PaintGlue::getUnderlineThickness},
     {"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition},
     {"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness},
+    {"nSetShadowLayer", "(JFFFJFFFF)V", (void*)PaintGlue::setShadowLayer},
     {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer},
     {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
 };
diff --git a/core/jni/android_graphics_ColorSpace.cpp b/core/jni/android_graphics_ColorSpace.cpp
new file mode 100644
index 0000000..7a9963e
--- /dev/null
+++ b/core/jni/android_graphics_ColorSpace.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include "core_jni_helpers.h"
+
+#include "SkColor.h"
+#include "SkColorSpace.h"
+#include "SkHalf.h"
+
+using namespace android;
+
+static skcms_Matrix3x3 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) {
+    skcms_Matrix3x3 xyzMatrix;
+    jfloat* array = env->GetFloatArrayElements(xyzD50, NULL);
+    xyzMatrix.vals[0][0] = array[0];
+    xyzMatrix.vals[1][0] = array[1];
+    xyzMatrix.vals[2][0] = array[2];
+    xyzMatrix.vals[0][1] = array[3];
+    xyzMatrix.vals[1][1] = array[4];
+    xyzMatrix.vals[2][1] = array[5];
+    xyzMatrix.vals[0][2] = array[6];
+    xyzMatrix.vals[1][2] = array[7];
+    xyzMatrix.vals[2][2] = array[8];
+    env->ReleaseFloatArrayElements(xyzD50, array, 0);
+    return xyzMatrix;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkColor4f GraphicsJNI::convertColorLong(jlong color) {
+    if ((color & 0x3f) == 0) {
+        // This corresponds to sRGB, which is treated differently than the rest.
+        uint8_t a = color >> 56 & 0xff;
+        uint8_t r = color >> 48 & 0xff;
+        uint8_t g = color >> 40 & 0xff;
+        uint8_t b = color >> 32 & 0xff;
+        SkColor c = SkColorSetARGB(a, r, g, b);
+        return SkColor4f::FromColor(c);
+    }
+
+    // These match the implementation of android.graphics.Color#red(long) etc.
+    float r = SkHalfToFloat((SkHalf)(color >> 48 & 0xffff));
+    float g = SkHalfToFloat((SkHalf)(color >> 32 & 0xffff));
+    float b = SkHalfToFloat((SkHalf)(color >> 16 & 0xffff));
+    float a =                       (color >>  6 &  0x3ff) / 1023.0f;
+
+    return SkColor4f{r, g, b, a};
+}
+
+sk_sp<SkColorSpace> GraphicsJNI::getNativeColorSpace(jlong colorSpaceHandle) {
+    if (colorSpaceHandle == 0) return nullptr;
+    return sk_ref_sp(reinterpret_cast<SkColorSpace*>(colorSpaceHandle));
+}
+
+static void unref_colorSpace(SkColorSpace* cs) {
+    SkSafeUnref(cs);
+}
+
+static jlong ColorSpace_getNativeFinalizer(JNIEnv*, jobject) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&unref_colorSpace));
+}
+
+static jlong ColorSpace_creator(JNIEnv* env, jobject, jfloat a, jfloat b, jfloat c,
+        jfloat d, jfloat e, jfloat f, jfloat g, jfloatArray xyzD50) {
+    skcms_TransferFunction p;
+    p.a = a;
+    p.b = b;
+    p.c = c;
+    p.d = d;
+    p.e = e;
+    p.f = f;
+    p.g = g;
+    skcms_Matrix3x3 xyzMatrix = getNativeXYZMatrix(env, xyzD50);
+
+    return reinterpret_cast<jlong>(SkColorSpace::MakeRGB(p, xyzMatrix).release());
+}
+
+static const JNINativeMethod gColorSpaceRgbMethods[] = {
+    {   "nativeGetNativeFinalizer", "()J", (void*)ColorSpace_getNativeFinalizer },
+    {   "nativeCreate", "(FFFFFFF[F)J", (void*)ColorSpace_creator }
+};
+
+namespace android {
+
+int register_android_graphics_ColorSpace(JNIEnv* env) {
+    return android::RegisterMethodsOrDie(env, "android/graphics/ColorSpace$Rgb",
+                                         gColorSpaceRgbMethods, NELEM(gColorSpaceRgbMethods));
+}
+
+}; // namespace android
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 9857b7a..2db2f5f 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -60,7 +60,7 @@
         mState.getSource(TYPE_IME).setVisible(true);
         SparseIntArray typeSideMap = new SparseIntArray();
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
-                DisplayCutout.NO_CUTOUT, typeSideMap);
+                DisplayCutout.NO_CUTOUT, null, null, typeSideMap);
         assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
         assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
         assertEquals(INSET_SIDE_TOP, typeSideMap.get(TYPE_TOP_BAR));
@@ -76,7 +76,7 @@
         mState.getSource(TYPE_IME).setFrame(new Rect(0, 100, 100, 300));
         mState.getSource(TYPE_IME).setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
-                DisplayCutout.NO_CUTOUT, null);
+                DisplayCutout.NO_CUTOUT, null, null, null);
         assertEquals(100, insets.getStableInsetBottom());
         assertEquals(Insets.of(0, 0, 0, 100), insets.getMaxInsets(Type.all()));
         assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
@@ -92,7 +92,7 @@
         mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
         mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
-                DisplayCutout.NO_CUTOUT, null);
+                DisplayCutout.NO_CUTOUT, null, null, null);
         assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
         assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar()));
         assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.sideBars()));
@@ -106,7 +106,7 @@
         mState.getSource(TYPE_IME).setVisible(true);
         mState.removeSource(TYPE_IME);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
-                DisplayCutout.NO_CUTOUT, null);
+                DisplayCutout.NO_CUTOUT, null, null, null);
         assertEquals(0, insets.getSystemWindowInsetBottom());
     }
 
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
index 15a96a1..d57fa8f 100644
--- a/core/tests/coretests/src/android/view/WindowInsetsTest.java
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -18,7 +18,7 @@
 
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowInsets.Type.sideBars;
-
+import static android.view.WindowInsets.Type.topBar;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -54,6 +54,7 @@
         assertTrue(new WindowInsets((Rect) null).isConsumed());
     }
 
+    // TODO: Move this to CTS once API made public
     @Test
     public void typeMap() {
         Builder b = new WindowInsets.Builder();
@@ -62,4 +63,14 @@
         WindowInsets insets = b.build();
         assertEquals(300, insets.getSystemWindowInsets().bottom);
     }
+
+    // TODO: Move this to CTS once API made public
+    @Test
+    public void compatInsets() {
+        Builder b = new WindowInsets.Builder();
+        b.setSystemWindowInsets(Insets.of(0, 50, 30, 10));
+        WindowInsets insets = b.build();
+        assertEquals(Insets.of(0, 50, 0, 0), insets.getInsets(topBar()));
+        assertEquals(Insets.of(0, 0, 30, 10), insets.getInsets(sideBars()));
+    }
 }
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 30f0bfa..bfbdbc5 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -21,7 +21,6 @@
 import android.annotation.ColorLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.Size;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.annotation.WorkerThread;
@@ -748,22 +747,10 @@
             throw new IllegalArgumentException("usage flags must contain USAGE_GPU_SAMPLED_IMAGE.");
         }
         int format = hardwareBuffer.getFormat();
-        ColorSpace.Rgb rgb = null;
-        if (colorSpace != null) {
-            if (!(colorSpace instanceof ColorSpace.Rgb)) {
-                throw new IllegalArgumentException("colorSpace must be an RGB color space");
-            }
-            rgb = (ColorSpace.Rgb) colorSpace;
-        } else {
-            rgb = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
+        if (colorSpace == null) {
+            colorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
         }
-        ColorSpace.Rgb.TransferParameters parameters = rgb.getTransferParameters();
-        if (parameters == null) {
-            throw new IllegalArgumentException("colorSpace must use an ICC "
-                    + "parametric transfer function");
-        }
-        ColorSpace.Rgb d50 = (ColorSpace.Rgb) ColorSpace.adapt(rgb, ColorSpace.ILLUMINANT_D50);
-        return nativeWrapHardwareBufferBitmap(hardwareBuffer, d50.getTransform(), parameters);
+        return nativeWrapHardwareBufferBitmap(hardwareBuffer, colorSpace.getNativeInstance());
     }
 
     /**
@@ -1103,26 +1090,18 @@
             throw new IllegalArgumentException("can't create bitmap without a color space");
         }
 
-        Bitmap bm;
-        // nullptr color spaces have a particular meaning in native and are interpreted as sRGB
-        // (we also avoid the unnecessary extra work of the else branch)
-        if (config != Config.ARGB_8888 || colorSpace == ColorSpace.get(ColorSpace.Named.SRGB)) {
-            bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true, null, null);
-        } else {
-            if (!(colorSpace instanceof ColorSpace.Rgb)) {
-                throw new IllegalArgumentException("colorSpace must be an RGB color space");
+        if (config != Config.ARGB_8888) {
+            if (config == Config.RGBA_F16) {
+                // FIXME: This should be LINEAR_EXTENDED_SRGB, but that would fail a CTS test. See
+                // b/120960866. SRGB matches the old (incorrect) behavior.
+                //colorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
+                colorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
+            } else {
+                colorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
             }
-            ColorSpace.Rgb rgb = (ColorSpace.Rgb) colorSpace;
-            ColorSpace.Rgb.TransferParameters parameters = rgb.getTransferParameters();
-            if (parameters == null) {
-                throw new IllegalArgumentException("colorSpace must use an ICC "
-                        + "parametric transfer function");
-            }
-
-            ColorSpace.Rgb d50 = (ColorSpace.Rgb) ColorSpace.adapt(rgb, ColorSpace.ILLUMINANT_D50);
-            bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true,
-                    d50.getTransform(), parameters);
         }
+        Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true,
+                colorSpace.getNativeInstance());
 
         if (display != null) {
             bm.mDensity = display.densityDpi;
@@ -1200,8 +1179,9 @@
         if (width <= 0 || height <= 0) {
             throw new IllegalArgumentException("width and height must be > 0");
         }
+        ColorSpace sRGB = ColorSpace.get(ColorSpace.Named.SRGB);
         Bitmap bm = nativeCreate(colors, offset, stride, width, height,
-                            config.nativeInt, false, null, null);
+                            config.nativeInt, false, sRGB.getNativeInstance());
         if (display != null) {
             bm.mDensity = display.densityDpi;
         }
@@ -1798,11 +1778,7 @@
         }
 
         ColorSpace cs = Color.colorSpace(c);
-        float r = Color.red(c);
-        float g = Color.green(c);
-        float b = Color.blue(c);
-        float a = Color.alpha(c);
-        nativeErase(mNativePtr, cs, r, g, b, a);
+        nativeErase(mNativePtr, cs.getNativeInstance(), c);
     }
 
     /**
@@ -2133,8 +2109,7 @@
     private static native Bitmap nativeCreate(int[] colors, int offset,
                                               int stride, int width, int height,
                                               int nativeConfig, boolean mutable,
-                                              @Nullable @Size(9) float[] xyzD50,
-                                              @Nullable ColorSpace.Rgb.TransferParameters p);
+                                              long nativeColorSpace);
     private static native Bitmap nativeCopy(long nativeSrcBitmap, int nativeConfig,
                                             boolean isMutable);
     private static native Bitmap nativeCopyAshmem(long nativeSrcBitmap);
@@ -2149,8 +2124,7 @@
                                             int quality, OutputStream stream,
                                             byte[] tempStorage);
     private static native void nativeErase(long nativeBitmap, int color);
-    private static native void nativeErase(long nativeBitmap, ColorSpace cs,
-                                           float r, float g, float b, float a);
+    private static native void nativeErase(long nativeBitmap, long colorSpacePtr, long color);
     private static native int nativeRowBytes(long nativeBitmap);
     private static native int nativeConfig(long nativeBitmap);
 
@@ -2194,8 +2168,7 @@
     private static native Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap);
     private static native Bitmap nativeCreateHardwareBitmap(GraphicBuffer buffer);
     private static native Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer,
-                                                              @Size(9) float[] xyzD50,
-                                                              ColorSpace.Rgb.TransferParameters p);
+                                                                long nativeColorSpace);
     private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap);
     private static native boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params);
     private static native boolean nativeIsSRGB(long nativePtr);
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 022fbdc..7aff041 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -460,6 +460,21 @@
                 }
             }
         }
+
+        /**
+         *  Helper for passing SkColorSpace pointer to native.
+         *
+         *  @throws IllegalArgumentException if the ColorSpace is not Rgb or does
+         *          not have TransferParameters.
+         */
+        static long nativeColorSpace(Options opts) {
+            if (opts == null || opts.inPreferredColorSpace == null) {
+                return 0;
+            }
+
+            return opts.inPreferredColorSpace.getNativeInstance();
+        }
+
     }
 
     /**
@@ -633,7 +648,8 @@
 
         Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
         try {
-            bm = nativeDecodeByteArray(data, offset, length, opts);
+            bm = nativeDecodeByteArray(data, offset, length, opts,
+                    Options.nativeColorSpace(opts));
 
             if (bm == null && opts != null && opts.inBitmap != null) {
                 throw new IllegalArgumentException("Problem decoding into existing bitmap");
@@ -728,7 +744,7 @@
         try {
             if (is instanceof AssetManager.AssetInputStream) {
                 final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
-                bm = nativeDecodeAsset(asset, outPadding, opts);
+                bm = nativeDecodeAsset(asset, outPadding, opts, Options.nativeColorSpace(opts));
             } else {
                 bm = decodeStreamInternal(is, outPadding, opts);
             }
@@ -755,7 +771,8 @@
         byte [] tempStorage = null;
         if (opts != null) tempStorage = opts.inTempStorage;
         if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE];
-        return nativeDecodeStream(is, tempStorage, outPadding, opts);
+        return nativeDecodeStream(is, tempStorage, outPadding, opts,
+                Options.nativeColorSpace(opts));
     }
 
     /**
@@ -798,7 +815,8 @@
         Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeFileDescriptor");
         try {
             if (nativeIsSeekable(fd)) {
-                bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
+                bm = nativeDecodeFileDescriptor(fd, outPadding, opts,
+                        Options.nativeColorSpace(opts));
             } else {
                 FileInputStream fis = new FileInputStream(fd);
                 try {
@@ -835,14 +853,15 @@
 
     @UnsupportedAppUsage
     private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
-            Rect padding, Options opts);
+            Rect padding, Options opts, long colorSpaceHandle);
     @UnsupportedAppUsage
     private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
-            Rect padding, Options opts);
+            Rect padding, Options opts, long colorSpaceHandle);
     @UnsupportedAppUsage
-    private static native Bitmap nativeDecodeAsset(long nativeAsset, Rect padding, Options opts);
+    private static native Bitmap nativeDecodeAsset(long nativeAsset, Rect padding, Options opts,
+            long colorSpaceHandle);
     @UnsupportedAppUsage
     private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
-            int length, Options opts);
+            int length, Options opts, long colorSpaceHandle);
     private static native boolean nativeIsSeekable(FileDescriptor fd);
 }
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index 43282d3..1410423 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -195,7 +195,8 @@
                     || rect.top >= getHeight())
                 throw new IllegalArgumentException("rectangle is outside the image");
             return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top,
-                    rect.right - rect.left, rect.bottom - rect.top, options);
+                    rect.right - rect.left, rect.bottom - rect.top, options,
+                    BitmapFactory.Options.nativeColorSpace(options));
         }
     }
 
@@ -265,7 +266,7 @@
 
     private static native Bitmap nativeDecodeRegion(long lbm,
             int start_x, int start_y, int width, int height,
-            BitmapFactory.Options options);
+            BitmapFactory.Options options, long colorSpaceHandle);
     private static native int nativeGetWidth(long lbm);
     private static native int nativeGetHeight(long lbm);
     private static native void nativeClean(long lbm);
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 9fa70a5..4755d45 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -25,6 +25,8 @@
 import android.annotation.SuppressAutoDoc;
 import android.util.Pair;
 
+import libcore.util.NativeAllocationRegistry;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -199,6 +201,9 @@
     private static final float[] NTSC_1953_PRIMARIES = { 0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f };
     private static final float[] ILLUMINANT_D50_XYZ = { 0.964212f, 1.0f, 0.825188f };
 
+    private static final Rgb.TransferParameters SRGB_TRANSFER_PARAMETERS =
+            new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4);
+
     // See static initialization block next to #get(Named)
     private static final ColorSpace[] sNamedColorSpaces = new ColorSpace[Named.values().length];
 
@@ -1341,6 +1346,26 @@
     }
 
     /**
+     * Helper method for creating native SkColorSpace.
+     *
+     * This essentially calls adapt on a ColorSpace that has not been fully
+     * created. It also does not fully create the adapted ColorSpace, but
+     * just returns the transform.
+     */
+    @NonNull @Size(9)
+    private static float[] adaptToIlluminantD50(
+            @NonNull @Size(2) float[] origWhitePoint,
+            @NonNull @Size(9) float[] origTransform) {
+        float[] desired = ILLUMINANT_D50;
+        if (compare(origWhitePoint, desired)) return origTransform;
+
+        float[] xyz = xyYToXyz(desired);
+        float[] adaptationTransform = chromaticAdaptation(Adaptation.BRADFORD.mTransform,
+                    xyYToXyz(origWhitePoint), xyz);
+        return mul3x3(adaptationTransform, origTransform);
+    }
+
+    /**
      * <p>Returns an instance of {@link ColorSpace} whose ID matches the
      * specified ID.</p>
      *
@@ -1431,7 +1456,7 @@
                 "sRGB IEC61966-2.1",
                 SRGB_PRIMARIES,
                 ILLUMINANT_D65,
-                new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
+                SRGB_TRANSFER_PARAMETERS,
                 Named.SRGB.ordinal()
         );
         sNamedColorSpaces[Named.LINEAR_SRGB.ordinal()] = new ColorSpace.Rgb(
@@ -1446,9 +1471,11 @@
                 "scRGB-nl IEC 61966-2-2:2003",
                 SRGB_PRIMARIES,
                 ILLUMINANT_D65,
+                null,
                 x -> absRcpResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
                 x -> absResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
                 -0.799f, 2.399f,
+                null, // FIXME: Use SRGB_TRANSFER_PARAMETERS
                 Named.EXTENDED_SRGB.ordinal()
         );
         sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
@@ -1485,7 +1512,7 @@
                 "Display P3",
                 new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
                 ILLUMINANT_D65,
-                new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
+                SRGB_TRANSFER_PARAMETERS,
                 Named.DISPLAY_P3.ordinal()
         );
         sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb(
@@ -1967,6 +1994,15 @@
     }
 
     /**
+     * Retrieve the native SkColorSpace object for passing to native.
+     *
+     * Only valid on ColorSpace.Rgb.
+     */
+    long getNativeInstance() {
+        throw new IllegalArgumentException("colorSpace must be an RGB color space");
+    }
+
+    /**
      * {@usesMathJax}
      *
      * <p>An RGB color space is an additive color space using the
@@ -2269,7 +2305,22 @@
         private final boolean mIsWideGamut;
         private final boolean mIsSrgb;
 
-        @Nullable private TransferParameters mTransferParameters;
+        @Nullable private final TransferParameters mTransferParameters;
+        private final long mNativePtr;
+
+        @Override
+        long getNativeInstance() {
+            if (mNativePtr == 0) {
+                // If this object has TransferParameters, it must have a native object.
+                throw new IllegalArgumentException("ColorSpace must use an ICC "
+                        + "parametric transfer function! used " + this);
+            }
+            return mNativePtr;
+        }
+
+        private static native long nativeGetNativeFinalizer();
+        private static native long nativeCreate(float a, float b, float c, float d,
+                float e, float f, float g, float[] xyz);
 
         /**
          * <p>Creates a new RGB color space using a 3x3 column-major transform matrix.
@@ -2298,8 +2349,8 @@
                 @NonNull @Size(9) float[] toXYZ,
                 @NonNull DoubleUnaryOperator oetf,
                 @NonNull DoubleUnaryOperator eotf) {
-            this(name, computePrimaries(toXYZ), computeWhitePoint(toXYZ),
-                    oetf, eotf, 0.0f, 1.0f, MIN_ID);
+            this(name, computePrimaries(toXYZ), computeWhitePoint(toXYZ), null,
+                    oetf, eotf, 0.0f, 1.0f, null, MIN_ID);
         }
 
         /**
@@ -2349,7 +2400,7 @@
                 @NonNull DoubleUnaryOperator eotf,
                 float min,
                 float max) {
-            this(name, primaries, whitePoint, oetf, eotf, min, max, MIN_ID);
+            this(name, primaries, whitePoint, null, oetf, eotf, min, max, null, MIN_ID);
         }
 
         /**
@@ -2459,7 +2510,7 @@
                 @NonNull @Size(min = 2, max = 3) float[] whitePoint,
                 @NonNull TransferParameters function,
                 @IntRange(from = MIN_ID, to = MAX_ID) int id) {
-            this(name, primaries, whitePoint,
+            this(name, primaries, whitePoint, null,
                     function.e == 0.0 && function.f == 0.0 ?
                             x -> rcpResponse(x, function.a, function.b,
                                     function.c, function.d, function.g) :
@@ -2470,8 +2521,7 @@
                                     function.c, function.d, function.g) :
                             x -> response(x, function.a, function.b, function.c,
                                     function.d, function.e, function.f, function.g),
-                    0.0f, 1.0f, id);
-            mTransferParameters = function;
+                    0.0f, 1.0f, function, id);
         }
 
         /**
@@ -2586,13 +2636,12 @@
                 float min,
                 float max,
                 @IntRange(from = MIN_ID, to = MAX_ID) int id) {
-            this(name, primaries, whitePoint,
+            this(name, primaries, whitePoint, null,
                     gamma == 1.0 ? DoubleUnaryOperator.identity() :
                             x -> Math.pow(x < 0.0 ? 0.0 : x, 1 / gamma),
                     gamma == 1.0 ? DoubleUnaryOperator.identity() :
                             x -> Math.pow(x < 0.0 ? 0.0 : x, gamma),
-                    min, max, id);
-            mTransferParameters = new TransferParameters(1.0, 0.0, 0.0, 0.0, gamma);
+                    min, max, new TransferParameters(1.0, 0.0, 0.0, 0.0, gamma), id);
         }
 
         /**
@@ -2615,10 +2664,13 @@
          * @param name Name of the color space, cannot be null, its length must be >= 1
          * @param primaries RGB primaries as an array of 6 (xy) or 9 (XYZ) floats
          * @param whitePoint Reference white as an array of 2 (xy) or 3 (XYZ) floats
+         * @param transform Computed transform matrix that converts from RGB to XYZ, or
+         *      {@code null} to compute it from {@code primaries} and {@code whitePoint}.
          * @param oetf Opto-electronic transfer function, cannot be null
          * @param eotf Electro-optical transfer function, cannot be null
          * @param min The minimum valid value in this color space's RGB range
          * @param max The maximum valid value in this color space's RGB range
+         * @param transferParameters Parameters for the transfer functions
          * @param id ID of this color space as an integer between {@link #MIN_ID} and {@link #MAX_ID}
          *
          * @throws IllegalArgumentException If any of the following conditions is met:
@@ -2637,10 +2689,12 @@
                 @NonNull @Size(min = 1) String name,
                 @NonNull @Size(min = 6, max = 9) float[] primaries,
                 @NonNull @Size(min = 2, max = 3) float[] whitePoint,
+                @Nullable @Size(9) float[] transform,
                 @NonNull DoubleUnaryOperator oetf,
                 @NonNull DoubleUnaryOperator eotf,
                 float min,
                 float max,
+                @Nullable TransferParameters transferParameters,
                 @IntRange(from = MIN_ID, to = MAX_ID) int id) {
 
             super(name, Model.RGB, id);
@@ -2668,7 +2722,15 @@
             mWhitePoint = xyWhitePoint(whitePoint);
             mPrimaries =  xyPrimaries(primaries);
 
-            mTransform = computeXYZMatrix(mPrimaries, mWhitePoint);
+            if (transform == null) {
+                mTransform = computeXYZMatrix(mPrimaries, mWhitePoint);
+            } else {
+                if (transform.length != 9) {
+                    throw new IllegalArgumentException("Transform must have 9 entries! Has "
+                            + transform.length);
+                }
+                mTransform = transform;
+            }
             mInverseTransform = inverse3x3(mTransform);
 
             mOetf = oetf;
@@ -2681,10 +2743,39 @@
             mClampedOetf = oetf.andThen(clamp);
             mClampedEotf = clamp.andThen(eotf);
 
+            mTransferParameters = transferParameters;
+
             // A color space is wide-gamut if its area is >90% of NTSC 1953 and
             // if it entirely contains the Color space definition in xyY
             mIsWideGamut = isWideGamut(mPrimaries, min, max);
             mIsSrgb = isSrgb(mPrimaries, mWhitePoint, oetf, eotf, min, max, id);
+
+            if (mTransferParameters != null) {
+                if (mWhitePoint == null || mTransform == null) {
+                    throw new IllegalStateException(
+                            "ColorSpace (" + this + ") cannot create native object! mWhitePoint: "
+                            + mWhitePoint + " mTransform: " + mTransform);
+                }
+
+                // This mimics the old code that was in native.
+                float[] nativeTransform = adaptToIlluminantD50(mWhitePoint, mTransform);
+                mNativePtr = nativeCreate((float) mTransferParameters.a,
+                                          (float) mTransferParameters.b,
+                                          (float) mTransferParameters.c,
+                                          (float) mTransferParameters.d,
+                                          (float) mTransferParameters.e,
+                                          (float) mTransferParameters.f,
+                                          (float) mTransferParameters.g,
+                                          nativeTransform);
+                NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativePtr);
+            } else {
+                mNativePtr = 0;
+            }
+        }
+
+        private static class NoImagePreloadHolder {
+            public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+                ColorSpace.Rgb.class.getClassLoader(), nativeGetNativeFinalizer(), 0);
         }
 
         /**
@@ -2695,27 +2786,9 @@
         private Rgb(Rgb colorSpace,
                 @NonNull @Size(9) float[] transform,
                 @NonNull @Size(min = 2, max = 3) float[] whitePoint) {
-            super(colorSpace.getName(), Model.RGB, -1);
-
-            mWhitePoint = xyWhitePoint(whitePoint);
-            mPrimaries = colorSpace.mPrimaries;
-
-            mTransform = transform;
-            mInverseTransform = inverse3x3(transform);
-
-            mMin = colorSpace.mMin;
-            mMax = colorSpace.mMax;
-
-            mOetf = colorSpace.mOetf;
-            mEotf = colorSpace.mEotf;
-
-            mClampedOetf = colorSpace.mClampedOetf;
-            mClampedEotf = colorSpace.mClampedEotf;
-
-            mIsWideGamut = colorSpace.mIsWideGamut;
-            mIsSrgb = colorSpace.mIsSrgb;
-
-            mTransferParameters = colorSpace.mTransferParameters;
+            this(colorSpace.getName(), colorSpace.mPrimaries, whitePoint, transform,
+                    colorSpace.mOetf, colorSpace.mEotf, colorSpace.mMin, colorSpace.mMax,
+                    colorSpace.mTransferParameters, MIN_ID);
         }
 
         /**
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index e3b165c..466a5fc 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -1628,17 +1628,6 @@
         if (mPostProcessor != null && mUnpremultipliedRequired) {
             throw new IllegalStateException("Cannot draw to unpremultiplied pixels!");
         }
-
-        if (mDesiredColorSpace != null) {
-            if (!(mDesiredColorSpace instanceof ColorSpace.Rgb)) {
-                throw new IllegalArgumentException("The target color space must use the "
-                            + "RGB color model - provided: " + mDesiredColorSpace);
-            }
-            if (((ColorSpace.Rgb) mDesiredColorSpace).getTransferParameters() == null) {
-                throw new IllegalArgumentException("The target color space must use an "
-                            + "ICC parametric transfer function - provided: " + mDesiredColorSpace);
-            }
-        }
     }
 
     private static void checkSubset(int width, int height, Rect r) {
@@ -1655,10 +1644,12 @@
     @NonNull
     private Bitmap decodeBitmapInternal() throws IOException {
         checkState();
+        long colorSpacePtr = mDesiredColorSpace == null ? 0 :
+                mDesiredColorSpace.getNativeInstance();
         return nDecodeBitmap(mNativePtr, this, mPostProcessor != null,
                 mDesiredWidth, mDesiredHeight, mCropRect,
                 mMutable, mAllocator, mUnpremultipliedRequired,
-                mConserveMemory, mDecodeAsAlphaMask, mDesiredColorSpace);
+                mConserveMemory, mDecodeAsAlphaMask, colorSpacePtr);
     }
 
     private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
@@ -1946,7 +1937,7 @@
             @Nullable Rect cropRect, boolean mutable,
             int allocator, boolean unpremulRequired,
             boolean conserveMemory, boolean decodeAsAlphaMask,
-            @Nullable ColorSpace desiredColorSpace)
+            long desiredColorSpace)
         throws IOException;
     private static native Size nGetSampledSize(long nativePtr,
                                                int sampleSize);
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 54e1abc..7eee6f4 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1017,7 +1017,7 @@
         float b = Color.blue(color);
         float a = Color.alpha(color);
 
-        nSetColor(mNativePaint, cs, r, g, b, a);
+        nSetColor(mNativePaint, cs.getNativeInstance(), r, g, b, a);
         mColor = color;
     }
 
@@ -1455,7 +1455,7 @@
         float g = Color.green(shadowColor);
         float b = Color.blue(shadowColor);
         float a = Color.alpha(shadowColor);
-        nSetShadowLayer(mNativePaint, radius, dx, dy, cs, r, g, b, a);
+        nSetShadowLayer(mNativePaint, radius, dx, dy, cs.getNativeInstance(), r, g, b, a);
 
         mShadowLayerRadius = radius;
         mShadowLayerDx = dx;
@@ -3003,11 +3003,6 @@
             int contextStart, int contextEnd, boolean isRtl, int offset);
     private static native int nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end,
             int contextStart, int contextEnd, boolean isRtl, float advance);
-    private static native void nSetColor(long paintPtr, ColorSpace cs,
-            float r, float g, float b, float a);
-    private static native void nSetShadowLayer(long paintPtr,
-            float radius, float dx, float dy, ColorSpace cs,
-            float r, float g, float b, float a);
 
 
     // ---------------- @FastNative ------------------------
@@ -3063,7 +3058,8 @@
             int mMinikinLocaleListId);
     @CriticalNative
     private static native void nSetShadowLayer(long paintPtr,
-            float radius, float dx, float dy, @ColorInt int color);
+            float radius, float dx, float dy, long colorSpaceHandle,
+            float r, float g, float b, float a);
     @CriticalNative
     private static native boolean nHasShadowLayer(long paintPtr);
     @CriticalNative
@@ -3111,6 +3107,9 @@
     @CriticalNative
     private static native void nSetFilterBitmap(long paintPtr, boolean filter);
     @CriticalNative
+    private static native void nSetColor(long paintPtr, long colorSpaceHandle,
+            float r, float g, float b, float a);
+    @CriticalNative
     private static native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
     @CriticalNative
     private static native boolean nIsElegantTextHeight(long paintPtr);
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 77e758fc..3f4fbb9 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -122,7 +122,6 @@
      * {@link MediaSession2.Builder} instead.
      *
      * @param token newly created session2 token
-     * @hide
      */
     public void notifySession2Created(@NonNull Session2Token token) {
         if (token == null) {
@@ -196,9 +195,7 @@
      * reject your uses of {@link MediaSession2}.
      *
      * @return A list of {@link Session2Token}.
-     * @hide
      */
-    // TODO: unhide
     @NonNull
     public List<Session2Token> getSession2Tokens() {
         return getSession2Tokens(UserHandle.myUserId());
@@ -209,13 +206,12 @@
      * given user.
      * <p>
      * If you want to get tokens for another user, you must hold the
-     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission.
+     * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL permission.
      *
      * @param userId The user id to fetch sessions for.
      * @return A list of {@link Session2Token}
      * @hide
      */
-    // TODO: unhide
     @NonNull
     public List<Session2Token> getSession2Tokens(int userId) {
         try {
@@ -342,13 +338,25 @@
      * for consistent behavior across all devices.
      *
      * @param listener The listener to add
-     * @param handler The handler to call listener on. If {@code null}, calling thread's looper will
-     *                be used.
-     * @hide
      */
-    // TODO(jaewan): Unhide
     public void addOnSession2TokensChangedListener(
-            @NonNull OnSession2TokensChangedListener listener, @Nullable Handler handler) {
+            @NonNull OnSession2TokensChangedListener listener) {
+        addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, new Handler());
+    }
+
+    /**
+     * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
+     * <p>
+     * This API is not generally intended for third party application developers.
+     * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+     * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+     * for consistent behavior across all devices.
+     *
+     * @param listener The listener to add
+     * @param handler The handler to call listener on.
+     */
+    public void addOnSession2TokensChangedListener(
+            @NonNull OnSession2TokensChangedListener listener, @NonNull Handler handler) {
         addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, handler);
     }
 
@@ -392,9 +400,7 @@
      * Removes the {@link OnSession2TokensChangedListener} to stop receiving session token updates.
      *
      * @param listener The listener to remove.
-     * @hide
      */
-    // TODO(jaewan): Unhide
     public void removeOnSession2TokensChangedListener(
             @NonNull OnSession2TokensChangedListener listener) {
         if (listener == null) {
@@ -688,8 +694,6 @@
      * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
      * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
      * for consistent behavior across all devices.
-     *
-     * @hide
      */
     public interface OnSession2TokensChangedListener {
         /**
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
index 14e2936..7b112df 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
@@ -29,7 +29,6 @@
 import static android.system.OsConstants.IPPROTO_UDP;
 import static android.system.OsConstants.SOCK_DGRAM;
 import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_BINDTODEVICE;
 import static android.system.OsConstants.SO_BROADCAST;
 import static android.system.OsConstants.SO_REUSEADDR;
 
@@ -45,6 +44,7 @@
 import android.net.NetworkUtils;
 import android.net.TrafficStats;
 import android.net.util.SharedLog;
+import android.net.util.SocketUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -629,14 +629,10 @@
             final int oldTag = TrafficStats.getAndSetThreadStatsTag(TAG_SYSTEM_DHCP_SERVER);
             try {
                 mSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+                SocketUtils.bindSocketToInterface(mSocket, mIfName);
                 Os.setsockoptInt(mSocket, SOL_SOCKET, SO_REUSEADDR, 1);
-                // SO_BINDTODEVICE actually takes a string. This works because the first member
-                // of struct ifreq is a NULL-terminated interface name.
-                // TODO: add a setsockoptString()
-                Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIfName);
                 Os.setsockoptInt(mSocket, SOL_SOCKET, SO_BROADCAST, 1);
                 Os.bind(mSocket, Inet4Address.ANY, DHCP_SERVER);
-                NetworkUtils.protectFromVpn(mSocket);
 
                 return mSocket;
             } catch (IOException | ErrnoException e) {
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
index f38888a..868f3be 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
@@ -30,10 +30,10 @@
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.NetworkUtils;
-
-import com.google.android.collect.Sets;
+import android.util.ArraySet;
 
 import java.net.Inet4Address;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
@@ -208,7 +208,7 @@
          * but it must always be set explicitly before building the {@link DhcpServingParams}.
          */
         public Builder setDefaultRouters(@NonNull Inet4Address... defaultRouters) {
-            return setDefaultRouters(Sets.newArraySet(defaultRouters));
+            return setDefaultRouters(new ArraySet<>(Arrays.asList(defaultRouters)));
         }
 
         /**
@@ -238,7 +238,7 @@
          * building the {@link DhcpServingParams}.
          */
         public Builder setDnsServers(@NonNull Inet4Address... dnsServers) {
-            return setDnsServers(Sets.newArraySet(dnsServers));
+            return setDnsServers(new ArraySet<>(Arrays.asList(dnsServers)));
         }
 
         /**
@@ -268,7 +268,7 @@
          * and do not need to be set here.
          */
         public Builder setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) {
-            return setExcludedAddrs(Sets.newArraySet(excludedAddrs));
+            return setExcludedAddrs(new ArraySet<>(Arrays.asList(excludedAddrs)));
         }
 
         /**
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index c8a8e1f..a3d7852 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -24,6 +24,7 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.metrics.ValidationProbeEvent.DNS_FAILURE;
@@ -80,7 +81,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Protocol;
 import com.android.internal.util.RingBufferIndices;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
@@ -150,29 +150,28 @@
         }
     }
 
-    private static final int BASE = Protocol.BASE_NETWORK_MONITOR;
     /**
      * ConnectivityService has sent a notification to indicate that network has connected.
      * Initiates Network Validation.
      */
-    private static final int CMD_NETWORK_CONNECTED = BASE + 1;
+    private static final int CMD_NETWORK_CONNECTED = 1;
 
     /**
      * Message to self indicating it's time to evaluate a network's connectivity.
      * arg1 = Token to ignore old messages.
      */
-    private static final int CMD_REEVALUATE = BASE + 6;
+    private static final int CMD_REEVALUATE = 6;
 
     /**
      * ConnectivityService has sent a notification to indicate that network has disconnected.
      */
-    private static final int CMD_NETWORK_DISCONNECTED = BASE + 7;
+    private static final int CMD_NETWORK_DISCONNECTED = 7;
 
     /**
      * Force evaluation even if it has succeeded in the past.
      * arg1 = UID responsible for requesting this reeval.  Will be billed for data.
      */
-    private static final int CMD_FORCE_REEVALUATION = BASE + 8;
+    private static final int CMD_FORCE_REEVALUATION = 8;
 
     /**
      * Message to self indicating captive portal app finished.
@@ -181,7 +180,7 @@
      *                APP_RETURN_WANTED_AS_IS
      * obj = mCaptivePortalLoggedInResponseToken as String
      */
-    private static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = BASE + 9;
+    private static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = 9;
 
     /**
      * Message indicating sign-in app should be launched.
@@ -190,14 +189,14 @@
      * ConnectivityService when the user touches the "sign into
      * network" button in the wifi access point detail page.
      */
-    private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = BASE + 11;
+    private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = 11;
 
     /**
      * Retest network to see if captive portal is still in place.
      * arg1 = UID responsible for requesting this reeval.  Will be billed for data.
      *        0 indicates self-initiated, so nobody to blame.
      */
-    private static final int CMD_CAPTIVE_PORTAL_RECHECK = BASE + 12;
+    private static final int CMD_CAPTIVE_PORTAL_RECHECK = 12;
 
     /**
      * ConnectivityService notifies NetworkMonitor of settings changes to
@@ -210,20 +209,20 @@
      * states, including being ignored until after an ongoing captive portal
      * validation phase is completed.
      */
-    private static final int CMD_PRIVATE_DNS_SETTINGS_CHANGED = BASE + 13;
-    private static final int CMD_EVALUATE_PRIVATE_DNS = BASE + 15;
+    private static final int CMD_PRIVATE_DNS_SETTINGS_CHANGED = 13;
+    private static final int CMD_EVALUATE_PRIVATE_DNS = 15;
 
     /**
      * Message to self indicating captive portal detection is completed.
      * obj = CaptivePortalProbeResult for detection result;
      */
-    public static final int CMD_PROBE_COMPLETE = BASE + 16;
+    public static final int CMD_PROBE_COMPLETE = 16;
 
     /**
      * ConnectivityService notifies NetworkMonitor of DNS query responses event.
      * arg1 = returncode in OnDnsEvent which indicates the response code for the DNS query.
      */
-    public static final int EVENT_DNS_NOTIFICATION = BASE + 17;
+    public static final int EVENT_DNS_NOTIFICATION = 17;
 
     // Start mReevaluateDelayMs at this value and double.
     private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
@@ -245,7 +244,6 @@
     private final INetworkMonitorCallbacks mCallback;
     private final Network mNetwork;
     private final Network mNonPrivateDnsBypassNetwork;
-    private final int mNetId;
     private final TelephonyManager mTelephonyManager;
     private final WifiManager mWifiManager;
     private final ConnectivityManager mCm;
@@ -322,7 +320,7 @@
             NetworkRequest defaultRequest, IpConnectivityLog logger, SharedLog validationLogs,
             Dependencies deps) {
         // Add suffix indicating which NetworkMonitor we're talking about.
-        super(TAG + "/" + network.netId);
+        super(TAG + "/" + network.toString());
 
         // Logs with a tag of the form given just above, e.g.
         //     <timestamp>   862  2402 D NetworkMonitor/NetworkAgentInfo [WIFI () - 100]: ...
@@ -335,7 +333,6 @@
         mDependencies = deps;
         mNonPrivateDnsBypassNetwork = network;
         mNetwork = deps.getPrivateDnsBypassNetwork(network);
-        mNetId = mNetwork.netId;
         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
         mCm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -471,7 +468,7 @@
 
     @Override
     protected void log(String s) {
-        if (DBG) Log.d(TAG + "/" + mNetwork.netId, s);
+        if (DBG) Log.d(TAG + "/" + mNetwork.toString(), s);
     }
 
     private void validationLog(int probeType, Object url, String msg) {
@@ -795,7 +792,7 @@
         CustomIntentReceiver(String action, int token, int what) {
             mToken = token;
             mWhat = what;
-            mAction = action + "_" + mNetId + "_" + token;
+            mAction = action + "_" + mNetwork.getNetworkHandle() + "_" + token;
             mContext.registerReceiver(this, new IntentFilter(mAction));
         }
         public PendingIntent getPendingIntent() {
@@ -1088,11 +1085,6 @@
         return mDependencies.getSetting(mContext, Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1;
     }
 
-    private boolean getWifiScansAlwaysAvailableDisabled() {
-        return mDependencies.getSetting(
-                mContext, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0;
-    }
-
     private String getCaptivePortalServerHttpsUrl() {
         return mDependencies.getSetting(mContext,
                 Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL);
@@ -1484,7 +1476,7 @@
      */
     private void sendNetworkConditionsBroadcast(boolean responseReceived, boolean isCaptivePortal,
             long requestTimestampMs, long responseTimestampMs) {
-        if (getWifiScansAlwaysAvailableDisabled()) {
+        if (!mWifiManager.isScanAlwaysAvailable()) {
             return;
         }
 
@@ -1568,7 +1560,7 @@
 
     private void logNetworkEvent(int evtype) {
         int[] transports = mNetworkCapabilities.getTransportTypes();
-        mMetricsLog.log(mNetId, transports, new NetworkEvent(evtype));
+        mMetricsLog.log(mNetwork, transports, new NetworkEvent(evtype));
     }
 
     private int networkEventType(ValidationStage s, EvaluationResult r) {
@@ -1590,7 +1582,8 @@
     private void maybeLogEvaluationResult(int evtype) {
         if (mEvaluationTimer.isRunning()) {
             int[] transports = mNetworkCapabilities.getTransportTypes();
-            mMetricsLog.log(mNetId, transports, new NetworkEvent(evtype, mEvaluationTimer.stop()));
+            mMetricsLog.log(mNetwork, transports,
+                    new NetworkEvent(evtype, mEvaluationTimer.stop()));
             mEvaluationTimer.reset();
         }
     }
@@ -1603,7 +1596,7 @@
                 .setReturnCode(probeResult)
                 .setDurationMs(durationMs)
                 .build();
-        mMetricsLog.log(mNetId, transports, ev);
+        mMetricsLog.log(mNetwork, transports, ev);
     }
 
     @VisibleForTesting
@@ -1743,7 +1736,7 @@
         boolean result = false;
         // Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the
         // possible traffic cost in metered network.
-        if (mNetworkCapabilities.isMetered()
+        if (!mNetworkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && (SystemClock.elapsedRealtime() - getLastProbeTime()
                 < mDataStallMinEvaluateTime)) {
             return false;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index ff378b3..14322ec 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -124,6 +124,11 @@
      */
     @VisibleForTesting
     protected void startServiceForUser(int userId) {
+        if (mServiceUsers.get(userId) != null) {
+            Slog.i(TAG, "userId " + userId + " already started, so not starting again");
+            return;
+        }
+
         UserBackupManagerService userBackupManagerService =
                 UserBackupManagerService.createAndInitializeService(
                         userId, mContext, mTrampoline, mTransportWhitelist);
@@ -155,7 +160,12 @@
         }
     }
 
-    SparseArray<UserBackupManagerService> getServiceUsers() {
+    /**
+     *  Returns a lst of users currently unlocked that have a
+     *  {@link UserBackupManagerService} registered.
+     */
+    @VisibleForTesting
+    public SparseArray<UserBackupManagerService> getServiceUsers() {
         return mServiceUsers;
     }
 
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 4ca2545..b9a6f3c 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -32,7 +32,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -45,7 +44,6 @@
 import android.provider.Settings;
 import android.util.Slog;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.DumpUtils;
 
 import java.io.File;
@@ -65,8 +63,8 @@
  * following two ways:
  *
  * <ul>
- *   <li>Temporary - create the file {@link #BACKUP_SUPPRESS_FILENAME}, or
- *   <li>Permanent - set the system property {@link #BACKUP_DISABLE_PROPERTY} to true.
+ * <li>Temporary - create the file {@link #BACKUP_SUPPRESS_FILENAME}, or
+ * <li>Permanent - set the system property {@link #BACKUP_DISABLE_PROPERTY} to true.
  * </ul>
  *
  * Temporary disabling is controlled by {@link #setBackupServiceActive(int, boolean)} through
@@ -91,10 +89,9 @@
 
     private final Context mContext;
 
-    @GuardedBy("mStateLock")
-    private final File mSuppressFile;
-
     private final boolean mGlobalDisable;
+    // Lock to write backup suppress files.
+    // TODD(b/121198006): remove this object and synchronized all methods on "this".
     private final Object mStateLock = new Object();
 
     private volatile BackupManagerService mService;
@@ -104,9 +101,6 @@
     public Trampoline(Context context) {
         mContext = context;
         mGlobalDisable = isBackupDisabled();
-        mSuppressFile = getSuppressFile();
-        mSuppressFile.getParentFile().mkdirs();
-
         mHandlerThread = new HandlerThread(BACKUP_THREAD, Process.THREAD_PRIORITY_BACKGROUND);
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
@@ -132,11 +126,42 @@
         return Binder.getCallingUid();
     }
 
-    protected File getSuppressFile() {
-        return new File(new File(Environment.getDataDirectory(), "backup"),
+    protected File getSuppressFileForUser(int userId) {
+        return new File(UserBackupManagerFiles.getBaseStateDir(userId),
                 BACKUP_SUPPRESS_FILENAME);
     }
 
+    protected void createBackupSuppressFileForUser(int userId) throws IOException {
+        synchronized (mStateLock) {
+            getSuppressFileForUser(userId).getParentFile().mkdirs();
+            getSuppressFileForUser(userId).createNewFile();
+        }
+    }
+
+    private void deleteBackupSuppressFileForUser(int userId) {
+        if (!getSuppressFileForUser(userId).delete()) {
+            Slog.w(TAG, "Failed deleting backup suppressed file for user: " + userId);
+        }
+    }
+
+    // A user is ready for a backup if it's unlocked and is not suppressed by a device
+    // admin (device owner or profile owner).
+    private boolean isUserReadyForBackup(int userId) {
+        return mService != null && mService.getServiceUsers().get(userId) != null
+                && !isBackupSuppressedForUser(userId);
+    }
+
+    private boolean isBackupSuppressedForUser(int userId) {
+        // If backup is disabled for system user, it's disabled for all other users on device.
+        if (getSuppressFileForUser(UserHandle.USER_SYSTEM).exists()) {
+            return true;
+        }
+        if (userId != UserHandle.USER_SYSTEM) {
+            return getSuppressFileForUser(userId).exists();
+        }
+        return false;
+    }
+
     protected Context getContext() {
         return mContext;
     }
@@ -162,12 +187,9 @@
                         Slog.i(TAG, "Backup service not supported");
                         return;
                     }
-
                     synchronized (mStateLock) {
-                        if (!mSuppressFile.exists()) {
+                        if (mService == null) {
                             mService = createBackupManagerService();
-                        } else {
-                            Slog.i(TAG, "Backup service inactive");
                         }
                     }
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -190,10 +212,11 @@
     }
 
     private void startServiceForUser(int userId) {
-        BackupManagerService service = mService;
-        if (service != null) {
+        // We know that the user is unlocked here because it is called from setBackupServiceActive
+        // and unlockUser which have these guarantees. So we can check if the file exists.
+        if (mService != null && !isBackupSuppressedForUser(userId)) {
             Slog.i(TAG, "Starting service for user: " + userId);
-            service.startServiceForUser(userId);
+            mService.startServiceForUser(userId);
         }
     }
 
@@ -209,10 +232,9 @@
 
         postToHandler(
                 () -> {
-                    BackupManagerService service = mService;
-                    if (service != null) {
+                    if (mService != null) {
                         Slog.i(TAG, "Stopping service for user: " + userId);
-                        service.stopServiceForUser(userId);
+                        mService.stopServiceForUser(userId);
                     }
                 });
     }
@@ -221,6 +243,8 @@
      * Only privileged callers should be changing the backup state. This method only acts on {@link
      * UserHandle#USER_SYSTEM} and is a no-op if passed non-system users. Deactivating backup in the
      * system user also deactivates backup in all users.
+     *
+     * This call will only work if the calling {@code userID} is unlocked.
      */
     public void setBackupServiceActive(int userId, boolean makeActive) {
         int caller = binderGetCallingUid();
@@ -246,16 +270,21 @@
         synchronized (mStateLock) {
             Slog.i(TAG, "Making backup " + (makeActive ? "" : "in") + "active");
             if (makeActive) {
-                mService = createBackupManagerService();
-                mSuppressFile.delete();
+                if (mService == null) {
+                    mService = createBackupManagerService();
+                }
+                deleteBackupSuppressFileForUser(userId);
                 startServiceForUser(userId);
             } else {
-                mService = null;
                 try {
-                    mSuppressFile.createNewFile();
+                    //TODO(b/121198006): what if this throws an exception?
+                    createBackupSuppressFileForUser(userId);
                 } catch (IOException e) {
                     Slog.e(TAG, "Unable to persist backup service inactivity");
                 }
+                //TODO(b/121198006): loop through active users that have work profile and
+                // stop them as well.
+                stopUser(userId);
             }
         }
     }
@@ -271,20 +300,15 @@
      */
     @Override
     public boolean isBackupServiceActive(int userId) {
-        // TODO: http://b/22388012
-        if (userId == UserHandle.USER_SYSTEM) {
-            synchronized (mStateLock) {
-                return mService != null;
-            }
+        synchronized (mStateLock) {
+            return isUserReadyForBackup(userId);
         }
-        return false;
     }
 
     @Override
     public void dataChangedForUser(int userId, String packageName) throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.dataChanged(userId, packageName);
+        if (isUserReadyForBackup(userId)) {
+            mService.dataChanged(userId, packageName);
         }
     }
 
@@ -296,18 +320,16 @@
     @Override
     public void initializeTransportsForUser(
             int userId, String[] transportNames, IBackupObserver observer) throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.initializeTransports(userId, transportNames, observer);
+        if (isUserReadyForBackup(userId)) {
+            mService.initializeTransports(userId, transportNames, observer);
         }
     }
 
     @Override
     public void clearBackupDataForUser(int userId, String transportName, String packageName)
             throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.clearBackupData(userId, transportName, packageName);
+        if (isUserReadyForBackup(userId)) {
+            mService.clearBackupData(userId, transportName, packageName);
         }
     }
 
@@ -320,9 +342,8 @@
     @Override
     public void agentConnectedForUser(int userId, String packageName, IBinder agent)
             throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.agentConnected(userId, packageName, agent);
+        if (isUserReadyForBackup(userId)) {
+            mService.agentConnected(userId, packageName, agent);
         }
     }
 
@@ -333,9 +354,8 @@
 
     @Override
     public void agentDisconnectedForUser(int userId, String packageName) throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.agentDisconnected(userId, packageName);
+        if (isUserReadyForBackup(userId)) {
+            mService.agentDisconnected(userId, packageName);
         }
     }
 
@@ -347,9 +367,8 @@
     @Override
     public void restoreAtInstallForUser(int userId, String packageName, int token)
             throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.restoreAtInstall(userId, packageName, token);
+        if (isUserReadyForBackup(userId)) {
+            mService.restoreAtInstall(userId, packageName, token);
         }
     }
 
@@ -361,9 +380,8 @@
     @Override
     public void setBackupEnabledForUser(@UserIdInt int userId, boolean isEnabled)
             throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.setBackupEnabled(userId, isEnabled);
+        if (isUserReadyForBackup(userId)) {
+            mService.setBackupEnabled(userId, isEnabled);
         }
     }
 
@@ -374,9 +392,8 @@
 
     @Override
     public void setAutoRestoreForUser(int userId, boolean doAutoRestore) throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.setAutoRestore(userId, doAutoRestore);
+        if (isUserReadyForBackup(userId)) {
+            mService.setAutoRestore(userId, doAutoRestore);
         }
     }
 
@@ -387,8 +404,7 @@
 
     @Override
     public boolean isBackupEnabledForUser(@UserIdInt int userId) throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.isBackupEnabled(userId) : false;
+        return isUserReadyForBackup(userId) && mService.isBackupEnabled(userId);
     }
 
     @Override
@@ -398,21 +414,20 @@
 
     @Override
     public boolean setBackupPassword(String currentPw, String newPw) throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.setBackupPassword(currentPw, newPw) : false;
+        int userId = binderGetCallingUserId();
+        return (isUserReadyForBackup(userId)) && mService.setBackupPassword(currentPw, newPw);
     }
 
     @Override
     public boolean hasBackupPassword() throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.hasBackupPassword() : false;
+        int userId = binderGetCallingUserId();
+        return (isUserReadyForBackup(userId)) && mService.hasBackupPassword();
     }
 
     @Override
     public void backupNowForUser(@UserIdInt int userId) throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.backupNow(userId);
+        if (isUserReadyForBackup(userId)) {
+            mService.backupNow(userId);
         }
     }
 
@@ -425,9 +440,8 @@
             boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets,
             boolean allApps, boolean allIncludesSystem, boolean doCompress, boolean doKeyValue,
             String[] packageNames) throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.adbBackup(userId, fd, includeApks, includeObbs, includeShared, doWidgets,
+        if (isUserReadyForBackup(userId)) {
+            mService.adbBackup(userId, fd, includeApks, includeObbs, includeShared, doWidgets,
                     allApps, allIncludesSystem, doCompress, doKeyValue, packageNames);
         }
     }
@@ -435,17 +449,15 @@
     @Override
     public void fullTransportBackupForUser(int userId, String[] packageNames)
             throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.fullTransportBackup(userId, packageNames);
+        if (isUserReadyForBackup(userId)) {
+            mService.fullTransportBackup(userId, packageNames);
         }
     }
 
     @Override
     public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.adbRestore(userId, fd);
+        if (isUserReadyForBackup(userId)) {
+            mService.adbRestore(userId, fd);
         }
     }
 
@@ -458,9 +470,8 @@
             String encryptionPassword,
             IFullBackupRestoreObserver observer)
             throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.acknowledgeAdbBackupOrRestore(userId, token, allow,
+        if (isUserReadyForBackup(userId)) {
+            mService.acknowledgeAdbBackupOrRestore(userId, token, allow,
                     curPassword, encryptionPassword, observer);
         }
     }
@@ -468,8 +479,7 @@
     @Override
     public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword,
             String encryptionPassword, IFullBackupRestoreObserver observer)
-                    throws RemoteException {
-        BackupManagerService svc = mService;
+            throws RemoteException {
         acknowledgeFullBackupOrRestoreForUser(
                 binderGetCallingUserId(), token, allow, curPassword, encryptionPassword, observer);
     }
@@ -477,8 +487,7 @@
 
     @Override
     public String getCurrentTransportForUser(int userId) throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.getCurrentTransport(userId) : null;
+        return (isUserReadyForBackup(userId)) ? mService.getCurrentTransport(userId) : null;
     }
 
     @Override
@@ -493,14 +502,13 @@
     @Override
     @Nullable
     public ComponentName getCurrentTransportComponentForUser(int userId) {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.getCurrentTransportComponent(userId) : null;
+        return (isUserReadyForBackup(userId)) ? mService.getCurrentTransportComponent(userId)
+                : null;
     }
 
     @Override
     public String[] listAllTransportsForUser(int userId) throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.listAllTransports(userId) : null;
+        return (isUserReadyForBackup(userId)) ? mService.listAllTransports(userId) : null;
     }
 
     @Override
@@ -510,14 +518,14 @@
 
     @Override
     public ComponentName[] listAllTransportComponentsForUser(int userId) throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.listAllTransportComponents(userId) : null;
+        return (isUserReadyForBackup(userId)) ? mService.listAllTransportComponents(userId)
+                : null;
     }
 
     @Override
     public String[] getTransportWhitelist() {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.getTransportWhitelist() : null;
+        int userId = binderGetCallingUserId();
+        return (isUserReadyForBackup(userId)) ? mService.getTransportWhitelist() : null;
     }
 
     @Override
@@ -529,9 +537,9 @@
             String currentDestinationString,
             @Nullable Intent dataManagementIntent,
             String dataManagementLabel) {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.updateTransportAttributes(
+
+        if (isUserReadyForBackup(userId)) {
+            mService.updateTransportAttributes(
                     userId,
                     transportComponent,
                     name,
@@ -545,8 +553,8 @@
     @Override
     public String selectBackupTransportForUser(int userId, String transport)
             throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.selectBackupTransport(userId, transport) : null;
+        return (isUserReadyForBackup(userId)) ? mService.selectBackupTransport(userId, transport)
+                : null;
     }
 
     @Override
@@ -557,9 +565,8 @@
     @Override
     public void selectBackupTransportAsyncForUser(int userId, ComponentName transport,
             ISelectBackupTransportCallback listener) throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.selectBackupTransportAsync(userId, transport, listener);
+        if (isUserReadyForBackup(userId)) {
+            mService.selectBackupTransportAsync(userId, transport, listener);
         } else {
             if (listener != null) {
                 try {
@@ -574,8 +581,8 @@
     @Override
     public Intent getConfigurationIntentForUser(int userId, String transport)
             throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.getConfigurationIntent(userId, transport) : null;
+        return isUserReadyForBackup(userId) ? mService.getConfigurationIntent(userId, transport)
+                : null;
     }
 
     @Override
@@ -586,8 +593,8 @@
 
     @Override
     public String getDestinationStringForUser(int userId, String transport) throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.getDestinationString(userId, transport) : null;
+        return isUserReadyForBackup(userId) ? mService.getDestinationString(userId, transport)
+                : null;
     }
 
     @Override
@@ -598,8 +605,8 @@
     @Override
     public Intent getDataManagementIntentForUser(int userId, String transport)
             throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.getDataManagementIntent(userId, transport) : null;
+        return isUserReadyForBackup(userId) ? mService.getDataManagementIntent(userId, transport)
+                : null;
     }
 
     @Override
@@ -611,8 +618,8 @@
     @Override
     public String getDataManagementLabelForUser(int userId, String transport)
             throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.getDataManagementLabel(userId, transport) : null;
+        return isUserReadyForBackup(userId) ? mService.getDataManagementLabel(userId, transport)
+                : null;
     }
 
     @Override
@@ -624,44 +631,43 @@
     @Override
     public IRestoreSession beginRestoreSessionForUser(
             int userId, String packageName, String transportID) throws RemoteException {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.beginRestoreSession(userId, packageName, transportID) : null;
+        return isUserReadyForBackup(userId) ? mService.beginRestoreSession(userId, packageName,
+                transportID) : null;
     }
 
     @Override
     public void opComplete(int token, long result) throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.opComplete(binderGetCallingUserId(), token, result);
+        int userId = binderGetCallingUserId();
+        if (isUserReadyForBackup(userId)) {
+            mService.opComplete(binderGetCallingUserId(), token, result);
         }
     }
 
     @Override
     public long getAvailableRestoreTokenForUser(int userId, String packageName) {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.getAvailableRestoreToken(userId, packageName) : 0;
+        return isUserReadyForBackup(userId) ? mService.getAvailableRestoreToken(userId,
+                packageName) : 0;
     }
 
     @Override
     public boolean isAppEligibleForBackupForUser(int userId, String packageName) {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.isAppEligibleForBackup(userId, packageName) : false;
+        return isUserReadyForBackup(userId) && mService.isAppEligibleForBackup(userId,
+                packageName);
     }
 
     @Override
     public String[] filterAppsEligibleForBackupForUser(int userId, String[] packages) {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.filterAppsEligibleForBackup(userId, packages) : null;
+        return isUserReadyForBackup(userId) ? mService.filterAppsEligibleForBackup(userId,
+                packages) : null;
     }
 
     @Override
     public int requestBackupForUser(@UserIdInt int userId, String[] packages, IBackupObserver
             observer, IBackupManagerMonitor monitor, int flags) throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc == null) {
+        if (!isUserReadyForBackup(userId)) {
             return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
         }
-        return svc.requestBackup(userId, packages, observer, monitor, flags);
+        return mService.requestBackup(userId, packages, observer, monitor, flags);
     }
 
     @Override
@@ -673,9 +679,8 @@
 
     @Override
     public void cancelBackupsForUser(@UserIdInt int userId) throws RemoteException {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.cancelBackups(userId);
+        if (isUserReadyForBackup(userId)) {
+            mService.cancelBackups(userId);
         }
     }
 
@@ -687,10 +692,9 @@
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.dump(fd, pw, args);
+        int userId = binderGetCallingUserId();
+        if (isUserReadyForBackup(userId)) {
+            mService.dump(fd, pw, args);
         } else {
             pw.println("Inactive");
         }
@@ -699,14 +703,12 @@
     // Full backup/restore entry points - non-Binder; called directly
     // by the full-backup scheduled job
     /* package */ boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
-        BackupManagerService svc = mService;
-        return (svc != null) ? svc.beginFullBackup(userId, scheduledJob) : false;
+        return (isUserReadyForBackup(userId)) && mService.beginFullBackup(userId, scheduledJob);
     }
 
     /* package */ void endFullBackup(@UserIdInt int userId) {
-        BackupManagerService svc = mService;
-        if (svc != null) {
-            svc.endFullBackup(userId);
+        if (isUserReadyForBackup(userId)) {
+            mService.endFullBackup(userId);
         }
     }
 }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index b6dae19..a33f14b 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -33,6 +33,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ILauncherApps;
 import android.content.pm.IOnAppsChangedListener;
+import android.content.pm.LauncherApps;
 import android.content.pm.LauncherApps.ShortcutQuery;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -42,6 +43,8 @@
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
+import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
 import android.content.pm.UserInfo;
 import android.graphics.Rect;
 import android.net.Uri;
@@ -56,21 +59,31 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.provider.Settings;
+import android.util.ByteStringUtils;
 import android.util.Log;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.StatLogger;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Service that manages requests and callbacks for launchers that support
@@ -108,6 +121,17 @@
     static class LauncherAppsImpl extends ILauncherApps.Stub {
         private static final boolean DEBUG = false;
         private static final String TAG = "LauncherAppsService";
+
+        // Stats
+        @VisibleForTesting
+        interface Stats {
+            int INIT_VOUCHED_SIGNATURES = 0;
+            int COUNT = INIT_VOUCHED_SIGNATURES + 1;
+        }
+        private final StatLogger mStatLogger = new StatLogger(new String[] {
+                "initVouchedSignatures"
+        });
+
         private final Context mContext;
         private final UserManager mUm;
         private final UserManagerInternal mUserManagerInternal;
@@ -117,11 +141,16 @@
         private final PackageCallbackList<IOnAppsChangedListener> mListeners
                 = new PackageCallbackList<IOnAppsChangedListener>();
         private final DevicePolicyManager mDpm;
+        private final ConcurrentHashMap<UserHandle, Set<String>> mVouchedSignaturesByUser;
+        private final Set<String> mVouchProviders;
 
         private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+        private final VouchesChangedMonitor mVouchesChangedMonitor = new VouchesChangedMonitor();
 
         private final Handler mCallbackHandler;
 
+        private final Object mVouchedSignaturesLocked = new Object();
+
         public LauncherAppsImpl(Context context) {
             mContext = context;
             mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
@@ -136,6 +165,9 @@
             mShortcutServiceInternal.addListener(mPackageMonitor);
             mCallbackHandler = BackgroundThread.getHandler();
             mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+            mVouchedSignaturesByUser = new ConcurrentHashMap<>();
+            mVouchProviders = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
+            mVouchesChangedMonitor.register(mContext, UserHandle.ALL, true, mCallbackHandler);
         }
 
         @VisibleForTesting
@@ -355,7 +387,7 @@
                     }
                     ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName, /*flags*/ 0,
                             callingUid, user.getIdentifier());
-                    if (shouldShowHiddenApp(appInfo)) {
+                    if (shouldShowHiddenApp(user, appInfo)) {
                         ResolveInfo info = getHiddenAppActivityInfo(packageName, callingUid, user);
                         if (info != null) {
                             result.add(info);
@@ -371,7 +403,7 @@
                         user.getIdentifier(), callingUid);
                 for (ApplicationInfo applicationInfo : installedPackages) {
                     if (!visiblePackages.contains(applicationInfo.packageName)) {
-                        if (!shouldShowHiddenApp(applicationInfo)) {
+                        if (!shouldShowHiddenApp(user, applicationInfo)) {
                             continue;
                         }
                         ResolveInfo info = getHiddenAppActivityInfo(applicationInfo.packageName,
@@ -387,13 +419,130 @@
             }
         }
 
-        private static boolean shouldShowHiddenApp(ApplicationInfo appInfo) {
+        private boolean shouldShowHiddenApp(UserHandle user, ApplicationInfo appInfo) {
             if (appInfo == null || appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) {
                 return false;
             }
+            if (!mVouchedSignaturesByUser.containsKey(user)) {
+                initVouchedSignatures(user);
+            }
+            if (mVouchProviders.contains(appInfo.packageName)) {
+                // If it's a vouching packages then we must show hidden app
+                return true;
+            }
+            // If app's signature is in vouch list, do not show hidden app
+            final Set<String> vouches = mVouchedSignaturesByUser.get(user);
+            try {
+                final PackageInfo pkgInfo = mContext.getPackageManager().getPackageInfo(
+                        appInfo.packageName, PackageManager.GET_SIGNING_CERTIFICATES);
+                final Signature[] signatures = getLatestSignatures(pkgInfo.signingInfo);
+                // If any of the signatures appears in vouches, then we don't show hidden app
+                for (Signature signature : signatures) {
+                    final String certDigest = computePackageCertDigest(signature);
+                    if (vouches.contains(certDigest)) {
+                        return false;
+                    }
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                // Should not happen
+            }
             return true;
         }
 
+        @VisibleForTesting
+        static String computePackageCertDigest(Signature signature) {
+            MessageDigest messageDigest;
+            try {
+                messageDigest = MessageDigest.getInstance("SHA1");
+            } catch (NoSuchAlgorithmException e) {
+                // Should not happen
+                return null;
+            }
+            messageDigest.update(signature.toByteArray());
+            final byte[] digest = messageDigest.digest();
+            return ByteStringUtils.toHexString(digest);
+        }
+
+        @VisibleForTesting
+        static Signature[] getLatestSignatures(SigningInfo signingInfo) {
+            if (signingInfo.hasMultipleSigners()) {
+                return signingInfo.getApkContentsSigners();
+            } else {
+                final Signature[] signatures = signingInfo.getSigningCertificateHistory();
+                return new Signature[]{signatures[0]};
+            }
+        }
+
+        private void updateVouches(String packageName, UserHandle user) {
+            final PackageManagerInternal pmInt =
+                    LocalServices.getService(PackageManagerInternal.class);
+            ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName,
+                    PackageManager.GET_META_DATA, Binder.getCallingUid(), user.getIdentifier());
+            if (appInfo == null) {
+                Log.w(TAG, "appInfo " + packageName + " is null");
+                return;
+            }
+            updateVouches(appInfo, user);
+        }
+
+        private void updateVouches(ApplicationInfo appInfo, UserHandle user) {
+            if (appInfo == null || appInfo.metaData == null) {
+                // No meta-data
+                return;
+            }
+            int tokenResourceId = appInfo.metaData.getInt(LauncherApps.VOUCHED_CERTS_KEY);
+            if (tokenResourceId == 0) {
+                // No xml file
+                return;
+            }
+            mVouchProviders.add(appInfo.packageName);
+            Set<String> vouches = mVouchedSignaturesByUser.get(user);
+            try {
+                List<String> signatures = Arrays.asList(
+                        mContext.getPackageManager().getResourcesForApplication(
+                                appInfo.packageName).getStringArray(tokenResourceId));
+                for (String signature : signatures) {
+                    vouches.add(signature.toUpperCase());
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                // Should not happen
+            }
+        }
+
+        private void initVouchedSignatures(UserHandle user) {
+            synchronized (mVouchedSignaturesLocked) {
+                if (mVouchedSignaturesByUser.contains(user)) {
+                    return;
+                }
+                final long startTime = mStatLogger.getTime();
+
+                Set<String> vouches = Collections.newSetFromMap(
+                        new ConcurrentHashMap<String, Boolean>());
+
+                final int callingUid = injectBinderCallingUid();
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    final PackageManagerInternal pmInt =
+                            LocalServices.getService(PackageManagerInternal.class);
+                    List<ApplicationInfo> installedPackages = pmInt.getInstalledApplications(
+                            PackageManager.GET_META_DATA, user.getIdentifier(), callingUid);
+                    for (ApplicationInfo appInfo : installedPackages) {
+                        updateVouches(appInfo, user);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+                mVouchedSignaturesByUser.putIfAbsent(user, vouches);
+                mStatLogger.logDurationStat(Stats.INIT_VOUCHED_SIGNATURES, startTime);
+            }
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
+            mStatLogger.dump(pw, "  ");
+        }
+
         @Override
         public ActivityInfo resolveActivity(
                 String callingPackage, ComponentName component, UserHandle user)
@@ -760,6 +909,18 @@
             mCallbackHandler.post(r);
         }
 
+        private class VouchesChangedMonitor extends PackageMonitor {
+            @Override
+            public void onPackageAdded(String packageName, int uid) {
+                updateVouches(packageName, new UserHandle(getChangingUserId()));
+            }
+
+            @Override
+            public void onPackageModified(String packageName) {
+                updateVouches(packageName, new UserHandle(getChangingUserId()));
+            }
+        }
+
         private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
 
             // TODO Simplify with lambdas.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6527ca0..8026a04 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -30,11 +30,14 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.FLAG_PRIVATE;
 import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 import static android.view.View.GONE;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
 import static android.view.WindowManager.DOCKED_BOTTOM;
 import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_TOP;
@@ -164,6 +167,7 @@
 import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
 import android.view.View;
+import android.view.ViewRootImpl;
 import android.view.WindowManager;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
 
@@ -1051,6 +1055,11 @@
      */
     void setInsetProvider(@InternalInsetType int type, WindowState win,
             @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
+        if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL) {
+            if (type == TYPE_TOP_BAR || type == TYPE_NAVIGATION_BAR) {
+                return;
+            }
+        }
         mInsetsStateController.getSourceProvider(type).setWindow(win, frameProvider);
     }
 
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index d6f1616..e49e4c0 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Rect;
@@ -159,7 +161,7 @@
     }
 
     boolean isClientVisible() {
-        return !ViewRootImpl.USE_NEW_INSETS || mClientVisible;
+        return ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE || mClientVisible;
     }
 
     private class ControlAdapter implements AnimationAdapter {
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index bc01f7c..32dbe96 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -19,6 +19,9 @@
 import static android.view.InsetsState.TYPE_IME;
 import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
+import static android.view.ViewRootImpl.sNewInsetsMode;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -160,7 +163,7 @@
     }
 
     private void onControlChanged(int type, @Nullable WindowState win) {
-        if (!ViewRootImpl.USE_NEW_INSETS) {
+        if (sNewInsetsMode == NEW_INSETS_MODE_NONE) {
             return;
         }
         final WindowState previous = mTypeWinControlMap.get(type);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f8e24ff..0977323 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -7656,18 +7656,7 @@
             }
 
             // Shutting down backup manager service permanently.
-            long ident = mInjector.binderClearCallingIdentity();
-            try {
-                if (mInjector.getIBackupManager() != null) {
-                    mInjector.getIBackupManager()
-                            .setBackupServiceActive(UserHandle.USER_SYSTEM, false);
-                }
-            } catch (RemoteException e) {
-                throw new IllegalStateException("Failed deactivating backup service.", e);
-            } finally {
-                mInjector.binderRestoreCallingIdentity(ident);
-            }
-
+            toggleBackupServiceActive(UserHandle.USER_SYSTEM, /* makeActive= */ false);
             if (isAdb()) {
                 // Log device owner provisioning was started using adb.
                 MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_DEVICE_OWNER);
@@ -7695,7 +7684,7 @@
                 saveUserRestrictionsLocked(userId);
             }
 
-            ident = mInjector.binderClearCallingIdentity();
+            long ident = mInjector.binderClearCallingIdentity();
             try {
                 // TODO Send to system too?
                 sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, userId);
@@ -7952,6 +7941,9 @@
                         .write();
             }
 
+            // Shutting down backup manager service permanently.
+            toggleBackupServiceActive(userHandle, /* makeActive= */ false);
+
             mOwners.setProfileOwner(who, ownerName, userHandle);
             mOwners.writeProfileOwner(userHandle);
             Slog.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle);
@@ -7975,6 +7967,24 @@
         }
     }
 
+
+    private void toggleBackupServiceActive(int userId, boolean makeActive) {
+        // Shutting down backup manager service permanently.
+        enforceUserUnlocked(userId);
+        long ident = mInjector.binderClearCallingIdentity();
+        try {
+            if (mInjector.getIBackupManager() != null) {
+                mInjector.getIBackupManager()
+                        .setBackupServiceActive(userId, makeActive);
+            }
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Failed deactivating backup service.", e);
+        } finally {
+            mInjector.binderRestoreCallingIdentity(ident);
+        }
+
+    }
+
     @Override
     public void clearProfileOwner(ComponentName who) {
         if (!mHasFeature) {
@@ -12727,22 +12737,9 @@
             return;
         }
         Preconditions.checkNotNull(admin);
-        synchronized (getLockObject()) {
-            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
-
-        final long ident = mInjector.binderClearCallingIdentity();
-        try {
-            IBackupManager ibm = mInjector.getIBackupManager();
-            if (ibm != null) {
-                ibm.setBackupServiceActive(UserHandle.USER_SYSTEM, enabled);
-            }
-        } catch (RemoteException e) {
-            throw new IllegalStateException(
-                "Failed " + (enabled ? "" : "de") + "activating backup service.", e);
-        } finally {
-            mInjector.binderRestoreCallingIdentity(ident);
-        }
+        enforceProfileOrDeviceOwner(admin);
+        int userId = mInjector.userHandleGetCallingUserId();
+        toggleBackupServiceActive(userId, enabled);
     }
 
     @Override
@@ -12751,11 +12748,13 @@
         if (!mHasFeature) {
             return true;
         }
+
+        enforceProfileOrDeviceOwner(admin);
         synchronized (getLockObject()) {
             try {
-                getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
                 IBackupManager ibm = mInjector.getIBackupManager();
-                return ibm != null && ibm.isBackupServiceActive(UserHandle.USER_SYSTEM);
+                return ibm != null && ibm.isBackupServiceActive(
+                    mInjector.userHandleGetCallingUserId());
             } catch (RemoteException e) {
                 throw new IllegalStateException("Failed requesting backup service state.", e);
             }
@@ -13009,7 +13008,12 @@
                 throw new IllegalStateException("logging is not available");
             }
             if (mNetworkLogger != null) {
-                return mNetworkLogger.forceBatchFinalization();
+                final long ident = mInjector.binderClearCallingIdentity();
+                try {
+                    return mNetworkLogger.forceBatchFinalization();
+                } finally {
+                    mInjector.binderRestoreCallingIdentity(ident);
+                }
             }
             return 0;
         }
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 3351b25..4943952 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -20,6 +20,7 @@
 import static android.net.util.NetworkConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
 import static android.net.util.NetworkConstants.ICMPV6_ROUTER_ADVERTISEMENT;
 import static android.net.util.NetworkConstants.ICMPV6_ROUTER_SOLICITATION;
+import static android.net.util.SocketUtils.makePacketSocketAddress;
 import static android.system.OsConstants.AF_PACKET;
 import static android.system.OsConstants.ARPHRD_ETHER;
 import static android.system.OsConstants.ETH_P_ARP;
@@ -55,7 +56,6 @@
 import android.os.SystemClock;
 import android.system.ErrnoException;
 import android.system.Os;
-import android.system.PacketSocketAddress;
 import android.text.format.DateUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -72,6 +72,7 @@
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
+import java.net.SocketAddress;
 import java.net.SocketException;
 import java.net.UnknownHostException;
 import java.nio.BufferUnderflowException;
@@ -472,7 +473,7 @@
                 installNewProgramLocked();
             }
             socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6);
-            PacketSocketAddress addr = new PacketSocketAddress(
+            SocketAddress addr = makePacketSocketAddress(
                     (short) ETH_P_IPV6, mInterfaceParams.index);
             Os.bind(socket, addr);
             NetworkUtils.attachRaFilter(socket, mApfCapabilities.apfPacketFormat);
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 15acc0e..04ac9a3 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -28,6 +28,7 @@
 import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
 import static android.net.dhcp.DhcpPacket.INADDR_ANY;
 import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
+import static android.net.util.SocketUtils.makePacketSocketAddress;
 import static android.system.OsConstants.AF_INET;
 import static android.system.OsConstants.AF_PACKET;
 import static android.system.OsConstants.ETH_P_IP;
@@ -35,7 +36,6 @@
 import static android.system.OsConstants.SOCK_DGRAM;
 import static android.system.OsConstants.SOCK_RAW;
 import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_BINDTODEVICE;
 import static android.system.OsConstants.SO_BROADCAST;
 import static android.system.OsConstants.SO_RCVBUF;
 import static android.system.OsConstants.SO_REUSEADDR;
@@ -44,23 +44,22 @@
 import android.net.DhcpResults;
 import android.net.NetworkUtils;
 import android.net.TrafficStats;
+import android.net.ip.IpClient;
 import android.net.metrics.DhcpClientEvent;
 import android.net.metrics.DhcpErrorEvent;
 import android.net.metrics.IpConnectivityLog;
 import android.net.util.InterfaceParams;
+import android.net.util.SocketUtils;
 import android.os.Message;
 import android.os.SystemClock;
 import android.system.ErrnoException;
 import android.system.Os;
-import android.system.PacketSocketAddress;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.SparseArray;
-import android.util.TimeUtils;
 
 import com.android.internal.util.HexDump;
 import com.android.internal.util.MessageUtils;
-import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.internal.util.WakeupMessage;
@@ -70,6 +69,7 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.net.Inet4Address;
+import java.net.SocketAddress;
 import java.net.SocketException;
 import java.nio.ByteBuffer;
 import java.util.Arrays;
@@ -117,7 +117,8 @@
     // t=0, t=2, t=6, t=14, t=30, allowing for 10% jitter.
     private static final int DHCP_TIMEOUT_MS    =  36 * SECONDS;
 
-    private static final int PUBLIC_BASE = Protocol.BASE_DHCP;
+    // DhcpClient uses IpClient's handler.
+    private static final int PUBLIC_BASE = IpClient.DHCPCLIENT_CMD_BASE;
 
     /* Commands from controller to start/stop DHCP */
     public static final int CMD_START_DHCP                  = PUBLIC_BASE + 1;
@@ -147,7 +148,7 @@
     public static final int DHCP_FAILURE = 2;
 
     // Internal messages.
-    private static final int PRIVATE_BASE         = Protocol.BASE_DHCP + 100;
+    private static final int PRIVATE_BASE         = IpClient.DHCPCLIENT_CMD_BASE + 100;
     private static final int CMD_KICK             = PRIVATE_BASE + 1;
     private static final int CMD_RECEIVED_PACKET  = PRIVATE_BASE + 2;
     private static final int CMD_TIMEOUT          = PRIVATE_BASE + 3;
@@ -204,7 +205,7 @@
     private InterfaceParams mIface;
     // TODO: MacAddress-ify more of this class hierarchy.
     private byte[] mHwAddr;
-    private PacketSocketAddress mInterfaceBroadcastAddr;
+    private SocketAddress mInterfaceBroadcastAddr;
     private int mTransactionId;
     private long mTransactionStartMillis;
     private DhcpResults mDhcpLease;
@@ -293,7 +294,7 @@
         }
 
         mHwAddr = mIface.macAddr.toByteArray();
-        mInterfaceBroadcastAddr = new PacketSocketAddress(mIface.index, DhcpPacket.ETHER_BROADCAST);
+        mInterfaceBroadcastAddr = makePacketSocketAddress(mIface.index, DhcpPacket.ETHER_BROADCAST);
         return true;
     }
 
@@ -309,7 +310,7 @@
     private boolean initPacketSocket() {
         try {
             mPacketSock = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IP);
-            PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IP, mIface.index);
+            SocketAddress addr = makePacketSocketAddress((short) ETH_P_IP, mIface.index);
             Os.bind(mPacketSock, addr);
             NetworkUtils.attachDhcpFilter(mPacketSock);
         } catch(SocketException|ErrnoException e) {
@@ -323,12 +324,11 @@
         final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_DHCP);
         try {
             mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+            SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName);
             Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
-            Os.setsockoptIfreq(mUdpSock, SOL_SOCKET, SO_BINDTODEVICE, mIfaceName);
             Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1);
             Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0);
             Os.bind(mUdpSock, Inet4Address.ANY, DhcpPacket.DHCP_CLIENT);
-            NetworkUtils.protectFromVpn(mUdpSock);
         } catch(SocketException|ErrnoException e) {
             Log.e(TAG, "Error creating UDP socket", e);
             return false;
@@ -544,13 +544,13 @@
 
         private String messageToString(Message message) {
             long now = SystemClock.uptimeMillis();
-            StringBuilder b = new StringBuilder(" ");
-            TimeUtils.formatDuration(message.getWhen() - now, b);
-            b.append(" ").append(messageName(message.what))
+            return new StringBuilder(" ")
+                    .append(message.getWhen() - now)
+                    .append(messageName(message.what))
                     .append(" ").append(message.arg1)
                     .append(" ").append(message.arg2)
-                    .append(" ").append(message.obj);
-            return b.toString();
+                    .append(" ").append(message.obj)
+                    .toString();
         }
 
         @Override
diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
index bef425a..385dd52 100644
--- a/services/net/java/android/net/ip/ConnectivityPacketTracker.java
+++ b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
@@ -16,6 +16,7 @@
 
 package android.net.ip;
 
+import static android.net.util.SocketUtils.makePacketSocketAddress;
 import static android.system.OsConstants.AF_PACKET;
 import static android.system.OsConstants.ARPHRD_ETHER;
 import static android.system.OsConstants.ETH_P_ALL;
@@ -28,7 +29,6 @@
 import android.os.Handler;
 import android.system.ErrnoException;
 import android.system.Os;
-import android.system.PacketSocketAddress;
 import android.text.TextUtils;
 import android.util.LocalLog;
 import android.util.Log;
@@ -103,7 +103,7 @@
             try {
                 s = Os.socket(AF_PACKET, SOCK_RAW, 0);
                 NetworkUtils.attachControlPacketFilter(s, ARPHRD_ETHER);
-                Os.bind(s, new PacketSocketAddress((short) ETH_P_ALL, mInterface.index));
+                Os.bind(s, makePacketSocketAddress((short) ETH_P_ALL, mInterface.index));
             } catch (ErrnoException | IOException e) {
                 logError("Failed to create packet tracking socket: ", e);
                 closeFd(s);
diff --git a/services/net/java/android/net/ip/InterfaceController.java b/services/net/java/android/net/ip/InterfaceController.java
index 55dfcef..b3af67c 100644
--- a/services/net/java/android/net/ip/InterfaceController.java
+++ b/services/net/java/android/net/ip/InterfaceController.java
@@ -18,13 +18,14 @@
 
 import android.net.INetd;
 import android.net.InterfaceConfiguration;
+import android.net.InterfaceConfigurationParcel;
 import android.net.LinkAddress;
 import android.net.util.SharedLog;
-import android.os.INetworkManagementService;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.system.OsConstants;
 
+import java.net.Inet4Address;
 import java.net.InetAddress;
 
 
@@ -39,76 +40,96 @@
     private final static boolean DBG = false;
 
     private final String mIfName;
-    private final INetworkManagementService mNMS;
     private final INetd mNetd;
     private final SharedLog mLog;
 
-    public InterfaceController(String ifname, INetworkManagementService nms, INetd netd,
-            SharedLog log) {
+    public InterfaceController(String ifname, INetd netd, SharedLog log) {
         mIfName = ifname;
-        mNMS = nms;
         mNetd = netd;
         mLog = log;
     }
 
+    private boolean setInterfaceConfig(InterfaceConfiguration config) {
+        final InterfaceConfigurationParcel cfgParcel = config.toParcel(mIfName);
+
+        try {
+            mNetd.interfaceSetCfg(cfgParcel);
+        } catch (RemoteException | ServiceSpecificException e) {
+            logError("Setting IPv4 address to %s/%d failed: %s",
+                    cfgParcel.ipv4Addr, cfgParcel.prefixLength, e);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Set the IPv4 address of the interface.
+     */
     public boolean setIPv4Address(LinkAddress address) {
-        final InterfaceConfiguration ifcg = new InterfaceConfiguration();
-        ifcg.setLinkAddress(address);
-        try {
-            mNMS.setInterfaceConfig(mIfName, ifcg);
-            if (DBG) mLog.log("IPv4 configuration succeeded");
-        } catch (IllegalStateException | RemoteException e) {
-            logError("IPv4 configuration failed: %s", e);
+        if (!(address.getAddress() instanceof Inet4Address)) {
             return false;
         }
-        return true;
+        final InterfaceConfiguration ifConfig = new InterfaceConfiguration();
+        ifConfig.setLinkAddress(address);
+        return setInterfaceConfig(ifConfig);
     }
 
+    /**
+     * Clear the IPv4Address of the interface.
+     */
     public boolean clearIPv4Address() {
+        final InterfaceConfiguration ifConfig = new InterfaceConfiguration();
+        ifConfig.setLinkAddress(new LinkAddress("0.0.0.0/0"));
+        return setInterfaceConfig(ifConfig);
+    }
+
+    private boolean setEnableIPv6(boolean enabled) {
         try {
-            final InterfaceConfiguration ifcg = new InterfaceConfiguration();
-            ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
-            mNMS.setInterfaceConfig(mIfName, ifcg);
-        } catch (IllegalStateException | RemoteException e) {
-            logError("Failed to clear IPv4 address on interface %s: %s", mIfName, e);
+            mNetd.interfaceSetEnableIPv6(mIfName, enabled);
+        } catch (RemoteException | ServiceSpecificException e) {
+            logError("%s IPv6 failed: %s", (enabled ? "enabling" : "disabling"), e);
             return false;
         }
         return true;
     }
 
+    /**
+     * Enable IPv6 on the interface.
+     */
     public boolean enableIPv6() {
-        try {
-            mNMS.enableIpv6(mIfName);
-        } catch (IllegalStateException | RemoteException e) {
-            logError("enabling IPv6 failed: %s", e);
-            return false;
-        }
-        return true;
+        return setEnableIPv6(true);
     }
 
+    /**
+     * Disable IPv6 on the interface.
+     */
     public boolean disableIPv6() {
-        try {
-            mNMS.disableIpv6(mIfName);
-        } catch (IllegalStateException | RemoteException e) {
-            logError("disabling IPv6 failed: %s", e);
-            return false;
-        }
-        return true;
+        return setEnableIPv6(false);
     }
 
+    /**
+     * Enable or disable IPv6 privacy extensions on the interface.
+     * @param enabled Whether the extensions should be enabled.
+     */
     public boolean setIPv6PrivacyExtensions(boolean enabled) {
         try {
-            mNMS.setInterfaceIpv6PrivacyExtensions(mIfName, enabled);
-        } catch (IllegalStateException | RemoteException e) {
-            logError("error setting IPv6 privacy extensions: %s", e);
+            mNetd.interfaceSetIPv6PrivacyExtensions(mIfName, enabled);
+        } catch (RemoteException | ServiceSpecificException e) {
+            logError("error %s IPv6 privacy extensions: %s",
+                    (enabled ? "enabling" : "disabling"), e);
             return false;
         }
         return true;
     }
 
+    /**
+     * Set IPv6 address generation mode on the interface.
+     *
+     * <p>IPv6 should be disabled before changing the mode.
+     */
     public boolean setIPv6AddrGenModeIfSupported(int mode) {
         try {
-            mNMS.setIPv6AddrGenMode(mIfName, mode);
+            mNetd.setIPv6AddrGenMode(mIfName, mode);
         } catch (RemoteException e) {
             logError("Unable to set IPv6 addrgen mode: %s", e);
             return false;
@@ -121,10 +142,16 @@
         return true;
     }
 
+    /**
+     * Add an address to the interface.
+     */
     public boolean addAddress(LinkAddress addr) {
         return addAddress(addr.getAddress(), addr.getPrefixLength());
     }
 
+    /**
+     * Add an address to the interface.
+     */
     public boolean addAddress(InetAddress ip, int prefixLen) {
         try {
             mNetd.interfaceAddAddress(mIfName, ip.getHostAddress(), prefixLen);
@@ -135,6 +162,9 @@
         return true;
     }
 
+    /**
+     * Remove an address from the interface.
+     */
     public boolean removeAddress(InetAddress ip, int prefixLen) {
         try {
             mNetd.interfaceDelAddress(mIfName, ip.getHostAddress(), prefixLen);
@@ -145,9 +175,12 @@
         return true;
     }
 
+    /**
+     * Remove all addresses from the interface.
+     */
     public boolean clearAllAddresses() {
         try {
-            mNMS.clearInterfaceAddresses(mIfName);
+            mNetd.interfaceClearAddrs(mIfName);
         } catch (Exception e) {
             logError("Failed to clear addresses: %s", e);
             return false;
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 9f15573..8187ac5 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -24,7 +24,6 @@
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
-import android.net.LinkProperties.ProvisioningChange;
 import android.net.Network;
 import android.net.ProvisioningConfigurationParcelable;
 import android.net.ProxyInfo;
@@ -359,6 +358,9 @@
     private static final int CMD_JUMP_RUNNING_TO_STOPPING         = 101;
     private static final int CMD_JUMP_STOPPING_TO_STOPPED         = 102;
 
+    // IpClient shares a handler with DhcpClient: commands must not overlap
+    public static final int DHCPCLIENT_CMD_BASE = 1000;
+
     private static final int MAX_LOG_RECORDS = 500;
     private static final int MAX_PACKET_RECORDS = 100;
 
@@ -371,6 +373,11 @@
 
     private static final int IMMEDIATE_FAILURE_DURATION = 0;
 
+    private static final int PROV_CHANGE_STILL_NOT_PROVISIONED = 1;
+    private static final int PROV_CHANGE_LOST_PROVISIONING = 2;
+    private static final int PROV_CHANGE_GAINED_PROVISIONING = 3;
+    private static final int PROV_CHANGE_STILL_PROVISIONED = 4;
+
     private final State mStoppedState = new StoppedState();
     private final State mStoppingState = new StoppingState();
     private final State mStartedState = new StartedState();
@@ -479,7 +486,7 @@
 
         // TODO: Consider creating, constructing, and passing in some kind of
         // InterfaceController.Dependencies class.
-        mInterfaceCtrl = new InterfaceController(mInterfaceName, mNwService, deps.getNetd(), mLog);
+        mInterfaceCtrl = new InterfaceController(mInterfaceName, deps.getNetd(), mLog);
 
         mNetlinkTracker = new NetlinkTracker(
                 mInterfaceName,
@@ -911,18 +918,18 @@
     // object that is a correct and complete assessment of what changed, taking
     // account of the asymmetries described in the comments in this function.
     // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
-    private ProvisioningChange compareProvisioning(LinkProperties oldLp, LinkProperties newLp) {
-        ProvisioningChange delta;
+    private int compareProvisioning(LinkProperties oldLp, LinkProperties newLp) {
+        int delta;
         InitialConfiguration config = mConfiguration != null ? mConfiguration.mInitialConfig : null;
         final boolean wasProvisioned = isProvisioned(oldLp, config);
         final boolean isProvisioned = isProvisioned(newLp, config);
 
         if (!wasProvisioned && isProvisioned) {
-            delta = ProvisioningChange.GAINED_PROVISIONING;
+            delta = PROV_CHANGE_GAINED_PROVISIONING;
         } else if (wasProvisioned && isProvisioned) {
-            delta = ProvisioningChange.STILL_PROVISIONED;
+            delta = PROV_CHANGE_STILL_PROVISIONED;
         } else if (!wasProvisioned && !isProvisioned) {
-            delta = ProvisioningChange.STILL_NOT_PROVISIONED;
+            delta = PROV_CHANGE_STILL_NOT_PROVISIONED;
         } else {
             // (wasProvisioned && !isProvisioned)
             //
@@ -934,7 +941,7 @@
             // that to be a network without DNS servers and connect anyway.
             //
             // See the comment below.
-            delta = ProvisioningChange.LOST_PROVISIONING;
+            delta = PROV_CHANGE_LOST_PROVISIONING;
         }
 
         final boolean lostIPv6 = oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned();
@@ -970,7 +977,7 @@
         // delta will never be LOST_PROVISIONING. So check for loss of
         // provisioning here too.
         if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) {
-            delta = ProvisioningChange.LOST_PROVISIONING;
+            delta = PROV_CHANGE_LOST_PROVISIONING;
         }
 
         // Additionally:
@@ -979,15 +986,15 @@
         // IPv6 default route then also consider the loss of that default route
         // to be a loss of provisioning. See b/27962810.
         if (oldLp.hasGlobalIPv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) {
-            delta = ProvisioningChange.LOST_PROVISIONING;
+            delta = PROV_CHANGE_LOST_PROVISIONING;
         }
 
         return delta;
     }
 
-    private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
+    private void dispatchCallback(int delta, LinkProperties newLp) {
         switch (delta) {
-            case GAINED_PROVISIONING:
+            case PROV_CHANGE_GAINED_PROVISIONING:
                 if (DBG) {
                     Log.d(mTag, "onProvisioningSuccess()");
                 }
@@ -995,7 +1002,7 @@
                 mCallback.onProvisioningSuccess(newLp);
                 break;
 
-            case LOST_PROVISIONING:
+            case PROV_CHANGE_LOST_PROVISIONING:
                 if (DBG) {
                     Log.d(mTag, "onProvisioningFailure()");
                 }
@@ -1015,7 +1022,7 @@
     // Updates all IpClient-related state concerned with LinkProperties.
     // Returns a ProvisioningChange for possibly notifying other interested
     // parties that are not fronted by IpClient.
-    private ProvisioningChange setLinkProperties(LinkProperties newLp) {
+    private int setLinkProperties(LinkProperties newLp) {
         if (mApfFilter != null) {
             mApfFilter.setLinkProperties(newLp);
         }
@@ -1023,10 +1030,10 @@
             mIpReachabilityMonitor.updateLinkProperties(newLp);
         }
 
-        ProvisioningChange delta = compareProvisioning(mLinkProperties, newLp);
+        int delta = compareProvisioning(mLinkProperties, newLp);
         mLinkProperties = new LinkProperties(newLp);
 
-        if (delta == ProvisioningChange.GAINED_PROVISIONING) {
+        if (delta == PROV_CHANGE_GAINED_PROVISIONING) {
             // TODO: Add a proper ProvisionedState and cancel the alarm in
             // its enter() method.
             mProvisioningTimeoutAlarm.cancel();
@@ -1122,17 +1129,17 @@
         if (Objects.equals(newLp, mLinkProperties)) {
             return true;
         }
-        final ProvisioningChange delta = setLinkProperties(newLp);
+        final int delta = setLinkProperties(newLp);
         if (sendCallbacks) {
             dispatchCallback(delta, newLp);
         }
-        return (delta != ProvisioningChange.LOST_PROVISIONING);
+        return (delta != PROV_CHANGE_LOST_PROVISIONING);
     }
 
     private void handleIPv4Success(DhcpResults dhcpResults) {
         mDhcpResults = new DhcpResults(dhcpResults);
         final LinkProperties newLp = assembleLinkProperties();
-        final ProvisioningChange delta = setLinkProperties(newLp);
+        final int delta = setLinkProperties(newLp);
 
         if (DBG) {
             Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
@@ -1160,7 +1167,7 @@
 
     private void handleProvisioningFailure() {
         final LinkProperties newLp = assembleLinkProperties();
-        ProvisioningChange delta = setLinkProperties(newLp);
+        int delta = setLinkProperties(newLp);
         // If we've gotten here and we're still not provisioned treat that as
         // a total loss of provisioning.
         //
@@ -1169,12 +1176,12 @@
         // timeout expired.
         //
         // Regardless: GAME OVER.
-        if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) {
-            delta = ProvisioningChange.LOST_PROVISIONING;
+        if (delta == PROV_CHANGE_STILL_NOT_PROVISIONED) {
+            delta = PROV_CHANGE_LOST_PROVISIONING;
         }
 
         dispatchCallback(delta, newLp);
-        if (delta == ProvisioningChange.LOST_PROVISIONING) {
+        if (delta == PROV_CHANGE_LOST_PROVISIONING) {
             transitionTo(mStoppingState);
         }
     }
@@ -1646,7 +1653,7 @@
                         mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
                     } else {
                         logError("Failed to set IPv4 address.");
-                        dispatchCallback(ProvisioningChange.LOST_PROVISIONING,
+                        dispatchCallback(PROV_CHANGE_LOST_PROVISIONING,
                                 new LinkProperties(mLinkProperties));
                         transitionTo(mStoppingState);
                     }
diff --git a/services/net/java/android/net/ip/IpNeighborMonitor.java b/services/net/java/android/net/ip/IpNeighborMonitor.java
index 34bf4b6..eb993a4 100644
--- a/services/net/java/android/net/ip/IpNeighborMonitor.java
+++ b/services/net/java/android/net/ip/IpNeighborMonitor.java
@@ -19,6 +19,7 @@
 import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
 import static android.net.netlink.NetlinkConstants.hexify;
 import static android.net.netlink.NetlinkConstants.stringForNlMsgType;
+import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
 
 import android.net.MacAddress;
 import android.net.netlink.NetlinkErrorMessage;
@@ -31,7 +32,6 @@
 import android.os.Handler;
 import android.os.SystemClock;
 import android.system.ErrnoException;
-import android.system.NetlinkSocketAddress;
 import android.system.Os;
 import android.system.OsConstants;
 import android.util.Log;
@@ -148,15 +148,12 @@
 
         try {
             fd = NetlinkSocket.forProto(OsConstants.NETLINK_ROUTE);
-            Os.bind(fd, (SocketAddress)(new NetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH)));
-            Os.connect(fd, (SocketAddress)(new NetlinkSocketAddress(0, 0)));
+            Os.bind(fd, makeNetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH));
+            NetlinkSocket.connectToKernel(fd);
 
             if (VDBG) {
-                final NetlinkSocketAddress nlAddr = (NetlinkSocketAddress) Os.getsockname(fd);
-                Log.d(TAG, "bound to sockaddr_nl{"
-                        + BitUtils.uint32(nlAddr.getPortId()) + ", "
-                        + nlAddr.getGroupsMask()
-                        + "}");
+                final SocketAddress nlAddr = Os.getsockname(fd);
+                Log.d(TAG, "bound to sockaddr_nl{" + nlAddr.toString() + "}");
             }
         } catch (ErrnoException|SocketException e) {
             logError("Failed to create rtnetlink socket", e);
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index 8b02156..29e2f0c 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -23,7 +23,6 @@
 
 import android.content.Context;
 import android.net.LinkProperties;
-import android.net.LinkProperties.ProvisioningChange;
 import android.net.RouteInfo;
 import android.net.ip.IpNeighborMonitor.NeighborEvent;
 import android.net.metrics.IpConnectivityLog;
@@ -308,10 +307,11 @@
             }
         }
 
-        final ProvisioningChange delta = LinkProperties.compareProvisioning(
-                mLinkProperties, whatIfLp);
+        final boolean lostProvisioning =
+                (mLinkProperties.isIPv4Provisioned() && !whatIfLp.isIPv4Provisioned())
+                || (mLinkProperties.isIPv6Provisioned() && !whatIfLp.isIPv6Provisioned());
 
-        if (delta == ProvisioningChange.LOST_PROVISIONING) {
+        if (lostProvisioning) {
             final String logMsg = "FAILURE: LOST_PROVISIONING, " + event;
             Log.w(TAG, logMsg);
             if (mCallback != null) {
@@ -320,7 +320,7 @@
                 mCallback.notifyLost(ip, logMsg);
             }
         }
-        logNudFailed(delta);
+        logNudFailed(lostProvisioning);
     }
 
     private boolean avoidingBadLinks() {
@@ -370,11 +370,10 @@
         mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
     }
 
-    private void logNudFailed(ProvisioningChange delta) {
+    private void logNudFailed(boolean lostProvisioning) {
         long duration = SystemClock.elapsedRealtime() - mLastProbeTimeMs;
         boolean isFromProbe = (duration < getProbeWakeLockDuration());
-        boolean isProvisioningLost = (delta == ProvisioningChange.LOST_PROVISIONING);
-        int eventType = nudFailureEventType(isFromProbe, isProvisioningLost);
+        int eventType = nudFailureEventType(isFromProbe, lostProvisioning);
         mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
     }
 
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 8b22f68..7910c9a 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -226,7 +226,7 @@
         mNetd = deps.getNetdService();
         mStatsService = statsService;
         mCallback = callback;
-        mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog);
+        mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog);
         mIfaceName = ifaceName;
         mInterfaceType = interfaceType;
         mLinkProperties = new LinkProperties();
diff --git a/services/net/java/android/net/netlink/NetlinkSocket.java b/services/net/java/android/net/netlink/NetlinkSocket.java
index 40098c1..2a98d90 100644
--- a/services/net/java/android/net/netlink/NetlinkSocket.java
+++ b/services/net/java/android/net/netlink/NetlinkSocket.java
@@ -16,26 +16,26 @@
 
 package android.net.netlink;
 
+import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
 import static android.system.OsConstants.AF_NETLINK;
 import static android.system.OsConstants.EIO;
 import static android.system.OsConstants.EPROTO;
 import static android.system.OsConstants.ETIMEDOUT;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOL_SOCKET;
 import static android.system.OsConstants.SO_RCVBUF;
 import static android.system.OsConstants.SO_RCVTIMEO;
 import static android.system.OsConstants.SO_SNDTIMEO;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOL_SOCKET;
 
 import android.system.ErrnoException;
-import android.system.NetlinkSocketAddress;
 import android.system.Os;
 import android.system.StructTimeval;
 import android.util.Log;
+
 import libcore.io.IoUtils;
 
 import java.io.FileDescriptor;
 import java.io.InterruptedIOException;
-import java.net.SocketAddress;
 import java.net.SocketException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -106,7 +106,7 @@
     }
 
     public static void connectToKernel(FileDescriptor fd) throws ErrnoException, SocketException {
-        Os.connect(fd, (SocketAddress) (new NetlinkSocketAddress(0, 0)));
+        Os.connect(fd, makeNetlinkSocketAddress(0, 0));
     }
 
     private static void checkTimeout(long timeoutMs) {
diff --git a/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java
index d5213df..51d955d 100644
--- a/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java
+++ b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java
@@ -182,9 +182,6 @@
         parcel.mtu = lp.getMtu();
         parcel.tcpBufferSizes = lp.getTcpBufferSizes();
         parcel.nat64Prefix = toStableParcelable(lp.getNat64Prefix());
-        parcel.stackedLinks = toParcelableArray(
-                lp.getStackedLinks(), LinkPropertiesParcelableUtil::toStableParcelable,
-                LinkPropertiesParcelable.class);
         return parcel;
     }
 
@@ -216,9 +213,6 @@
         lp.setMtu(parcel.mtu);
         lp.setTcpBufferSizes(parcel.tcpBufferSizes);
         lp.setNat64Prefix(fromStableParcelable(parcel.nat64Prefix));
-        for (LinkPropertiesParcelable stackedLink : parcel.stackedLinks) {
-            lp.addStackedLink(fromStableParcelable(stackedLink));
-        }
         return lp;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index 6a9a121..ac4a5fe 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -25,7 +25,6 @@
 
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
@@ -48,6 +47,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
 import android.test.mock.MockContentResolver;
+import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -90,17 +90,30 @@
     };
     private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1;
 
-    @UserIdInt private int mUserId;
-    @Mock private BackupManagerService mBackupManagerServiceMock;
-    @Mock private Context mContextMock;
-    @Mock private File mSuppressFileMock;
-    @Mock private File mSuppressFileParentMock;
-    @Mock private IBinder mAgentMock;
-    @Mock private ParcelFileDescriptor mParcelFileDescriptorMock;
-    @Mock private IFullBackupRestoreObserver mFullBackupRestoreObserverMock;
-    @Mock private IBackupObserver mBackupObserverMock;
-    @Mock private IBackupManagerMonitor mBackupManagerMonitorMock;
-    @Mock private PrintWriter mPrintWriterMock;
+    @UserIdInt
+    private int mUserId;
+    @Mock
+    private BackupManagerService mBackupManagerServiceMock;
+    @Mock
+    private UserBackupManagerService mUserBackupManagerService;
+    @Mock
+    private Context mContextMock;
+    @Mock
+    private File mSuppressFileMock;
+    @Mock
+    private File mSuppressFileParentMock;
+    @Mock
+    private IBinder mAgentMock;
+    @Mock
+    private ParcelFileDescriptor mParcelFileDescriptorMock;
+    @Mock
+    private IFullBackupRestoreObserver mFullBackupRestoreObserverMock;
+    @Mock
+    private IBackupObserver mBackupObserverMock;
+    @Mock
+    private IBackupManagerMonitor mBackupManagerMonitorMock;
+    @Mock
+    private PrintWriter mPrintWriterMock;
 
     private FileDescriptor mFileDescriptorStub = new FileDescriptor();
 
@@ -110,16 +123,20 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mUserId = NON_USER_SYSTEM;
+
+        SparseArray<UserBackupManagerService> serviceUsers = new SparseArray<>();
+        serviceUsers.append(UserHandle.SYSTEM.getIdentifier(), mUserBackupManagerService);
+        serviceUsers.append(NON_USER_SYSTEM, mUserBackupManagerService);
+        when(mBackupManagerServiceMock.getServiceUsers()).thenReturn(serviceUsers);
 
         TrampolineTestable.sBackupManagerServiceMock = mBackupManagerServiceMock;
-        TrampolineTestable.sSuppressFile = mSuppressFileMock;
         TrampolineTestable.sCallingUserId = UserHandle.USER_SYSTEM;
         TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
         TrampolineTestable.sBackupDisabled = false;
 
         when(mSuppressFileMock.getParentFile()).thenReturn(mSuppressFileParentMock);
 
-        mUserId = NON_USER_SYSTEM;
         mTrampoline = new TrampolineTestable(mContextMock);
 
         mContentResolver = new MockContentResolver();
@@ -128,11 +145,6 @@
     }
 
     @Test
-    public void constructor_createsSuppressFileDirectory() {
-        verify(mSuppressFileParentMock).mkdirs();
-    }
-
-    @Test
     public void unlockUser_whenMultiUserSettingDisabled_callsBackupManagerServiceForSystemUser() {
         Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
         mTrampoline.initializeService();
@@ -147,9 +159,9 @@
         Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
         mTrampoline.initializeService();
 
-        mTrampoline.unlockUser(10);
+        mTrampoline.unlockUser(NON_USER_SYSTEM);
 
-        verify(mBackupManagerServiceMock, never()).startServiceForUser(10);
+        verify(mBackupManagerServiceMock, never()).startServiceForUser(NON_USER_SYSTEM);
     }
 
     @Test
@@ -157,9 +169,9 @@
         Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
         mTrampoline.initializeService();
 
-        mTrampoline.unlockUser(10);
+        mTrampoline.unlockUser(NON_USER_SYSTEM);
 
-        verify(mBackupManagerServiceMock).startServiceForUser(10);
+        verify(mBackupManagerServiceMock).startServiceForUser(NON_USER_SYSTEM);
     }
 
     @Test
@@ -177,19 +189,20 @@
         Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
         mTrampoline.initializeService();
 
-        mTrampoline.stopUser(10);
+        mTrampoline.stopUser(NON_USER_SYSTEM);
 
-        verify(mBackupManagerServiceMock, never()).stopServiceForUser(10);
+        verify(mBackupManagerServiceMock, never()).stopServiceForUser(NON_USER_SYSTEM);
     }
 
     @Test
     public void stopUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
         Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
+
         mTrampoline.initializeService();
 
-        mTrampoline.stopUser(10);
+        mTrampoline.stopUser(NON_USER_SYSTEM);
 
-        verify(mBackupManagerServiceMock).stopServiceForUser(10);
+        verify(mBackupManagerServiceMock).stopServiceForUser(NON_USER_SYSTEM);
     }
 
     @Test
@@ -211,9 +224,10 @@
 
     // Verify that BackupManagerService is not initialized if suppress file exists.
     @Test
-    public void initializeService_suppressFileExists_nonInitialized() {
-        when(mSuppressFileMock.exists()).thenReturn(true);
+    public void initializeService_suppressFileExists_nonInitialized() throws Exception {
         TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
+        trampoline.createBackupSuppressFileForUser(UserHandle.USER_SYSTEM);
+
 
         trampoline.initializeService();
 
@@ -233,6 +247,14 @@
     }
 
     @Test
+    public void isBackupServiceActive_forNonSysUser_whenSysUserIsDeactivated_returnsFalse() {
+        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
+        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+
+        assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
+    }
+
+    @Test
     public void setBackupServiceActive_callerSystemUid_serviceCreated() {
         TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
 
@@ -292,11 +314,18 @@
     }
 
     @Test
+    public void setBackupServiceActive_makeNonActive_alreadyNonActive_ignored() {
+        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+
+        assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
     public void setBackupServiceActive_makeActive_serviceCreatedAndSuppressFileDeleted() {
         mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
 
         assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-        verify(mSuppressFileMock).delete();
     }
 
     @Test
@@ -308,40 +337,25 @@
         mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
 
         assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-        verify(mSuppressFileMock).createNewFile();
     }
 
     @Test
-    public void
-    setBackupServiceActive_makeNonActive_serviceDeletedAndSuppressFileCreated_ioExceptionHandled()
-            throws IOException {
-        when(mSuppressFileMock.createNewFile()).thenThrow(new IOException());
+    public void setBackupActive_nonSystemUser_disabledForSystemUser_ignored() {
         mTrampoline.initializeService();
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-
         mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
 
-        assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-        verify(mSuppressFileMock).createNewFile();
+        assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
     }
 
     @Test
-    public void setBackupServiceActive_makeNonActive_alreadyNonActive_ignored() throws IOException {
-        reset(mSuppressFileMock);
-
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
-
-        verifyNoMoreInteractions(mSuppressFileMock);
-    }
-
-    @Test
-    public void dataChanged_calledBeforeInitialize_ignored() throws RemoteException {
+    public void dataChanged_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.dataChanged(PACKAGE_NAME);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void dataChangedForUser_forwarded() throws RemoteException {
+    public void dataChangedForUser_forwarded() throws Exception {
         mTrampoline.initializeService();
 
         mTrampoline.dataChangedForUser(mUserId, PACKAGE_NAME);
@@ -350,7 +364,7 @@
     }
 
     @Test
-    public void dataChanged_forwarded() throws RemoteException {
+    public void dataChanged_forwarded() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         mTrampoline.initializeService();
 
@@ -360,13 +374,13 @@
     }
 
     @Test
-    public void clearBackupData_calledBeforeInitialize_ignored() throws RemoteException {
+    public void clearBackupData_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.clearBackupData(TRANSPORT_NAME, PACKAGE_NAME);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void clearBackupDataForUser_forwarded() throws RemoteException {
+    public void clearBackupDataForUser_forwarded() throws Exception {
         mTrampoline.initializeService();
 
         mTrampoline.clearBackupDataForUser(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
@@ -375,7 +389,7 @@
     }
 
     @Test
-    public void clearBackupData_forwarded() throws RemoteException {
+    public void clearBackupData_forwarded() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         mTrampoline.initializeService();
 
@@ -385,13 +399,13 @@
     }
 
     @Test
-    public void agentConnected_calledBeforeInitialize_ignored() throws RemoteException {
+    public void agentConnected_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.agentConnected(PACKAGE_NAME, mAgentMock);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void agentConnectedForUser_forwarded() throws RemoteException {
+    public void agentConnectedForUser_forwarded() throws Exception {
         mTrampoline.initializeService();
 
         mTrampoline.agentConnectedForUser(mUserId, PACKAGE_NAME, mAgentMock);
@@ -400,7 +414,7 @@
     }
 
     @Test
-    public void agentConnected_forwarded() throws RemoteException {
+    public void agentConnected_forwarded() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         mTrampoline.initializeService();
 
@@ -410,13 +424,13 @@
     }
 
     @Test
-    public void agentDisconnected_calledBeforeInitialize_ignored() throws RemoteException {
+    public void agentDisconnected_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.agentDisconnected(PACKAGE_NAME);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void agentDisconnectedForUser_forwarded() throws RemoteException {
+    public void agentDisconnectedForUser_forwarded() throws Exception {
         mTrampoline.initializeService();
 
         mTrampoline.agentDisconnectedForUser(mUserId, PACKAGE_NAME);
@@ -425,7 +439,7 @@
     }
 
     @Test
-    public void agentDisconnected_forwarded() throws RemoteException {
+    public void agentDisconnected_forwarded() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         mTrampoline.initializeService();
 
@@ -435,13 +449,13 @@
     }
 
     @Test
-    public void restoreAtInstall_calledBeforeInitialize_ignored() throws RemoteException {
+    public void restoreAtInstall_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.restoreAtInstall(PACKAGE_NAME, 123);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void restoreAtInstallForUser_forwarded() throws RemoteException {
+    public void restoreAtInstallForUser_forwarded() throws Exception {
         mTrampoline.initializeService();
 
         mTrampoline.restoreAtInstallForUser(mUserId, PACKAGE_NAME, 123);
@@ -450,7 +464,7 @@
     }
 
     @Test
-    public void restoreAtInstall_forwarded() throws RemoteException {
+    public void restoreAtInstall_forwarded() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         mTrampoline.initializeService();
 
@@ -460,13 +474,13 @@
     }
 
     @Test
-    public void setBackupEnabled_calledBeforeInitialize_ignored() throws RemoteException {
+    public void setBackupEnabled_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.setBackupEnabled(true);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void setBackupEnabledForUser_forwarded() throws RemoteException {
+    public void setBackupEnabledForUser_forwarded() throws Exception {
         mTrampoline.initializeService();
 
         mTrampoline.setBackupEnabledForUser(mUserId, true);
@@ -475,7 +489,7 @@
     }
 
     @Test
-    public void setBackupEnabled_forwardedToCallingUserId() throws RemoteException {
+    public void setBackupEnabled_forwardedToCallingUserId() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         mTrampoline.initializeService();
 
@@ -485,13 +499,13 @@
     }
 
     @Test
-    public void setAutoRestore_calledBeforeInitialize_ignored() throws RemoteException {
+    public void setAutoRestore_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.setAutoRestore(true);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void setAutoRestoreForUser_forwarded() throws RemoteException {
+    public void setAutoRestoreForUser_forwarded() throws Exception {
         mTrampoline.initializeService();
 
         mTrampoline.setAutoRestoreForUser(mUserId, true);
@@ -500,7 +514,7 @@
     }
 
     @Test
-    public void setAutoRestore_forwarded() throws RemoteException {
+    public void setAutoRestore_forwarded() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         mTrampoline.initializeService();
 
@@ -510,13 +524,13 @@
     }
 
     @Test
-    public void isBackupEnabled_calledBeforeInitialize_ignored() throws RemoteException {
+    public void isBackupEnabled_calledBeforeInitialize_ignored() throws Exception {
         assertFalse(mTrampoline.isBackupEnabled());
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void isBackupEnabledForUser_forwarded() throws RemoteException {
+    public void isBackupEnabledForUser_forwarded() throws Exception {
         mTrampoline.initializeService();
 
         mTrampoline.isBackupEnabledForUser(mUserId);
@@ -525,7 +539,7 @@
     }
 
     @Test
-    public void isBackupEnabled_forwardedToCallingUserId() throws RemoteException {
+    public void isBackupEnabled_forwardedToCallingUserId() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         mTrampoline.initializeService();
 
@@ -535,39 +549,39 @@
     }
 
     @Test
-    public void setBackupPassword_calledBeforeInitialize_ignored() throws RemoteException {
+    public void setBackupPassword_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.setBackupPassword(CURRENT_PASSWORD, NEW_PASSWORD);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void setBackupPassword_forwarded() throws RemoteException {
+    public void setBackupPassword_forwarded() throws Exception {
         mTrampoline.initializeService();
         mTrampoline.setBackupPassword(CURRENT_PASSWORD, NEW_PASSWORD);
         verify(mBackupManagerServiceMock).setBackupPassword(CURRENT_PASSWORD, NEW_PASSWORD);
     }
 
     @Test
-    public void hasBackupPassword_calledBeforeInitialize_ignored() throws RemoteException {
+    public void hasBackupPassword_calledBeforeInitialize_ignored() throws Exception {
         assertFalse(mTrampoline.hasBackupPassword());
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void hasBackupPassword_forwarded() throws RemoteException {
+    public void hasBackupPassword_forwarded() throws Exception {
         mTrampoline.initializeService();
         mTrampoline.hasBackupPassword();
         verify(mBackupManagerServiceMock).hasBackupPassword();
     }
 
     @Test
-    public void backupNow_calledBeforeInitialize_ignored() throws RemoteException {
+    public void backupNow_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.backupNow();
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void backupNowForUser_forwarded() throws RemoteException {
+    public void backupNowForUser_forwarded() throws Exception {
         mTrampoline.initializeService();
 
         mTrampoline.backupNowForUser(mUserId);
@@ -576,7 +590,7 @@
     }
 
     @Test
-    public void backupNow_forwardedToCallingUserId() throws RemoteException {
+    public void backupNow_forwardedToCallingUserId() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         mTrampoline.initializeService();
 
@@ -586,7 +600,7 @@
     }
 
     @Test
-    public void adbBackup_calledBeforeInitialize_ignored() throws RemoteException {
+    public void adbBackup_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.adbBackup(mUserId, mParcelFileDescriptorMock, true, true,
                 true, true, true, true, true, true,
                 PACKAGE_NAMES);
@@ -594,7 +608,7 @@
     }
 
     @Test
-    public void adbBackup_forwarded() throws RemoteException {
+    public void adbBackup_forwarded() throws Exception {
         mTrampoline.initializeService();
         mTrampoline.adbBackup(mUserId, mParcelFileDescriptorMock, true, true,
                 true, true, true, true, true, true,
@@ -604,13 +618,13 @@
     }
 
     @Test
-    public void fullTransportBackup_calledBeforeInitialize_ignored() throws RemoteException {
+    public void fullTransportBackup_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.fullTransportBackupForUser(mUserId, PACKAGE_NAMES);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void fullTransportBackupForUser_forwarded() throws RemoteException {
+    public void fullTransportBackupForUser_forwarded() throws Exception {
         mTrampoline.initializeService();
 
         mTrampoline.fullTransportBackupForUser(mUserId, PACKAGE_NAMES);
@@ -619,13 +633,13 @@
     }
 
     @Test
-    public void adbRestore_calledBeforeInitialize_ignored() throws RemoteException {
+    public void adbRestore_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.adbRestore(mUserId, mParcelFileDescriptorMock);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void adbRestore_forwarded() throws RemoteException {
+    public void adbRestore_forwarded() throws Exception {
         mTrampoline.initializeService();
         mTrampoline.adbRestore(mUserId, mParcelFileDescriptorMock);
         verify(mBackupManagerServiceMock).adbRestore(mUserId, mParcelFileDescriptorMock);
@@ -633,14 +647,14 @@
 
     @Test
     public void acknowledgeFullBackupOrRestore_calledBeforeInitialize_ignored()
-            throws RemoteException {
+            throws Exception {
         mTrampoline.acknowledgeFullBackupOrRestore(123, true, CURRENT_PASSWORD, ENCRYPTION_PASSWORD,
                 mFullBackupRestoreObserverMock);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void acknowledgeFullBackupOrRestoreForUser_forwarded() throws RemoteException {
+    public void acknowledgeFullBackupOrRestoreForUser_forwarded() throws Exception {
         mTrampoline.initializeService();
 
         mTrampoline.acknowledgeFullBackupOrRestoreForUser(
@@ -662,7 +676,7 @@
     }
 
     @Test
-    public void acknowledgeFullBackupOrRestore_forwarded() throws RemoteException {
+    public void acknowledgeFullBackupOrRestore_forwarded() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         mTrampoline.initializeService();
 
@@ -680,13 +694,13 @@
     }
 
     @Test
-    public void getCurrentTransport_calledBeforeInitialize_ignored() throws RemoteException {
+    public void getCurrentTransport_calledBeforeInitialize_ignored() throws Exception {
         assertNull(mTrampoline.getCurrentTransport());
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void getCurrentTransportForUser_forwarded() throws RemoteException {
+    public void getCurrentTransportForUser_forwarded() throws Exception {
         when(mBackupManagerServiceMock.getCurrentTransport(mUserId)).thenReturn(TRANSPORT_NAME);
         mTrampoline.initializeService();
 
@@ -695,7 +709,7 @@
     }
 
     @Test
-    public void getCurrentTransport_forwarded() throws RemoteException {
+    public void getCurrentTransport_forwarded() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         when(mBackupManagerServiceMock.getCurrentTransport(mUserId)).thenReturn(TRANSPORT_NAME);
         mTrampoline.initializeService();
@@ -705,13 +719,13 @@
     }
 
     @Test
-    public void listAllTransports_calledBeforeInitialize_ignored() throws RemoteException {
+    public void listAllTransports_calledBeforeInitialize_ignored() throws Exception {
         assertNull(mTrampoline.listAllTransports());
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void listAllTransportsForUser_forwarded() throws RemoteException {
+    public void listAllTransportsForUser_forwarded() throws Exception {
         when(mBackupManagerServiceMock.listAllTransports(mUserId)).thenReturn(TRANSPORTS);
         mTrampoline.initializeService();
 
@@ -721,7 +735,7 @@
 
 
     @Test
-    public void listAllTransports_forwarded() throws RemoteException {
+    public void listAllTransports_forwarded() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         when(mBackupManagerServiceMock.listAllTransports(mUserId)).thenReturn(TRANSPORTS);
         mTrampoline.initializeService();
@@ -732,13 +746,13 @@
 
     @Test
     public void listAllTransportComponentsForUser_calledBeforeInitialize_ignored()
-            throws RemoteException {
+            throws Exception {
         assertNull(mTrampoline.listAllTransportComponentsForUser(mUserId));
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void listAllTransportComponentsForUser_forwarded() throws RemoteException {
+    public void listAllTransportComponentsForUser_forwarded() throws Exception {
         when(mBackupManagerServiceMock.listAllTransportComponents(mUserId)).thenReturn(
                 TRANSPORT_COMPONENTS);
         mTrampoline.initializeService();
@@ -748,13 +762,13 @@
     }
 
     @Test
-    public void getTransportWhitelist_calledBeforeInitialize_ignored() throws RemoteException {
+    public void getTransportWhitelist_calledBeforeInitialize_ignored() throws Exception {
         assertNull(mTrampoline.getTransportWhitelist());
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void getTransportWhitelist_forwarded() throws RemoteException {
+    public void getTransportWhitelist_forwarded() {
         when(mBackupManagerServiceMock.getTransportWhitelist()).thenReturn(TRANSPORTS);
         mTrampoline.initializeService();
 
@@ -763,8 +777,7 @@
     }
 
     @Test
-    public void updateTransportAttributesForUser_calledBeforeInitialize_ignored()
-            throws RemoteException {
+    public void updateTransportAttributesForUser_calledBeforeInitialize_ignored() {
         mTrampoline.updateTransportAttributesForUser(
                 mUserId,
                 TRANSPORT_COMPONENT_NAME,
@@ -778,7 +791,7 @@
     }
 
     @Test
-    public void updateTransportAttributesForUser_forwarded() throws RemoteException {
+    public void updateTransportAttributesForUser_forwarded() {
         when(mBackupManagerServiceMock.getTransportWhitelist()).thenReturn(TRANSPORTS);
         mTrampoline.initializeService();
 
@@ -809,7 +822,7 @@
     }
 
     @Test
-    public void selectBackupTransportForUser_forwarded() throws RemoteException {
+    public void selectBackupTransportForUser_forwarded() throws Exception {
         mTrampoline.initializeService();
 
         mTrampoline.selectBackupTransportForUser(mUserId, TRANSPORT_NAME);
@@ -818,7 +831,7 @@
     }
 
     @Test
-    public void selectBackupTransport_forwarded() throws RemoteException {
+    public void selectBackupTransport_forwarded() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         mTrampoline.initializeService();
 
@@ -895,7 +908,7 @@
     }
 
     @Test
-    public void selectBackupTransportAsyncForUser_forwarded() throws RemoteException {
+    public void selectBackupTransportAsyncForUser_forwarded() throws Exception {
         mTrampoline.initializeService();
 
         mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
@@ -905,13 +918,13 @@
     }
 
     @Test
-    public void getConfigurationIntent_calledBeforeInitialize_ignored() throws RemoteException {
+    public void getConfigurationIntent_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.getConfigurationIntent(TRANSPORT_NAME);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void getConfigurationIntentForUser_forwarded() throws RemoteException {
+    public void getConfigurationIntentForUser_forwarded() throws Exception {
         Intent configurationIntentStub = new Intent();
         when(mBackupManagerServiceMock.getConfigurationIntent(mUserId, TRANSPORT_NAME)).thenReturn(
                 configurationIntentStub);
@@ -924,7 +937,7 @@
     }
 
     @Test
-    public void getConfigurationIntent_forwarded() throws RemoteException {
+    public void getConfigurationIntent_forwarded() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         Intent configurationIntentStub = new Intent();
         when(mBackupManagerServiceMock.getConfigurationIntent(mUserId, TRANSPORT_NAME)).thenReturn(
@@ -936,13 +949,13 @@
     }
 
     @Test
-    public void getDestinationString_calledBeforeInitialize_ignored() throws RemoteException {
+    public void getDestinationString_calledBeforeInitialize_ignored() throws Exception {
         assertNull(mTrampoline.getDestinationString(TRANSPORT_NAME));
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void getDestinationStringForUser_forwarded() throws RemoteException {
+    public void getDestinationStringForUser_forwarded() throws Exception {
         when(mBackupManagerServiceMock.getDestinationString(mUserId, TRANSPORT_NAME)).thenReturn(
                 DESTINATION_STRING);
         mTrampoline.initializeService();
@@ -954,7 +967,7 @@
     }
 
     @Test
-    public void getDestinationString_forwarded() throws RemoteException {
+    public void getDestinationString_forwarded() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         when(mBackupManagerServiceMock.getDestinationString(mUserId, TRANSPORT_NAME)).thenReturn(
                 DESTINATION_STRING);
@@ -965,13 +978,13 @@
     }
 
     @Test
-    public void getDataManagementIntent_calledBeforeInitialize_ignored() throws RemoteException {
+    public void getDataManagementIntent_calledBeforeInitialize_ignored() throws Exception {
         assertNull(mTrampoline.getDataManagementIntent(TRANSPORT_NAME));
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void getDataManagementIntentForUser_forwarded() throws RemoteException {
+    public void getDataManagementIntentForUser_forwarded() throws Exception {
         Intent dataManagementIntent = new Intent();
         when(mBackupManagerServiceMock.getDataManagementIntent(mUserId, TRANSPORT_NAME)).thenReturn(
                 dataManagementIntent);
@@ -984,7 +997,7 @@
     }
 
     @Test
-    public void getDataManagementIntent_forwarded() throws RemoteException {
+    public void getDataManagementIntent_forwarded() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         Intent dataManagementIntent = new Intent();
         when(mBackupManagerServiceMock.getDataManagementIntent(mUserId, TRANSPORT_NAME)).thenReturn(
@@ -996,13 +1009,13 @@
     }
 
     @Test
-    public void getDataManagementLabel_calledBeforeInitialize_ignored() throws RemoteException {
+    public void getDataManagementLabel_calledBeforeInitialize_ignored() throws Exception {
         assertNull(mTrampoline.getDataManagementLabel(TRANSPORT_NAME));
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void getDataManagementLabelForUser_forwarded() throws RemoteException {
+    public void getDataManagementLabelForUser_forwarded() throws Exception {
         when(mBackupManagerServiceMock.getDataManagementLabel(mUserId, TRANSPORT_NAME)).thenReturn(
                 DATA_MANAGEMENT_LABEL);
         mTrampoline.initializeService();
@@ -1014,7 +1027,7 @@
     }
 
     @Test
-    public void getDataManagementLabel_forwarded() throws RemoteException {
+    public void getDataManagementLabel_forwarded() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         when(mBackupManagerServiceMock.getDataManagementLabel(mUserId, TRANSPORT_NAME)).thenReturn(
                 DATA_MANAGEMENT_LABEL);
@@ -1025,13 +1038,13 @@
     }
 
     @Test
-    public void beginRestoreSession_calledBeforeInitialize_ignored() throws RemoteException {
+    public void beginRestoreSession_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.beginRestoreSessionForUser(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void beginRestoreSessionForUser_forwarded() throws RemoteException {
+    public void beginRestoreSessionForUser_forwarded() throws Exception {
         mTrampoline.initializeService();
 
         mTrampoline.beginRestoreSessionForUser(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
@@ -1041,13 +1054,13 @@
     }
 
     @Test
-    public void opComplete_calledBeforeInitialize_ignored() throws RemoteException {
+    public void opComplete_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.opComplete(1, 2);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void opComplete_forwarded() throws RemoteException {
+    public void opComplete_forwarded() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         mTrampoline.initializeService();
 
@@ -1057,14 +1070,13 @@
     }
 
     @Test
-    public void getAvailableRestoreTokenForUser_calledBeforeInitialize_ignored()
-            throws RemoteException {
+    public void getAvailableRestoreTokenForUser_calledBeforeInitialize_ignored() {
         assertEquals(0, mTrampoline.getAvailableRestoreTokenForUser(mUserId, PACKAGE_NAME));
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void getAvailableRestoreTokenForUser_forwarded() throws RemoteException {
+    public void getAvailableRestoreTokenForUser_forwarded() {
         when(mBackupManagerServiceMock.getAvailableRestoreToken(mUserId, PACKAGE_NAME))
                 .thenReturn(123L);
         mTrampoline.initializeService();
@@ -1074,14 +1086,13 @@
     }
 
     @Test
-    public void isAppEligibleForBackupForUser_calledBeforeInitialize_ignored()
-            throws RemoteException {
+    public void isAppEligibleForBackupForUser_calledBeforeInitialize_ignored() {
         assertFalse(mTrampoline.isAppEligibleForBackupForUser(mUserId, PACKAGE_NAME));
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void isAppEligibleForBackupForUser_forwarded() throws RemoteException {
+    public void isAppEligibleForBackupForUser_forwarded() {
         when(mBackupManagerServiceMock.isAppEligibleForBackup(mUserId, PACKAGE_NAME))
                 .thenReturn(true);
         mTrampoline.initializeService();
@@ -1098,7 +1109,7 @@
     }
 
     @Test
-    public void requestBackupForUser_forwarded() throws RemoteException {
+    public void requestBackupForUser_forwarded() throws Exception {
         when(mBackupManagerServiceMock.requestBackup(mUserId, PACKAGE_NAMES,
                 mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
         mTrampoline.initializeService();
@@ -1110,7 +1121,7 @@
     }
 
     @Test
-    public void requestBackup_forwardedToCallingUserId() throws RemoteException {
+    public void requestBackup_forwardedToCallingUserId() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         when(mBackupManagerServiceMock.requestBackup(NON_USER_SYSTEM, PACKAGE_NAMES,
                 mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
@@ -1123,13 +1134,13 @@
     }
 
     @Test
-    public void cancelBackups_calledBeforeInitialize_ignored() throws RemoteException {
+    public void cancelBackups_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.cancelBackups();
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void cancelBackupsForUser_forwarded() throws RemoteException {
+    public void cancelBackupsForUser_forwarded() throws Exception {
         mTrampoline.initializeService();
 
         mTrampoline.cancelBackupsForUser(mUserId);
@@ -1138,7 +1149,7 @@
     }
 
     @Test
-    public void cancelBackups_forwardedToCallingUserId() throws RemoteException {
+    public void cancelBackups_forwardedToCallingUserId() throws Exception {
         TrampolineTestable.sCallingUserId = mUserId;
         mTrampoline.initializeService();
 
@@ -1148,13 +1159,13 @@
     }
 
     @Test
-    public void beginFullBackup_calledBeforeInitialize_ignored() throws RemoteException {
+    public void beginFullBackup_calledBeforeInitialize_ignored() throws Exception {
         mTrampoline.beginFullBackup(mUserId, new FullBackupJob());
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void beginFullBackup_forwarded() throws RemoteException {
+    public void beginFullBackup_forwarded() throws Exception {
         FullBackupJob fullBackupJob = new FullBackupJob();
         when(mBackupManagerServiceMock.beginFullBackup(mUserId, fullBackupJob)).thenReturn(true);
 
@@ -1164,20 +1175,20 @@
     }
 
     @Test
-    public void endFullBackup_calledBeforeInitialize_ignored() throws RemoteException {
+    public void endFullBackup_calledBeforeInitialize_ignored() {
         mTrampoline.endFullBackup(mUserId);
         verifyNoMoreInteractions(mBackupManagerServiceMock);
     }
 
     @Test
-    public void endFullBackup_forwarded() throws RemoteException {
+    public void endFullBackup_forwarded() {
         mTrampoline.initializeService();
         mTrampoline.endFullBackup(mUserId);
         verify(mBackupManagerServiceMock).endFullBackup(mUserId);
     }
 
     @Test
-    public void dump_callerDoesNotHavePermission_ignored() throws RemoteException {
+    public void dump_callerDoesNotHavePermission_ignored() {
         when(mContextMock.checkCallingOrSelfPermission(
                 android.Manifest.permission.DUMP)).thenReturn(
                 PackageManager.PERMISSION_DENIED);
@@ -1189,7 +1200,7 @@
     }
 
     @Test
-    public void dump_calledBeforeInitialize_ignored() throws RemoteException {
+    public void dump_calledBeforeInitialize_ignored() {
         when(mContextMock.checkCallingOrSelfPermission(
                 android.Manifest.permission.DUMP)).thenReturn(
                 PackageManager.PERMISSION_GRANTED);
@@ -1200,7 +1211,7 @@
     }
 
     @Test
-    public void dump_callerHasPermission_forwarded() throws RemoteException {
+    public void dump_callerHasPermission_forwarded() {
         when(mContextMock.checkCallingOrSelfPermission(
                 android.Manifest.permission.DUMP)).thenReturn(
                 PackageManager.PERMISSION_GRANTED);
@@ -1213,11 +1224,36 @@
 
     private static class TrampolineTestable extends Trampoline {
         static boolean sBackupDisabled = false;
-        static File sSuppressFile = null;
         static int sCallingUserId = -1;
         static int sCallingUid = -1;
         static BackupManagerService sBackupManagerServiceMock = null;
         private int mCreateServiceCallsCount = 0;
+        private SparseArray<FakeFile> mSuppressFiles = new SparseArray<>();
+
+        private static class FakeFile extends File {
+            private boolean mExists;
+
+            FakeFile(String pathname) {
+                super(pathname);
+            }
+
+            @Override
+            public boolean exists() {
+                return mExists;
+            }
+
+            @Override
+            public boolean delete() {
+                mExists = false;
+                return true;
+            }
+
+            @Override
+            public boolean createNewFile() throws IOException {
+                mExists = true;
+                return true;
+            }
+        }
 
         TrampolineTestable(Context context) {
             super(context);
@@ -1229,8 +1265,12 @@
         }
 
         @Override
-        public File getSuppressFile() {
-            return sSuppressFile;
+        public File getSuppressFileForUser(int userId) {
+            if (mSuppressFiles.get(userId) == null) {
+                FakeFile file = new FakeFile(Integer.toString(userId));
+                mSuppressFiles.append(userId, file);
+            }
+            return mSuppressFiles.get(userId);
         }
 
         protected int binderGetCallingUserId() {
@@ -1249,6 +1289,11 @@
         }
 
         @Override
+        protected void createBackupSuppressFileForUser(int userId) throws IOException {
+            getSuppressFileForUser(userId).createNewFile();
+        }
+
+        @Override
         protected void postToHandler(Runnable runnable) {
             runnable.run();
         }
diff --git a/services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java
new file mode 100644
index 0000000..d7dc58d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.pm;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.pm.PackageParser;
+import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public class LauncherAppsServiceTest {
+
+    private static final Signature SIGNATURE_1 = new Signature(new byte[]{0x00, 0x01, 0x02, 0x03});
+    private static final Signature SIGNATURE_2 = new Signature(new byte[]{0x04, 0x05, 0x06, 0x07});
+    private static final Signature SIGNATURE_3 = new Signature(new byte[]{0x08, 0x09, 0x10, 0x11});
+
+    @Test
+    public void testComputePackageCertDigest() {
+        String digest = LauncherAppsService.LauncherAppsImpl.computePackageCertDigest(SIGNATURE_1);
+        assertEquals("A02A05B025B928C039CF1AE7E8EE04E7C190C0DB", digest);
+    }
+
+    @Test
+    public void testGetLatestSignaturesWithSingleCert() {
+        SigningInfo signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[]{SIGNATURE_1},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null));
+        Signature[] signatures = LauncherAppsService.LauncherAppsImpl.getLatestSignatures(
+                signingInfo);
+        assertEquals(1, signatures.length);
+        assertEquals(SIGNATURE_1, signatures[0]);
+    }
+
+    @Test
+    public void testGetLatestSignaturesWithMultiCert() {
+        SigningInfo signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[]{SIGNATURE_1, SIGNATURE_2},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null));
+        Signature[] signatures = LauncherAppsService.LauncherAppsImpl.getLatestSignatures(
+                signingInfo);
+        assertEquals(2, signatures.length);
+        assertEquals(SIGNATURE_1, signatures[0]);
+        assertEquals(SIGNATURE_2, signatures[1]);
+    }
+
+    @Test
+    public void testGetLatestSignaturesWithCertHistory() {
+        SigningInfo signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[]{SIGNATURE_1},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        new Signature[]{SIGNATURE_2, SIGNATURE_3}));
+        Signature[] signatures = LauncherAppsService.LauncherAppsImpl.getLatestSignatures(
+                signingInfo);
+        assertEquals(1, signatures.length);
+        assertEquals(SIGNATURE_2, signatures[0]);
+    }
+
+}
diff --git a/tests/net/java/android/net/ip/IpClientTest.java b/tests/net/java/android/net/ip/IpClientTest.java
index a2dcfef..7a83757 100644
--- a/tests/net/java/android/net/ip/IpClientTest.java
+++ b/tests/net/java/android/net/ip/IpClientTest.java
@@ -122,13 +122,14 @@
     private IpClient makeIpClient(String ifname) throws Exception {
         setTestInterfaceParams(ifname);
         final IpClient ipc = new IpClient(mContext, ifname, mCb, mDependecies);
-        verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).disableIpv6(ifname);
-        verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).clearInterfaceAddresses(ifname);
+        verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(ifname, false);
+        verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(ifname);
         ArgumentCaptor<BaseNetworkObserver> arg =
                 ArgumentCaptor.forClass(BaseNetworkObserver.class);
         verify(mNMService, times(1)).registerObserver(arg.capture());
         mObserver = arg.getValue();
         reset(mNMService);
+        reset(mNetd);
         // Verify IpClient doesn't call onLinkPropertiesChange() when it starts.
         verify(mCb, never()).onLinkPropertiesChange(any());
         reset(mCb);
@@ -200,8 +201,8 @@
         verify(mCb, never()).onProvisioningFailure(any());
 
         ipc.shutdown();
-        verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).disableIpv6(iface);
-        verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).clearInterfaceAddresses(iface);
+        verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(iface, false);
+        verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(iface);
         verify(mCb, timeout(TEST_TIMEOUT_MS).times(1))
                 .onLinkPropertiesChange(eq(makeEmptyLinkProperties(iface)));
     }
@@ -251,8 +252,8 @@
         verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).onProvisioningSuccess(eq(want));
 
         ipc.shutdown();
-        verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).disableIpv6(iface);
-        verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).clearInterfaceAddresses(iface);
+        verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(iface, false);
+        verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(iface);
         verify(mCb, timeout(TEST_TIMEOUT_MS).times(1))
                 .onLinkPropertiesChange(eq(makeEmptyLinkProperties(iface)));
     }
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java
index c3162af..80aac04 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -33,6 +33,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
@@ -47,6 +48,7 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.net.INetd;
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
 import android.net.IpPrefix;
@@ -92,6 +94,7 @@
     private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
 
     @Mock private INetworkManagementService mNMService;
+    @Mock private INetd mNetd;
     @Mock private INetworkStatsService mStatsService;
     @Mock private IpServer.Callback mCallback;
     @Mock private InterfaceConfiguration mInterfaceConfiguration;
@@ -112,16 +115,6 @@
     }
 
     private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception {
-        mIpServer = new IpServer(
-                IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
-                mNMService, mStatsService, mCallback, usingLegacyDhcp, mDependencies);
-        mIpServer.start();
-        // Starting the state machine always puts us in a consistent state and notifies
-        // the rest of the world that we've changed from an unknown to available state.
-        mLooper.dispatchAll();
-        reset(mNMService, mStatsService, mCallback);
-        when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
-
         doAnswer(inv -> {
             final IDhcpServerCallbacks cb = inv.getArgument(2);
             new Thread(() -> {
@@ -135,6 +128,17 @@
         }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any());
         when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
         when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
+        when(mDependencies.getNetdService()).thenReturn(mNetd);
+
+        mIpServer = new IpServer(
+                IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
+                mNMService, mStatsService, mCallback, usingLegacyDhcp, mDependencies);
+        mIpServer.start();
+        // Starting the state machine always puts us in a consistent state and notifies
+        // the rest of the world that we've changed from an unknown to available state.
+        mLooper.dispatchAll();
+        reset(mNMService, mStatsService, mCallback);
+        when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
 
         when(mRaDaemon.start()).thenReturn(true);
     }
@@ -223,9 +227,9 @@
         initTetheredStateMachine(TETHERING_BLUETOOTH, null);
 
         dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
-        InOrder inOrder = inOrder(mNMService, mStatsService, mCallback);
+        InOrder inOrder = inOrder(mNMService, mNetd, mStatsService, mCallback);
         inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
-        inOrder.verify(mNMService).setInterfaceConfig(eq(IFACE_NAME), any());
+        inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
         inOrder.verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
         inOrder.verify(mCallback).updateLinkProperties(
@@ -318,12 +322,12 @@
         initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
 
         dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
-        InOrder inOrder = inOrder(mNMService, mStatsService, mCallback);
+        InOrder inOrder = inOrder(mNMService, mNetd, mStatsService, mCallback);
         inOrder.verify(mStatsService).forceUpdate();
         inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
         inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
-        inOrder.verify(mNMService).setInterfaceConfig(eq(IFACE_NAME), any());
+        inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
         inOrder.verify(mCallback).updateInterfaceState(
                 mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
         inOrder.verify(mCallback).updateLinkProperties(
diff --git a/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java
index 6f711c0..b6d01db 100644
--- a/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java
+++ b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java
@@ -48,61 +48,52 @@
     private LinkProperties mLinkProperties;
 
     private static final String TEST_LINKPROPS_IFACE = "TEST_IFACE";
-    private static final String TEST_STACKED_LINK_1_IFACE = "TEST_STACKED_IFACE_1";
-    private static final String TEST_STACKED_LINK_2_IFACE = "TEST_STACKED_IFACE_2";
 
     @Before
     public void setUp() {
-        mLinkProperties = makeLinkProperties(TEST_LINKPROPS_IFACE);
-        mLinkProperties.addStackedLink(makeLinkProperties(TEST_STACKED_LINK_1_IFACE));
-        mLinkProperties.addStackedLink(makeLinkProperties(TEST_STACKED_LINK_2_IFACE));
-    }
-
-    private static LinkProperties makeLinkProperties(String iface) {
-        final LinkProperties lp = new LinkProperties();
-        lp.setInterfaceName(iface);
-        lp.setLinkAddresses(Arrays.asList(
+        mLinkProperties = new LinkProperties();
+        mLinkProperties.setInterfaceName(TEST_LINKPROPS_IFACE);
+        mLinkProperties.setLinkAddresses(Arrays.asList(
                 new LinkAddress(InetAddresses.parseNumericAddress("192.168.0.42"), 16),
                 new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::7"), 42)));
-        lp.setDnsServers(Arrays.asList(
+        mLinkProperties.setDnsServers(Arrays.asList(
                 InetAddresses.parseNumericAddress("2001:db8::42"),
                 InetAddresses.parseNumericAddress("192.168.1.1")
         ));
-        lp.setValidatedPrivateDnsServers(Arrays.asList(
+        mLinkProperties.setValidatedPrivateDnsServers(Arrays.asList(
                 InetAddresses.parseNumericAddress("2001:db8::43"),
                 InetAddresses.parseNumericAddress("192.168.42.43")
         ));
-        lp.setPcscfServers(Arrays.asList(
+        mLinkProperties.setPcscfServers(Arrays.asList(
                 InetAddresses.parseNumericAddress("2001:db8::47"),
                 InetAddresses.parseNumericAddress("192.168.42.47")
         ));
-        lp.setUsePrivateDns(true);
-        lp.setPrivateDnsServerName("test.example.com");
-        lp.setDomains("test1.example.com,test2.example.com");
-        lp.addRoute(new RouteInfo(
+        mLinkProperties.setUsePrivateDns(true);
+        mLinkProperties.setPrivateDnsServerName("test.example.com");
+        mLinkProperties.setDomains("test1.example.com,test2.example.com");
+        mLinkProperties.addRoute(new RouteInfo(
                 new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::44"), 45),
                 InetAddresses.parseNumericAddress("2001:db8::45"),
-                iface,
+                TEST_LINKPROPS_IFACE,
                 RouteInfo.RTN_UNICAST
         ));
-        lp.addRoute(new RouteInfo(
+        mLinkProperties.addRoute(new RouteInfo(
                 new IpPrefix(InetAddresses.parseNumericAddress("192.168.44.45"), 16),
                 InetAddresses.parseNumericAddress("192.168.45.1"),
-                iface,
+                TEST_LINKPROPS_IFACE,
                 RouteInfo.RTN_THROW
         ));
-        lp.setHttpProxy(new ProxyInfo("test3.example.com", 8000,
+        mLinkProperties.setHttpProxy(new ProxyInfo("test3.example.com", 8000,
                 "excl1.example.com,excl2.example.com"));
-        lp.setMtu(5000);
-        lp.setTcpBufferSizes("1,2,3,4,5,6");
-        lp.setNat64Prefix(new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::48"), 96));
+        mLinkProperties.setMtu(5000);
+        mLinkProperties.setTcpBufferSizes("1,2,3,4,5,6");
+        mLinkProperties.setNat64Prefix(
+                new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::48"), 96));
 
         // Verify that this test does not miss any new field added later.
         // If any added field is not included in LinkProperties#equals, assertLinkPropertiesEquals
         // must also be updated.
         assertFieldCountEquals(14, LinkProperties.class);
-
-        return lp;
     }
 
     @Test
@@ -186,7 +177,7 @@
     private static void assertLinkPropertiesEquals(LinkProperties expected, LinkProperties actual) {
         assertEquals(expected, actual);
 
-        // LinkProperties equals() does not include stacked links
-        assertEquals(expected.getStackedLinks(), actual.getStackedLinks());
+        // Equality on stacked links is not tested as they should not be passed to processes using
+        // LinkPropertiesParcelable.
     }
 }
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 1ea83c2..b635607 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -803,13 +803,14 @@
         sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
         mLooper.dispatchAll();
 
-        // We verify get/set called thrice here: once for setup and twice during
-        // teardown because all events happen over the course of the single
+        // We verify get/set called thrice here: twice for setup (on NMService) and once during
+        // teardown (on Netd) because all events happen over the course of the single
         // dispatchAll() above. Note that once the IpServer IPv4 address config
         // code is refactored the two calls during shutdown will revert to one.
         verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME);
-        verify(mNMService, times(3))
+        verify(mNMService, times(2))
                 .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
+        verify(mNetd, times(1)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName)));
         verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
         verify(mWifiManager).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index d1fe43e..75c3eba 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -297,6 +297,7 @@
 class V2LineParser(object):
     __slots__ = ["tokenized", "current", "len"]
 
+    FIELD_KINDS = ("field", "property", "enum_constant")
     MODIFIERS = set("public protected internal private abstract default static final transient volatile synchronized native operator sealed strictfp infix inline suspend vararg".split())
     JAVA_LANG_TYPES = set("AbstractMethodError AbstractStringBuilder Appendable ArithmeticException ArrayIndexOutOfBoundsException ArrayStoreException AssertionError AutoCloseable Boolean BootstrapMethodError Byte Character CharSequence Class ClassCastException ClassCircularityError ClassFormatError ClassLoader ClassNotFoundException Cloneable CloneNotSupportedException Comparable Compiler Deprecated Double Enum EnumConstantNotPresentException Error Exception ExceptionInInitializerError Float FunctionalInterface IllegalAccessError IllegalAccessException IllegalArgumentException IllegalMonitorStateException IllegalStateException IllegalThreadStateException IncompatibleClassChangeError IndexOutOfBoundsException InheritableThreadLocal InstantiationError InstantiationException Integer InternalError InterruptedException Iterable LinkageError Long Math NegativeArraySizeException NoClassDefFoundError NoSuchFieldError NoSuchFieldException NoSuchMethodError NoSuchMethodException NullPointerException Number NumberFormatException Object OutOfMemoryError Override Package package-info.java Process ProcessBuilder ProcessEnvironment ProcessImpl Readable ReflectiveOperationException Runnable Runtime RuntimeException RuntimePermission SafeVarargs SecurityException SecurityManager Short StackOverflowError StackTraceElement StrictMath String StringBuffer StringBuilder StringIndexOutOfBoundsException SuppressWarnings System Thread ThreadDeath ThreadGroup ThreadLocal Throwable TypeNotPresentException UNIXProcess UnknownError UnsatisfiedLinkError UnsupportedClassVersionError UnsupportedOperationException VerifyError VirtualMachineError Void".split())
 
@@ -355,7 +356,7 @@
         self.parse_eof()
 
     def parse_into_field(self, field):
-        kind = self.parse_one_of("field", "property")
+        kind = self.parse_one_of(*V2LineParser.FIELD_KINDS)
         field.split = [kind]
         annotations = self.parse_annotations()
         if "@Deprecated" in annotations:
@@ -591,6 +592,14 @@
     sig_format = 1
 
     re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$")
+
+    field_prefixes = map(lambda kind: "    %s" % (kind,), V2LineParser.FIELD_KINDS)
+    def startsWithFieldPrefix(raw):
+        for prefix in field_prefixes:
+            if raw.startswith(prefix):
+                return True
+        return False
+
     for raw in f:
         line += 1
         raw = raw.rstrip()
@@ -611,7 +620,7 @@
             clazz.ctors.append(Method(clazz, line, raw, blame, sig_format=sig_format))
         elif raw.startswith("    method"):
             clazz.methods.append(Method(clazz, line, raw, blame, sig_format=sig_format))
-        elif raw.startswith("    field") or raw.startswith("    property"):
+        elif startsWithFieldPrefix(raw):
             clazz.fields.append(Field(clazz, line, raw, blame, sig_format=sig_format))
         elif raw.startswith("  }") and clazz:
             yield clazz
diff --git a/tools/apilint/apilint_test.py b/tools/apilint/apilint_test.py
index fde61a9..9c261d5 100644
--- a/tools/apilint/apilint_test.py
+++ b/tools/apilint/apilint_test.py
@@ -143,6 +143,27 @@
                                       out_classes_with_base=classes_with_base)
         self.assertEquals(map(lambda x: x.fullname, classes_with_base), ["android.app.WallpaperColors"])
 
+class ParseV2Stream(unittest.TestCase):
+    def test_field_kinds(self):
+        api = apilint._parse_stream("""
+// Signature format: 2.0
+package android {
+  public enum SomeEnum {
+    enum_constant public static final android.SomeEnum ENUM_CONST;
+    field public static final int FIELD_CONST;
+    property public final int someProperty;
+    ctor public SomeEnum();
+    method public Object? getObject();
+  }
+}
+        """.strip().split('\n'))
+
+        self.assertEquals(api['android.SomeEnum'].fields[0].split[0], 'enum_constant')
+        self.assertEquals(api['android.SomeEnum'].fields[1].split[0], 'field')
+        self.assertEquals(api['android.SomeEnum'].fields[2].split[0], 'property')
+        self.assertEquals(api['android.SomeEnum'].ctors[0].split[0], 'ctor')
+        self.assertEquals(api['android.SomeEnum'].methods[0].split[0], 'method')
+
 class V2TokenizerTests(unittest.TestCase):
     def _test(self, raw, expected):
         self.assertEquals(apilint.V2Tokenizer(raw).tokenize(), expected)
@@ -211,6 +232,10 @@
         self.assertEquals('Interface', cls.implements)
         self.assertEquals('pkg.Some.Name', cls.fullname)
 
+    def test_enum(self):
+        cls = self._cls("public enum Some.Name {")
+        self._field("enum_constant public static final android.ValueType COLOR;")
+
     def test_interface(self):
         cls = self._cls("@Deprecated @IntRange(from=1, to=2) public interface Some.Name extends Interface<Class> {")
         self.assertTrue('deprecated' in cls.split)