Merge "Create a builder for NetworkCapabilities." into rvc-dev
diff --git a/Tethering/common/TetheringLib/api/module-lib-current.txt b/Tethering/common/TetheringLib/api/module-lib-current.txt
index e25d77d..e823e17 100644
--- a/Tethering/common/TetheringLib/api/module-lib-current.txt
+++ b/Tethering/common/TetheringLib/api/module-lib-current.txt
@@ -62,19 +62,20 @@
     field public static final int TETHERING_WIFI = 0; // 0x0
     field public static final int TETHERING_WIFI_P2P = 3; // 0x3
     field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
-    field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9
-    field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8
+    field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9
+    field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8
     field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
     field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
-    field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5
+    field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5
     field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
     field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
     field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
-    field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
+    field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb
     field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
     field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
     field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
     field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
+    field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10
     field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
     field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
     field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
@@ -86,29 +87,26 @@
     method public void onTetheringEntitlementResult(int);
   }
 
-  public abstract static class TetheringManager.StartTetheringCallback {
-    ctor public TetheringManager.StartTetheringCallback();
-    method public void onTetheringFailed(int);
-    method public void onTetheringStarted();
+  public static interface TetheringManager.StartTetheringCallback {
+    method public default void onTetheringFailed(int);
+    method public default void onTetheringStarted();
   }
 
-  public abstract static class TetheringManager.TetheringEventCallback {
-    ctor public TetheringManager.TetheringEventCallback();
-    method public void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
-    method public void onError(@NonNull String, int);
-    method public void onOffloadStatusChanged(int);
-    method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
-    method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
-    method public void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
-    method public void onTetheringSupported(boolean);
-    method public void onUpstreamChanged(@Nullable android.net.Network);
+  public static interface TetheringManager.TetheringEventCallback {
+    method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
+    method public default void onError(@NonNull String, int);
+    method public default void onOffloadStatusChanged(int);
+    method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
+    method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onTetheringSupported(boolean);
+    method public default void onUpstreamChanged(@Nullable android.net.Network);
   }
 
-  @Deprecated public static class TetheringManager.TetheringInterfaceRegexps {
-    ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]);
-    method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
-    method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
-    method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
+  public static class TetheringManager.TetheringInterfaceRegexps {
+    method @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
+    method @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
+    method @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
   }
 
   public static class TetheringManager.TetheringRequest {
@@ -117,9 +115,14 @@
   public static class TetheringManager.TetheringRequest.Builder {
     ctor public TetheringManager.TetheringRequest.Builder(int);
     method @NonNull public android.net.TetheringManager.TetheringRequest build();
+    method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
+    method @Nullable public android.net.LinkAddress getLocalIpv4Address();
+    method public boolean getShouldShowEntitlementUi();
+    method public int getTetheringType();
+    method public boolean isExemptFromEntitlementCheck();
     method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
-    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean);
-    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress);
+    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
+    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
   }
 
 }
diff --git a/Tethering/common/TetheringLib/api/system-current.txt b/Tethering/common/TetheringLib/api/system-current.txt
index d6fcb62..4ac44ba 100644
--- a/Tethering/common/TetheringLib/api/system-current.txt
+++ b/Tethering/common/TetheringLib/api/system-current.txt
@@ -23,7 +23,6 @@
     method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
-    method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
@@ -40,19 +39,20 @@
     field public static final int TETHERING_WIFI = 0; // 0x0
     field public static final int TETHERING_WIFI_P2P = 3; // 0x3
     field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
-    field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9
-    field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8
+    field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9
+    field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8
     field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
     field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
-    field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5
+    field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5
     field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
     field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
     field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
-    field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
+    field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb
     field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
     field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
     field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
     field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
+    field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10
     field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
     field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
     field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
@@ -64,29 +64,19 @@
     method public void onTetheringEntitlementResult(int);
   }
 
-  public abstract static class TetheringManager.StartTetheringCallback {
-    ctor public TetheringManager.StartTetheringCallback();
-    method public void onTetheringFailed(int);
-    method public void onTetheringStarted();
+  public static interface TetheringManager.StartTetheringCallback {
+    method public default void onTetheringFailed(int);
+    method public default void onTetheringStarted();
   }
 
-  public abstract static class TetheringManager.TetheringEventCallback {
-    ctor public TetheringManager.TetheringEventCallback();
-    method public void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
-    method public void onError(@NonNull String, int);
-    method public void onOffloadStatusChanged(int);
-    method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
-    method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
-    method public void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
-    method public void onTetheringSupported(boolean);
-    method public void onUpstreamChanged(@Nullable android.net.Network);
-  }
-
-  @Deprecated public static class TetheringManager.TetheringInterfaceRegexps {
-    ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]);
-    method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
-    method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
-    method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
+  public static interface TetheringManager.TetheringEventCallback {
+    method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
+    method public default void onError(@NonNull String, int);
+    method public default void onOffloadStatusChanged(int);
+    method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public default void onTetheringSupported(boolean);
+    method public default void onUpstreamChanged(@Nullable android.net.Network);
   }
 
   public static class TetheringManager.TetheringRequest {
@@ -95,9 +85,14 @@
   public static class TetheringManager.TetheringRequest.Builder {
     ctor public TetheringManager.TetheringRequest.Builder(int);
     method @NonNull public android.net.TetheringManager.TetheringRequest build();
+    method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
+    method @Nullable public android.net.LinkAddress getLocalIpv4Address();
+    method public boolean getShouldShowEntitlementUi();
+    method public int getTetheringType();
+    method public boolean isExemptFromEntitlementCheck();
     method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
-    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean);
-    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress);
+    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
+    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
   }
 
 }
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 7f831ce..bd65fc2 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -115,6 +115,19 @@
      */
     public static final String EXTRA_ERRORED_TETHER = "erroredArray";
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = false, value = {
+            TETHERING_WIFI,
+            TETHERING_USB,
+            TETHERING_BLUETOOTH,
+            TETHERING_WIFI_P2P,
+            TETHERING_NCM,
+            TETHERING_ETHERNET,
+    })
+    public @interface TetheringType {
+    }
+
     /**
      * Invalid tethering type.
      * @see #startTethering.
@@ -158,22 +171,60 @@
      */
     public static final int TETHERING_ETHERNET = 5;
 
-    public static final int TETHER_ERROR_NO_ERROR           = 0;
-    public static final int TETHER_ERROR_UNKNOWN_IFACE      = 1;
-    public static final int TETHER_ERROR_SERVICE_UNAVAIL    = 2;
-    public static final int TETHER_ERROR_UNSUPPORTED        = 3;
-    public static final int TETHER_ERROR_UNAVAIL_IFACE      = 4;
-    public static final int TETHER_ERROR_MASTER_ERROR       = 5;
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            TETHER_ERROR_NO_ERROR,
+            TETHER_ERROR_PROVISIONING_FAILED,
+            TETHER_ERROR_ENTITLEMENT_UNKNOWN,
+    })
+    public @interface EntitlementResult {
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            TETHER_ERROR_NO_ERROR,
+            TETHER_ERROR_UNKNOWN_IFACE,
+            TETHER_ERROR_SERVICE_UNAVAIL,
+            TETHER_ERROR_INTERNAL_ERROR,
+            TETHER_ERROR_TETHER_IFACE_ERROR,
+            TETHER_ERROR_ENABLE_FORWARDING_ERROR,
+            TETHER_ERROR_DISABLE_FORWARDING_ERROR,
+            TETHER_ERROR_IFACE_CFG_ERROR,
+            TETHER_ERROR_DHCPSERVER_ERROR,
+    })
+    public @interface TetheringIfaceError {
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            TETHER_ERROR_SERVICE_UNAVAIL,
+            TETHER_ERROR_INTERNAL_ERROR,
+            TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION,
+            TETHER_ERROR_UNKNOWN_TYPE,
+    })
+    public @interface StartTetheringError {
+    }
+
+    public static final int TETHER_ERROR_NO_ERROR = 0;
+    public static final int TETHER_ERROR_UNKNOWN_IFACE = 1;
+    public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2;
+    public static final int TETHER_ERROR_UNSUPPORTED = 3;
+    public static final int TETHER_ERROR_UNAVAIL_IFACE = 4;
+    public static final int TETHER_ERROR_INTERNAL_ERROR = 5;
     public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6;
     public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7;
-    public static final int TETHER_ERROR_ENABLE_NAT_ERROR     = 8;
-    public static final int TETHER_ERROR_DISABLE_NAT_ERROR    = 9;
-    public static final int TETHER_ERROR_IFACE_CFG_ERROR      = 10;
-    public static final int TETHER_ERROR_PROVISION_FAILED     = 11;
-    public static final int TETHER_ERROR_DHCPSERVER_ERROR     = 12;
+    public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8;
+    public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9;
+    public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
+    public static final int TETHER_ERROR_PROVISIONING_FAILED = 11;
+    public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12;
     public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13;
     public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14;
     public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15;
+    public static final int TETHER_ERROR_UNKNOWN_TYPE = 16;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -508,23 +559,40 @@
             private final TetheringRequestParcel mBuilderParcel;
 
             /** Default constructor of Builder. */
-            public Builder(final int type) {
+            public Builder(@TetheringType final int type) {
                 mBuilderParcel = new TetheringRequestParcel();
                 mBuilderParcel.tetheringType = type;
                 mBuilderParcel.localIPv4Address = null;
+                mBuilderParcel.staticClientAddress = null;
                 mBuilderParcel.exemptFromEntitlementCheck = false;
                 mBuilderParcel.showProvisioningUi = true;
             }
 
             /**
-             * Configure tethering with static IPv4 assignment (with DHCP disabled).
+             * Configure tethering with static IPv4 assignment.
              *
-             * @param localIPv4Address The preferred local IPv4 address to use.
+             * The clientAddress must be in the localIPv4Address prefix. A DHCP server will be
+             * started, but will only be able to offer the client address. The two addresses must
+             * be in the same prefix.
+             *
+             * @param localIPv4Address The preferred local IPv4 link address to use.
+             * @param clientAddress The static client address.
              */
             @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
             @NonNull
-            public Builder useStaticIpv4Addresses(@NonNull final LinkAddress localIPv4Address) {
+            public Builder setStaticIpv4Addresses(@NonNull final LinkAddress localIPv4Address,
+                    @NonNull final LinkAddress clientAddress) {
+                Objects.requireNonNull(localIPv4Address);
+                Objects.requireNonNull(clientAddress);
+                if (localIPv4Address.getPrefixLength() != clientAddress.getPrefixLength()
+                        || !localIPv4Address.isIpv4() || !clientAddress.isIpv4()
+                        || !new IpPrefix(localIPv4Address.toString()).equals(
+                        new IpPrefix(clientAddress.toString()))) {
+                    throw new IllegalArgumentException("Invalid server or client addresses");
+                }
+
                 mBuilderParcel.localIPv4Address = localIPv4Address;
+                mBuilderParcel.staticClientAddress = clientAddress;
                 return this;
             }
 
@@ -536,11 +604,14 @@
                 return this;
             }
 
-            /** Start tethering without showing the provisioning UI. */
+            /**
+             * If an entitlement check is needed, sets whether to show the entitlement UI or to
+             * perform a silent entitlement check. By default, the entitlement UI is shown.
+             */
             @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
             @NonNull
-            public Builder setSilentProvisioning(boolean silent) {
-                mBuilderParcel.showProvisioningUi = silent;
+            public Builder setShouldShowEntitlementUi(boolean showUi) {
+                mBuilderParcel.showProvisioningUi = showUi;
                 return this;
             }
 
@@ -549,6 +620,33 @@
             public TetheringRequest build() {
                 return new TetheringRequest(mBuilderParcel);
             }
+
+            @Nullable
+            public LinkAddress getLocalIpv4Address() {
+                return mBuilderParcel.localIPv4Address;
+            }
+
+            /** Get static client address. */
+            @Nullable
+            public LinkAddress getClientStaticIpv4Address() {
+                return mBuilderParcel.staticClientAddress;
+            }
+
+            /** Get tethering type. */
+            @TetheringType
+            public int getTetheringType() {
+                return mBuilderParcel.tetheringType;
+            }
+
+            /** Check if exempt from entitlement check. */
+            public boolean isExemptFromEntitlementCheck() {
+                return mBuilderParcel.exemptFromEntitlementCheck;
+            }
+
+            /** Check if show entitlement ui.  */
+            public boolean getShouldShowEntitlementUi() {
+                return mBuilderParcel.showProvisioningUi;
+            }
         }
 
         /**
@@ -563,6 +661,7 @@
         public String toString() {
             return "TetheringRequest [ type= " + mRequestParcel.tetheringType
                     + ", localIPv4Address= " + mRequestParcel.localIPv4Address
+                    + ", staticClientAddress= " + mRequestParcel.staticClientAddress
                     + ", exemptFromEntitlementCheck= "
                     + mRequestParcel.exemptFromEntitlementCheck + ", showProvisioningUi= "
                     + mRequestParcel.showProvisioningUi + " ]";
@@ -572,18 +671,18 @@
     /**
      * Callback for use with {@link #startTethering} to find out whether tethering succeeded.
      */
-    public abstract static class StartTetheringCallback {
+    public interface StartTetheringCallback {
         /**
          * Called when tethering has been successfully started.
          */
-        public void onTetheringStarted() {}
+        default void onTetheringStarted() {}
 
         /**
          * Called when starting tethering failed.
          *
-         * @param resultCode One of the {@code TETHER_ERROR_*} constants.
+         * @param error The error that caused the failure.
          */
-        public void onTetheringFailed(final int resultCode) {}
+        default void onTetheringFailed(@StartTetheringError final int error) {}
     }
 
     /**
@@ -633,11 +732,13 @@
      * @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants.
      * @param executor {@link Executor} to specify the thread upon which the callback of
      *         TetheringRequest will be invoked.
+     * @hide
      */
     @RequiresPermission(anyOf = {
             android.Manifest.permission.TETHER_PRIVILEGED,
             android.Manifest.permission.WRITE_SETTINGS
     })
+    @SystemApi(client = MODULE_LIBRARIES)
     public void startTethering(int type, @NonNull final Executor executor,
             @NonNull final StartTetheringCallback callback) {
         startTethering(new TetheringRequest.Builder(type).build(), executor, callback);
@@ -654,7 +755,7 @@
             android.Manifest.permission.TETHER_PRIVILEGED,
             android.Manifest.permission.WRITE_SETTINGS
     })
-    public void stopTethering(final int type) {
+    public void stopTethering(@TetheringType final int type) {
         final String callerPkg = mContext.getOpPackageName();
         Log.i(TAG, "stopTethering caller:" + callerPkg);
 
@@ -679,10 +780,10 @@
          *
          * @param resultCode an int value of entitlement result. It may be one of
          *         {@link #TETHER_ERROR_NO_ERROR},
-         *         {@link #TETHER_ERROR_PROVISION_FAILED}, or
+         *         {@link #TETHER_ERROR_PROVISIONING_FAILED}, or
          *         {@link #TETHER_ERROR_ENTITLEMENT_UNKNOWN}.
          */
-        void onTetheringEntitlementResult(int resultCode);
+        void onTetheringEntitlementResult(@EntitlementResult int result);
     }
 
     /**
@@ -697,7 +798,8 @@
      * fail if a tethering entitlement check is required.
      *
      * @param type the downstream type of tethering. Must be one of {@code #TETHERING_*} constants.
-     * @param showEntitlementUi a boolean indicating whether to run UI-based entitlement check.
+     * @param showEntitlementUi a boolean indicating whether to check result for the UI-based
+     *         entitlement check or the silent entitlement check.
      * @param executor the executor on which callback will be invoked.
      * @param listener an {@link OnTetheringEntitlementResultListener} which will be called to
      *         notify the caller of the result of entitlement check. The listener may be called zero
@@ -707,7 +809,8 @@
             android.Manifest.permission.TETHER_PRIVILEGED,
             android.Manifest.permission.WRITE_SETTINGS
     })
-    public void requestLatestTetheringEntitlementResult(int type, boolean showEntitlementUi,
+    public void requestLatestTetheringEntitlementResult(@TetheringType int type,
+            boolean showEntitlementUi,
             @NonNull Executor executor,
             @NonNull final OnTetheringEntitlementResultListener listener) {
         if (listener == null) {
@@ -736,7 +839,7 @@
      */
     // TODO: improve the usage of ResultReceiver, b/145096122
     @SystemApi(client = MODULE_LIBRARIES)
-    public void requestLatestTetheringEntitlementResult(final int type,
+    public void requestLatestTetheringEntitlementResult(@TetheringType final int type,
             @NonNull final ResultReceiver receiver, final boolean showEntitlementUi) {
         final String callerPkg = mContext.getOpPackageName();
         Log.i(TAG, "getLatestTetheringEntitlementResult caller:" + callerPkg);
@@ -749,7 +852,7 @@
      * Callback for use with {@link registerTetheringEventCallback} to find out tethering
      * upstream status.
      */
-    public abstract static class TetheringEventCallback {
+    public interface TetheringEventCallback {
         /**
          * Called when tethering supported status changed.
          *
@@ -761,7 +864,7 @@
          *
          * @param supported The new supported status
          */
-        public void onTetheringSupported(boolean supported) {}
+        default void onTetheringSupported(boolean supported) {}
 
         /**
          * Called when tethering upstream changed.
@@ -772,7 +875,7 @@
          * @param network the {@link Network} of tethering upstream. Null means tethering doesn't
          * have any upstream.
          */
-        public void onUpstreamChanged(@Nullable Network network) {}
+        default void onUpstreamChanged(@Nullable Network network) {}
 
         /**
          * Called when there was a change in tethering interface regular expressions.
@@ -780,28 +883,30 @@
          * <p>This will be called immediately after the callback is registered, and may be called
          * multiple times later upon changes.
          * @param reg The new regular expressions.
-         * @deprecated Referencing interfaces by regular expressions is a deprecated mechanism.
+         *
+         * @hide
          */
-        @Deprecated
-        public void onTetherableInterfaceRegexpsChanged(@NonNull TetheringInterfaceRegexps reg) {}
+        @SystemApi(client = MODULE_LIBRARIES)
+        default void onTetherableInterfaceRegexpsChanged(@NonNull TetheringInterfaceRegexps reg) {}
 
         /**
-         * Called when there was a change in the list of tetherable interfaces.
+         * Called when there was a change in the list of tetherable interfaces. Tetherable
+         * interface means this interface is available and can be used for tethering.
          *
          * <p>This will be called immediately after the callback is registered, and may be called
          * multiple times later upon changes.
-         * @param interfaces The list of tetherable interfaces.
+         * @param interfaces The list of tetherable interface names.
          */
-        public void onTetherableInterfacesChanged(@NonNull List<String> interfaces) {}
+        default void onTetherableInterfacesChanged(@NonNull List<String> interfaces) {}
 
         /**
          * Called when there was a change in the list of tethered interfaces.
          *
          * <p>This will be called immediately after the callback is registered, and may be called
          * multiple times later upon changes.
-         * @param interfaces The list of tethered interfaces.
+         * @param interfaces The list of 0 or more String of currently tethered interface names.
          */
-        public void onTetheredInterfacesChanged(@NonNull List<String> interfaces) {}
+        default void onTetheredInterfacesChanged(@NonNull List<String> interfaces) {}
 
         /**
          * Called when an error occurred configuring tethering.
@@ -811,7 +916,7 @@
          * @param ifName Name of the interface.
          * @param error One of {@code TetheringManager#TETHER_ERROR_*}.
          */
-        public void onError(@NonNull String ifName, int error) {}
+        default void onError(@NonNull String ifName, @TetheringIfaceError int error) {}
 
         /**
          * Called when the list of tethered clients changes.
@@ -824,7 +929,7 @@
          * determine if they are still connected.
          * @param clients The new set of tethered clients; the collection is not ordered.
          */
-        public void onClientsChanged(@NonNull Collection<TetheredClient> clients) {}
+        default void onClientsChanged(@NonNull Collection<TetheredClient> clients) {}
 
         /**
          * Called when tethering offload status changes.
@@ -832,19 +937,20 @@
          * <p>This will be called immediately after the callback is registered.
          * @param status The offload status.
          */
-        public void onOffloadStatusChanged(@TetherOffloadStatus int status) {}
+        default void onOffloadStatusChanged(@TetherOffloadStatus int status) {}
     }
 
     /**
      * Regular expressions used to identify tethering interfaces.
-     * @deprecated Referencing interfaces by regular expressions is a deprecated mechanism.
+     * @hide
      */
-    @Deprecated
+    @SystemApi(client = MODULE_LIBRARIES)
     public static class TetheringInterfaceRegexps {
         private final String[] mTetherableBluetoothRegexs;
         private final String[] mTetherableUsbRegexs;
         private final String[] mTetherableWifiRegexs;
 
+        /** @hide */
         public TetheringInterfaceRegexps(@NonNull String[] tetherableBluetoothRegexs,
                 @NonNull String[] tetherableUsbRegexs, @NonNull String[] tetherableWifiRegexs) {
             mTetherableBluetoothRegexs = tetherableBluetoothRegexs.clone();
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl b/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
index bf19d85..c0280d3 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
@@ -25,6 +25,7 @@
 parcelable TetheringRequestParcel {
     int tetheringType;
     LinkAddress localIPv4Address;
+    LinkAddress staticClientAddress;
     boolean exemptFromEntitlementCheck;
     boolean showProvisioningUi;
 }
diff --git a/Tethering/res/values-mcc204-mnc04/strings.xml b/Tethering/res/values-mcc204-mnc04/strings.xml
new file mode 100644
index 0000000..6bc2e2a
--- /dev/null
+++ b/Tethering/res/values-mcc204-mnc04/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<resources>
+    <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+    <string name="no_upstream_notification_title">Tethering &amp; Mobile Hotspot has no internet access</string>
+
+    <!-- String for cellular roaming notification title [CHAR LIMIT=200]  -->
+    <string name="upstream_roaming_notification_title">Roaming</string>
+    <!-- String for cellular roaming notification message [CHAR LIMIT=500]  -->
+    <string name="upstream_roaming_notification_message">You will be charged on a per MB basis for all data sent or received while using this service outside the Verizon Network. Please check our website for current international rates. To minimize charges, visit My Verizon periodically to monitor your usage, check your device settings to confirm which devices are connected, and consider using alternate data connections when available</string>
+    <!-- String for cellular roaming notification continue button [CHAR LIMIT=200]  -->
+    <string name="upstream_roaming_notification_continue_button">Continue</string>
+</resources>
\ No newline at end of file
diff --git a/Tethering/res/values-mcc310-mnc004/strings.xml b/Tethering/res/values-mcc310-mnc004/strings.xml
new file mode 100644
index 0000000..6bc2e2a
--- /dev/null
+++ b/Tethering/res/values-mcc310-mnc004/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<resources>
+    <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+    <string name="no_upstream_notification_title">Tethering &amp; Mobile Hotspot has no internet access</string>
+
+    <!-- String for cellular roaming notification title [CHAR LIMIT=200]  -->
+    <string name="upstream_roaming_notification_title">Roaming</string>
+    <!-- String for cellular roaming notification message [CHAR LIMIT=500]  -->
+    <string name="upstream_roaming_notification_message">You will be charged on a per MB basis for all data sent or received while using this service outside the Verizon Network. Please check our website for current international rates. To minimize charges, visit My Verizon periodically to monitor your usage, check your device settings to confirm which devices are connected, and consider using alternate data connections when available</string>
+    <!-- String for cellular roaming notification continue button [CHAR LIMIT=200]  -->
+    <string name="upstream_roaming_notification_continue_button">Continue</string>
+</resources>
\ No newline at end of file
diff --git a/Tethering/res/values-mcc310-mnc120/strings.xml b/Tethering/res/values-mcc310-mnc120/strings.xml
new file mode 100644
index 0000000..618df90
--- /dev/null
+++ b/Tethering/res/values-mcc310-mnc120/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- String for tethered notification title with client number info. -->
+    <plurals name="tethered_notification_title_with_client_number">
+        <item quantity="one"><xliff:g>%1$d</xliff:g> device connected.</item>
+        <item quantity="other"><xliff:g>%1$d</xliff:g> devices connected.</item>
+    </plurals>
+</resources>
\ No newline at end of file
diff --git a/Tethering/res/values-mcc311-mnc480/strings.xml b/Tethering/res/values-mcc311-mnc480/strings.xml
new file mode 100644
index 0000000..6bc2e2a
--- /dev/null
+++ b/Tethering/res/values-mcc311-mnc480/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<resources>
+    <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+    <string name="no_upstream_notification_title">Tethering &amp; Mobile Hotspot has no internet access</string>
+
+    <!-- String for cellular roaming notification title [CHAR LIMIT=200]  -->
+    <string name="upstream_roaming_notification_title">Roaming</string>
+    <!-- String for cellular roaming notification message [CHAR LIMIT=500]  -->
+    <string name="upstream_roaming_notification_message">You will be charged on a per MB basis for all data sent or received while using this service outside the Verizon Network. Please check our website for current international rates. To minimize charges, visit My Verizon periodically to monitor your usage, check your device settings to confirm which devices are connected, and consider using alternate data connections when available</string>
+    <!-- String for cellular roaming notification continue button [CHAR LIMIT=200]  -->
+    <string name="upstream_roaming_notification_continue_button">Continue</string>
+</resources>
\ No newline at end of file
diff --git a/Tethering/res/values-mcc311-mnc490/strings.xml b/Tethering/res/values-mcc311-mnc490/strings.xml
new file mode 100644
index 0000000..618df90
--- /dev/null
+++ b/Tethering/res/values-mcc311-mnc490/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- String for tethered notification title with client number info. -->
+    <plurals name="tethered_notification_title_with_client_number">
+        <item quantity="one"><xliff:g>%1$d</xliff:g> device connected.</item>
+        <item quantity="other"><xliff:g>%1$d</xliff:g> devices connected.</item>
+    </plurals>
+</resources>
\ No newline at end of file
diff --git a/Tethering/res/values-mcc312-mnc530/strings.xml b/Tethering/res/values-mcc312-mnc530/strings.xml
new file mode 100644
index 0000000..618df90
--- /dev/null
+++ b/Tethering/res/values-mcc312-mnc530/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- String for tethered notification title with client number info. -->
+    <plurals name="tethered_notification_title_with_client_number">
+        <item quantity="one"><xliff:g>%1$d</xliff:g> device connected.</item>
+        <item quantity="other"><xliff:g>%1$d</xliff:g> devices connected.</item>
+    </plurals>
+</resources>
\ No newline at end of file
diff --git a/Tethering/res/values/strings.xml b/Tethering/res/values/strings.xml
index ba98a66..981524e 100644
--- a/Tethering/res/values/strings.xml
+++ b/Tethering/res/values/strings.xml
@@ -13,12 +13,15 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<resources>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Shown when the device is tethered -->
     <!-- String for tethered notification title [CHAR LIMIT=200] -->
     <string name="tethered_notification_title">Tethering or hotspot active</string>
     <!-- String for tethered notification message [CHAR LIMIT=200] -->
     <string name="tethered_notification_message">Tap to set up.</string>
+    <!-- String for tethered notification title with client number info. -->
+    <plurals name="tethered_notification_title_with_client_number">
+    </plurals>
 
     <!-- This notification is shown when tethering has been disabled on a user's device.
     The device is managed by the user's employer. Tethering can't be turned on unless the
@@ -32,4 +35,14 @@
     Internet" settings page. That is currently the tether_settings_title_all string. -->
     <!-- String for tether notification channel name [CHAR LIMIT=200] -->
     <string name="notification_channel_tethering_status">Hotspot &amp; tethering status</string>
+
+    <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+    <string name="no_upstream_notification_title"></string>
+
+    <!-- String for cellular roaming notification title [CHAR LIMIT=200]  -->
+    <string name="upstream_roaming_notification_title"></string>
+    <!-- String for cellular roaming notification message [CHAR LIMIT=500]  -->
+    <string name="upstream_roaming_notification_message"></string>
+    <!-- String for cellular roaming notification continue button [CHAR LIMIT=200]  -->
+    <string name="upstream_roaming_notification_continue_button"></string>
 </resources>
\ No newline at end of file
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 6c0c432..c5478d2 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -35,6 +35,7 @@
 import android.net.RouteInfo;
 import android.net.TetheredClient;
 import android.net.TetheringManager;
+import android.net.TetheringRequestParcel;
 import android.net.dhcp.DhcpLeaseParcelable;
 import android.net.dhcp.DhcpServerCallbacks;
 import android.net.dhcp.DhcpServingParamsParcel;
@@ -243,6 +244,10 @@
     private IDhcpServer mDhcpServer;
     private RaParams mLastRaParams;
     private LinkAddress mIpv4Address;
+
+    private LinkAddress mStaticIpv4ServerAddr;
+    private LinkAddress mStaticIpv4ClientAddr;
+
     @NonNull
     private List<TetheredClient> mDhcpLeases = Collections.emptyList();
 
@@ -547,6 +552,8 @@
         // into calls to InterfaceController, shared with startIPv4().
         mInterfaceCtrl.clearIPv4Address();
         mIpv4Address = null;
+        mStaticIpv4ServerAddr = null;
+        mStaticIpv4ClientAddr = null;
     }
 
     private boolean configureIPv4(boolean enabled) {
@@ -557,7 +564,10 @@
         final Inet4Address srvAddr;
         int prefixLen = 0;
         try {
-            if (mInterfaceType == TetheringManager.TETHERING_USB
+            if (mStaticIpv4ServerAddr != null) {
+                srvAddr = (Inet4Address) mStaticIpv4ServerAddr.getAddress();
+                prefixLen = mStaticIpv4ServerAddr.getPrefixLength();
+            } else if (mInterfaceType == TetheringManager.TETHERING_USB
                     || mInterfaceType == TetheringManager.TETHERING_NCM) {
                 srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR);
                 prefixLen = USB_PREFIX_LENGTH;
@@ -602,10 +612,6 @@
             return false;
         }
 
-        if (!configureDhcp(enabled, srvAddr, prefixLen)) {
-            return false;
-        }
-
         // Directly-connected route.
         final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(),
                 mIpv4Address.getPrefixLength());
@@ -617,7 +623,8 @@
             mLinkProperties.removeLinkAddress(mIpv4Address);
             mLinkProperties.removeRoute(route);
         }
-        return true;
+
+        return configureDhcp(enabled, srvAddr, prefixLen);
     }
 
     private String getRandomWifiIPv4Address() {
@@ -937,6 +944,13 @@
         mLinkProperties.setInterfaceName(mIfaceName);
     }
 
+    private void maybeConfigureStaticIp(final TetheringRequestParcel request) {
+        if (request == null) return;
+
+        mStaticIpv4ServerAddr = request.localIPv4Address;
+        mStaticIpv4ClientAddr = request.staticClientAddress;
+    }
+
     class InitialState extends State {
         @Override
         public void enter() {
@@ -951,9 +965,11 @@
                     mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
                     switch (message.arg1) {
                         case STATE_LOCAL_ONLY:
+                            maybeConfigureStaticIp((TetheringRequestParcel) message.obj);
                             transitionTo(mLocalHotspotState);
                             break;
                         case STATE_TETHERED:
+                            maybeConfigureStaticIp((TetheringRequestParcel) message.obj);
                             transitionTo(mTetheredState);
                             break;
                         default:
@@ -1038,7 +1054,7 @@
                 case CMD_START_TETHERING_ERROR:
                 case CMD_STOP_TETHERING_ERROR:
                 case CMD_SET_DNS_FORWARDERS_ERROR:
-                    mLastError = TetheringManager.TETHER_ERROR_MASTER_ERROR;
+                    mLastError = TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
                     transitionTo(mInitialState);
                     break;
                 default:
@@ -1169,7 +1185,7 @@
                         } catch (RemoteException | ServiceSpecificException e) {
                             mLog.e("Exception enabling NAT: " + e.toString());
                             cleanupUpstream();
-                            mLastError = TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR;
+                            mLastError = TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
                             transitionTo(mInitialState);
                             return true;
                         }
diff --git a/Tethering/src/android/net/util/TetheringUtils.java b/Tethering/src/android/net/util/TetheringUtils.java
index 5a6d5c1..dd67ddd 100644
--- a/Tethering/src/android/net/util/TetheringUtils.java
+++ b/Tethering/src/android/net/util/TetheringUtils.java
@@ -15,8 +15,11 @@
  */
 package android.net.util;
 
+import android.net.TetheringRequestParcel;
+
 import java.io.FileDescriptor;
 import java.net.SocketException;
+import java.util.Objects;
 
 /**
  * Native methods for tethering utilization.
@@ -38,4 +41,17 @@
     public static int uint16(short s) {
         return s & 0xffff;
     }
+
+    /** Check whether two TetheringRequestParcels are the same. */
+    public static boolean isTetheringRequestEquals(final TetheringRequestParcel request,
+            final TetheringRequestParcel otherRequest) {
+        if (request == otherRequest) return true;
+
+        return request != null && otherRequest != null
+                && request.tetheringType == otherRequest.tetheringType
+                && Objects.equals(request.localIPv4Address, otherRequest.localIPv4Address)
+                && Objects.equals(request.staticClientAddress, otherRequest.staticClientAddress)
+                && request.exemptFromEntitlementCheck == otherRequest.exemptFromEntitlementCheck
+                && request.showProvisioningUi == otherRequest.showProvisioningUi;
+    }
 }
diff --git a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
index e81d6ac..bd60594 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -25,7 +25,7 @@
 import static android.net.TetheringManager.TETHERING_WIFI;
 import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED;
+import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -579,7 +579,7 @@
         switch (value) {
             case TETHER_ERROR_ENTITLEMENT_UNKNOWN: return "TETHER_ERROR_ENTITLEMENT_UNKONWN";
             case TETHER_ERROR_NO_ERROR: return "TETHER_ERROR_NO_ERROR";
-            case TETHER_ERROR_PROVISION_FAILED: return "TETHER_ERROR_PROVISION_FAILED";
+            case TETHER_ERROR_PROVISIONING_FAILED: return "TETHER_ERROR_PROVISIONING_FAILED";
             default:
                 return String.format("UNKNOWN ERROR (%d)", value);
         }
@@ -592,7 +592,7 @@
             protected void onReceiveResult(int resultCode, Bundle resultData) {
                 int updatedCacheValue = updateEntitlementCacheValue(type, resultCode);
                 addDownstreamMapping(type, updatedCacheValue);
-                if (updatedCacheValue == TETHER_ERROR_PROVISION_FAILED && notifyFail) {
+                if (updatedCacheValue == TETHER_ERROR_PROVISIONING_FAILED && notifyFail) {
                     mListener.onUiEntitlementFailed(type);
                 }
                 if (receiver != null) receiver.send(updatedCacheValue, null);
@@ -635,8 +635,8 @@
             mEntitlementCacheValue.put(type, resultCode);
             return resultCode;
         } else {
-            mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISION_FAILED);
-            return TETHER_ERROR_PROVISION_FAILED;
+            mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISIONING_FAILED);
+            return TETHER_ERROR_PROVISIONING_FAILED;
         }
     }
 
diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index 3d8dbab..36113ac 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -39,11 +39,12 @@
 import static android.net.TetheringManager.TETHERING_USB;
 import static android.net.TetheringManager.TETHERING_WIFI;
 import static android.net.TetheringManager.TETHERING_WIFI_P2P;
-import static android.net.TetheringManager.TETHER_ERROR_MASTER_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
 import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
 import static android.net.TetheringManager.TETHER_ERROR_UNAVAIL_IFACE;
 import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
+import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_TYPE;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
@@ -92,6 +93,7 @@
 import android.net.util.InterfaceSet;
 import android.net.util.PrefixUtils;
 import android.net.util.SharedLog;
+import android.net.util.TetheringUtils;
 import android.net.util.VersionedBroadcastListener;
 import android.net.wifi.WifiClient;
 import android.net.wifi.WifiManager;
@@ -196,6 +198,11 @@
     private final SharedLog mLog = new SharedLog(TAG);
     private final RemoteCallbackList<ITetheringEventCallback> mTetheringEventCallbacks =
             new RemoteCallbackList<>();
+    // Currently active tethering requests per tethering type. Only one of each type can be
+    // requested at a time. After a tethering type is requested, the map keeps tethering parameters
+    // to be used after the interface comes up asynchronously.
+    private final SparseArray<TetheringRequestParcel> mActiveTetheringRequests =
+            new SparseArray<>();
 
     // used to synchronize public access to members
     private final Object mPublicSync;
@@ -487,14 +494,31 @@
     }
 
     void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) {
-        mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType,
-                request.showProvisioningUi);
-        enableTetheringInternal(request.tetheringType, true /* enabled */, listener);
+        mHandler.post(() -> {
+            final TetheringRequestParcel unfinishedRequest = mActiveTetheringRequests.get(
+                    request.tetheringType);
+            // If tethering is already enabled with a different request,
+            // disable before re-enabling.
+            if (unfinishedRequest != null
+                    && !TetheringUtils.isTetheringRequestEquals(unfinishedRequest, request)) {
+                enableTetheringInternal(request.tetheringType, false /* disabled */, null);
+                mEntitlementMgr.stopProvisioningIfNeeded(request.tetheringType);
+            }
+            mActiveTetheringRequests.put(request.tetheringType, request);
+
+            mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType,
+                    request.showProvisioningUi);
+            enableTetheringInternal(request.tetheringType, true /* enabled */, listener);
+        });
     }
 
     void stopTethering(int type) {
-        enableTetheringInternal(type, false /* disabled */, null);
-        mEntitlementMgr.stopProvisioningIfNeeded(type);
+        mHandler.post(() -> {
+            mActiveTetheringRequests.remove(type);
+
+            enableTetheringInternal(type, false /* disabled */, null);
+            mEntitlementMgr.stopProvisioningIfNeeded(type);
+        });
     }
 
     /**
@@ -503,39 +527,45 @@
      */
     private void enableTetheringInternal(int type, boolean enable,
             final IIntResultListener listener) {
-        int result;
+        int result = TETHER_ERROR_NO_ERROR;
         switch (type) {
             case TETHERING_WIFI:
                 result = setWifiTethering(enable);
-                sendTetherResult(listener, result);
                 break;
             case TETHERING_USB:
                 result = setUsbTethering(enable);
-                sendTetherResult(listener, result);
                 break;
             case TETHERING_BLUETOOTH:
                 setBluetoothTethering(enable, listener);
                 break;
             case TETHERING_NCM:
                 result = setNcmTethering(enable);
-                sendTetherResult(listener, result);
                 break;
             case TETHERING_ETHERNET:
                 result = setEthernetTethering(enable);
-                sendTetherResult(listener, result);
                 break;
             default:
                 Log.w(TAG, "Invalid tether type.");
-                sendTetherResult(listener, TETHER_ERROR_UNKNOWN_IFACE);
+                result = TETHER_ERROR_UNKNOWN_TYPE;
+        }
+
+        // The result of Bluetooth tethering will be sent by #setBluetoothTethering.
+        if (type != TETHERING_BLUETOOTH) {
+            sendTetherResult(listener, result, type);
         }
     }
 
-    private void sendTetherResult(final IIntResultListener listener, int result) {
+    private void sendTetherResult(final IIntResultListener listener, final int result,
+            final int type) {
         if (listener != null) {
             try {
                 listener.onResult(result);
             } catch (RemoteException e) { }
         }
+
+        // If changing tethering fail, remove corresponding request
+        // no matter who trigger the start/stop.
+        if (result != TETHER_ERROR_NO_ERROR) mActiveTetheringRequests.remove(type);
     }
 
     private int setWifiTethering(final boolean enable) {
@@ -557,7 +587,7 @@
             Binder.restoreCallingIdentity(ident);
         }
 
-        return TETHER_ERROR_MASTER_ERROR;
+        return TETHER_ERROR_INTERNAL_ERROR;
     }
 
     private void setBluetoothTethering(final boolean enable, final IIntResultListener listener) {
@@ -565,7 +595,7 @@
         if (adapter == null || !adapter.isEnabled()) {
             Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: "
                     + (adapter == null));
-            sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL);
+            sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL, TETHERING_BLUETOOTH);
             return;
         }
 
@@ -593,8 +623,8 @@
                 // We should figure out a way to bubble up that failure instead of sending success.
                 final int result = (((BluetoothPan) proxy).isTetheringOn() == enable)
                         ? TETHER_ERROR_NO_ERROR
-                        : TETHER_ERROR_MASTER_ERROR;
-                sendTetherResult(listener, result);
+                        : TETHER_ERROR_INTERNAL_ERROR;
+                sendTetherResult(listener, result, TETHERING_BLUETOOTH);
                 adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
             }
         }, BluetoothProfile.PAN);
@@ -611,21 +641,21 @@
                 mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback);
             } else {
                 stopEthernetTetheringLocked();
-                if (mEthernetCallback != null) {
-                    mEthernetIfaceRequest.release();
-                    mEthernetCallback = null;
-                    mEthernetIfaceRequest = null;
-                }
             }
         }
         return TETHER_ERROR_NO_ERROR;
     }
 
     private void stopEthernetTetheringLocked() {
-        if (mConfiguredEthernetIface == null) return;
-        changeInterfaceState(mConfiguredEthernetIface, IpServer.STATE_AVAILABLE);
-        stopTrackingInterfaceLocked(mConfiguredEthernetIface);
-        mConfiguredEthernetIface = null;
+        if (mConfiguredEthernetIface != null) {
+            stopTrackingInterfaceLocked(mConfiguredEthernetIface);
+            mConfiguredEthernetIface = null;
+        }
+        if (mEthernetCallback != null) {
+            mEthernetIfaceRequest.release();
+            mEthernetCallback = null;
+            mEthernetIfaceRequest = null;
+        }
     }
 
     private class EthernetCallback implements EthernetManager.TetheredInterfaceCallback {
@@ -672,12 +702,18 @@
                 Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
                 return TETHER_ERROR_UNAVAIL_IFACE;
             }
-            // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's
-            // queue but not yet processed, this will be a no-op and it will not
-            // return an error.
+            // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's queue but not yet
+            // processed, this will be a no-op and it will not return an error.
             //
-            // TODO: reexamine the threading and messaging model.
-            tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState);
+            // This code cannot race with untether() because they both synchronize on mPublicSync.
+            // TODO: reexamine the threading and messaging model to totally remove mPublicSync.
+            final int type = tetherState.ipServer.interfaceType();
+            final TetheringRequestParcel request = mActiveTetheringRequests.get(type, null);
+            if (request != null) {
+                mActiveTetheringRequests.delete(type);
+            }
+            tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState, 0,
+                    request);
             return TETHER_ERROR_NO_ERROR;
         }
     }
@@ -1458,7 +1494,7 @@
             } else {
                 dnsServers = mConfig.defaultIPv4DNS;
             }
-            final int netId = (network != null) ? network.netId : NETID_UNSET;
+            final int netId = (network != null) ? network.getNetId() : NETID_UNSET;
             try {
                 mNetd.tetherDnsSet(netId, dnsServers);
                 mLog.log(String.format(
@@ -2145,7 +2181,7 @@
         // If TetherMasterSM is in ErrorState, TetherMasterSM stays there.
         // Thus we give a chance for TetherMasterSM to recover to InitialState
         // by sending CMD_CLEAR_ERROR
-        if (error == TETHER_ERROR_MASTER_ERROR) {
+        if (error == TETHER_ERROR_INTERNAL_ERROR) {
             mTetherMasterSM.sendMessage(TetherMasterSM.CMD_CLEAR_ERROR, who);
         }
         int which;
diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index 020b32a..c5329d8 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -33,6 +33,7 @@
 import android.net.ITetheringEventCallback;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
+import android.net.NetworkStack;
 import android.net.TetheringRequestParcel;
 import android.net.dhcp.DhcpServerCallbacks;
 import android.net.dhcp.DhcpServingParamsParcel;
@@ -364,8 +365,7 @@
                     IBinder connector;
                     try {
                         final long before = System.currentTimeMillis();
-                        while ((connector = (IBinder) mContext.getSystemService(
-                                Context.NETWORK_STACK_SERVICE)) == null) {
+                        while ((connector = NetworkStack.getService()) == null) {
                             if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
                                 Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector");
                                 return null;
diff --git a/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 3aba214..45bb4ab 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -244,7 +244,8 @@
         // Additionally, we log a message to aid in any subsequent debugging.
         mLog.i("requesting mobile upstream network: " + mobileUpstreamRequest);
 
-        cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType, mHandler);
+        cm().requestNetwork(mobileUpstreamRequest, 0, legacyType, mHandler,
+                mMobileNetworkCallback);
     }
 
     /** Release mobile network request. */
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 948266d..3106e0e 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -21,7 +21,7 @@
 import static android.net.TetheringManager.TETHERING_USB;
 import static android.net.TetheringManager.TETHERING_WIFI;
 import static android.net.TetheringManager.TETHERING_WIFI_P2P;
-import static android.net.TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
 import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
@@ -448,7 +448,7 @@
         usbTeardownOrder.verify(mNetd, times(2)).interfaceSetCfg(
                 argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
         usbTeardownOrder.verify(mCallback).updateInterfaceState(
-                mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
+                mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_FORWARDING_ERROR);
         usbTeardownOrder.verify(mCallback).updateLinkProperties(
                 eq(mIpServer), mLinkPropertiesCaptor.capture());
         assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
diff --git a/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java b/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java
new file mode 100644
index 0000000..1499f3b
--- /dev/null
+++ b/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.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 android.net.util;
+
+import static android.net.TetheringManager.TETHERING_USB;
+import static android.net.TetheringManager.TETHERING_WIFI;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.net.LinkAddress;
+import android.net.TetheringRequestParcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.MiscAssertsKt;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TetheringUtilsTest {
+    private static final LinkAddress TEST_SERVER_ADDR = new LinkAddress("192.168.43.1/24");
+    private static final LinkAddress TEST_CLIENT_ADDR = new LinkAddress("192.168.43.5/24");
+    private TetheringRequestParcel mTetheringRequest;
+
+    @Before
+    public void setUp() {
+        mTetheringRequest = makeTetheringRequestParcel();
+    }
+
+    public TetheringRequestParcel makeTetheringRequestParcel() {
+        final TetheringRequestParcel request = new TetheringRequestParcel();
+        request.tetheringType = TETHERING_WIFI;
+        request.localIPv4Address = TEST_SERVER_ADDR;
+        request.staticClientAddress = TEST_CLIENT_ADDR;
+        request.exemptFromEntitlementCheck = false;
+        request.showProvisioningUi = true;
+        return request;
+    }
+
+    @Test
+    public void testIsTetheringRequestEquals() throws Exception {
+        TetheringRequestParcel request = makeTetheringRequestParcel();
+
+        assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, mTetheringRequest));
+        assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
+        assertTrue(TetheringUtils.isTetheringRequestEquals(null, null));
+        assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, null));
+        assertFalse(TetheringUtils.isTetheringRequestEquals(null, mTetheringRequest));
+
+        request = makeTetheringRequestParcel();
+        request.tetheringType = TETHERING_USB;
+        assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
+
+        request = makeTetheringRequestParcel();
+        request.localIPv4Address = null;
+        request.staticClientAddress = null;
+        assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
+
+        request = makeTetheringRequestParcel();
+        request.exemptFromEntitlementCheck = true;
+        assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
+
+        request = makeTetheringRequestParcel();
+        request.showProvisioningUi = false;
+        assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
+
+        MiscAssertsKt.assertFieldCountEquals(5, TetheringRequestParcel.class);
+    }
+}
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index 3a1d4a6..0a7850b 100644
--- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -21,7 +21,7 @@
 import static android.net.TetheringManager.TETHERING_WIFI;
 import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED;
+import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
@@ -284,11 +284,11 @@
         assertEquals(0, mEnMgr.uiProvisionCount);
         mEnMgr.reset();
         // 3. No cache value and ui entitlement check is needed.
-        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
         receiver = new ResultReceiver(null) {
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
-                assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode);
+                assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode);
                 mCallbacklatch.countDown();
             }
         };
@@ -297,12 +297,13 @@
         callbackTimeoutHelper(mCallbacklatch);
         assertEquals(1, mEnMgr.uiProvisionCount);
         mEnMgr.reset();
-        // 4. Cache value is TETHER_ERROR_PROVISION_FAILED and don't need to run entitlement check.
+        // 4. Cache value is TETHER_ERROR_PROVISIONING_FAILED and don't need to run entitlement
+        // check.
         mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
         receiver = new ResultReceiver(null) {
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
-                assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode);
+                assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode);
                 mCallbacklatch.countDown();
             }
         };
@@ -311,7 +312,7 @@
         callbackTimeoutHelper(mCallbacklatch);
         assertEquals(0, mEnMgr.uiProvisionCount);
         mEnMgr.reset();
-        // 5. Cache value is TETHER_ERROR_PROVISION_FAILED and ui entitlement check is needed.
+        // 5. Cache value is TETHER_ERROR_PROVISIONING_FAILED and ui entitlement check is needed.
         mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
         receiver = new ResultReceiver(null) {
             @Override
@@ -364,7 +365,7 @@
     public void verifyPermissionResult() {
         setupForRequiredProvisioning();
         mEnMgr.notifyUpstream(true);
-        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
         mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
         mLooper.dispatchAll();
         assertFalse(mEnMgr.isCellularUpstreamPermitted());
@@ -380,15 +381,15 @@
     public void verifyPermissionIfAllNotApproved() {
         setupForRequiredProvisioning();
         mEnMgr.notifyUpstream(true);
-        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
         mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
         mLooper.dispatchAll();
         assertFalse(mEnMgr.isCellularUpstreamPermitted());
-        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
         mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
         mLooper.dispatchAll();
         assertFalse(mEnMgr.isCellularUpstreamPermitted());
-        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
         mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true);
         mLooper.dispatchAll();
         assertFalse(mEnMgr.isCellularUpstreamPermitted());
@@ -403,7 +404,7 @@
         mLooper.dispatchAll();
         assertTrue(mEnMgr.isCellularUpstreamPermitted());
         mLooper.dispatchAll();
-        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
         mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
         mLooper.dispatchAll();
         assertTrue(mEnMgr.isCellularUpstreamPermitted());
@@ -465,7 +466,7 @@
         assertEquals(0, mEnMgr.silentProvisionCount);
         mEnMgr.reset();
         // 6. switch upstream back to mobile again
-        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
         mEnMgr.notifyUpstream(true);
         mLooper.dispatchAll();
         assertEquals(0, mEnMgr.uiProvisionCount);
@@ -477,7 +478,7 @@
     public void testCallStopTetheringWhenUiProvisioningFail() {
         setupForRequiredProvisioning();
         verify(mEntitlementFailedListener, times(0)).onUiEntitlementFailed(TETHERING_WIFI);
-        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
         mEnMgr.notifyUpstream(true);
         mLooper.dispatchAll();
         mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index 820c852..60d7ad1 100644
--- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -82,6 +82,7 @@
 import android.net.ConnectivityManager;
 import android.net.EthernetManager;
 import android.net.EthernetManager.TetheredInterfaceRequest;
+import android.net.IIntResultListener;
 import android.net.INetd;
 import android.net.ITetheringEventCallback;
 import android.net.InetAddresses;
@@ -499,10 +500,16 @@
         return new Tethering(mTetheringDependencies);
     }
 
-    private TetheringRequestParcel createTetheringRquestParcel(final int type) {
+    private TetheringRequestParcel createTetheringRequestParcel(final int type) {
+        return createTetheringRequestParcel(type, null, null);
+    }
+
+    private TetheringRequestParcel createTetheringRequestParcel(final int type,
+            final LinkAddress serverAddr, final LinkAddress clientAddr) {
         final TetheringRequestParcel request = new TetheringRequestParcel();
         request.tetheringType = type;
-        request.localIPv4Address = null;
+        request.localIPv4Address = serverAddr;
+        request.staticClientAddress = clientAddr;
         request.exemptFromEntitlementCheck = false;
         request.showProvisioningUi = false;
 
@@ -616,7 +623,7 @@
 
     private void prepareNcmTethering() {
         // Emulate startTethering(TETHERING_NCM) called
-        mTethering.startTethering(createTetheringRquestParcel(TETHERING_NCM), null);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), null);
         mLooper.dispatchAll();
         verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM);
 
@@ -629,7 +636,7 @@
                 .thenReturn(upstreamState);
 
         // Emulate pressing the USB tethering button in Settings UI.
-        mTethering.startTethering(createTetheringRquestParcel(TETHERING_USB), null);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), null);
         mLooper.dispatchAll();
         verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
 
@@ -903,7 +910,7 @@
         when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
 
         // Emulate pressing the WiFi tethering button.
-        mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
         mLooper.dispatchAll();
         verify(mWifiManager, times(1)).startTetheredHotspot(null);
         verifyNoMoreInteractions(mWifiManager);
@@ -931,7 +938,7 @@
         when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
 
         // Emulate pressing the WiFi tethering button.
-        mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
         mLooper.dispatchAll();
         verify(mWifiManager, times(1)).startTetheredHotspot(null);
         verifyNoMoreInteractions(mWifiManager);
@@ -1008,7 +1015,7 @@
         doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME);
 
         // Emulate pressing the WiFi tethering button.
-        mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
         mLooper.dispatchAll();
         verify(mWifiManager, times(1)).startTetheredHotspot(null);
         verifyNoMoreInteractions(mWifiManager);
@@ -1303,7 +1310,7 @@
         tetherState = callback.pollTetherStatesChanged();
         assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME});
 
-        mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
         sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
         mLooper.dispatchAll();
         tetherState = callback.pollTetherStatesChanged();
@@ -1398,10 +1405,10 @@
     public void testNoDuplicatedEthernetRequest() throws Exception {
         final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class);
         when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest);
-        mTethering.startTethering(createTetheringRquestParcel(TETHERING_ETHERNET), null);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
         mLooper.dispatchAll();
         verify(mEm, times(1)).requestTetheredInterface(any(), any());
-        mTethering.startTethering(createTetheringRquestParcel(TETHERING_ETHERNET), null);
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
         mLooper.dispatchAll();
         verifyNoMoreInteractions(mEm);
         mTethering.stopTethering(TETHERING_ETHERNET);
@@ -1580,6 +1587,86 @@
         assertTrue(element + " not found in " + collection, collection.contains(element));
     }
 
+    private class ResultListener extends IIntResultListener.Stub {
+        private final int mExpectedResult;
+        private boolean mHasResult = false;
+        ResultListener(final int expectedResult) {
+            mExpectedResult = expectedResult;
+        }
+
+        @Override
+        public void onResult(final int resultCode) {
+            mHasResult = true;
+            if (resultCode != mExpectedResult) {
+                fail("expected result: " + mExpectedResult + " but actual result: " + resultCode);
+            }
+        }
+
+        public void assertHasResult() {
+            if (!mHasResult) fail("No callback result");
+        }
+    }
+
+    @Test
+    public void testMultipleStartTethering() throws Exception {
+        final LinkAddress serverLinkAddr = new LinkAddress("192.168.20.1/24");
+        final LinkAddress clientLinkAddr = new LinkAddress("192.168.20.42/24");
+        final String serverAddr = "192.168.20.1";
+        final ResultListener firstResult = new ResultListener(TETHER_ERROR_NO_ERROR);
+        final ResultListener secondResult = new ResultListener(TETHER_ERROR_NO_ERROR);
+        final ResultListener thirdResult = new ResultListener(TETHER_ERROR_NO_ERROR);
+
+        // Enable USB tethering and check that Tethering starts USB.
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
+                  null, null), firstResult);
+        mLooper.dispatchAll();
+        firstResult.assertHasResult();
+        verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
+        verifyNoMoreInteractions(mUsbManager);
+
+        // Enable USB tethering again with the same request and expect no change to USB.
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
+                  null, null), secondResult);
+        mLooper.dispatchAll();
+        secondResult.assertHasResult();
+        verify(mUsbManager, never()).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+        reset(mUsbManager);
+
+        // Enable USB tethering with a different request and expect that USB is stopped and
+        // started.
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
+                  serverLinkAddr, clientLinkAddr), thirdResult);
+        mLooper.dispatchAll();
+        thirdResult.assertHasResult();
+        verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+        verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
+
+        // Expect that when USB comes up, the DHCP server is configured with the requested address.
+        mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
+        sendUsbBroadcast(true, true, true, TETHERING_USB);
+        mLooper.dispatchAll();
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+                any(), any());
+        verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr)));
+    }
+
+    @Test
+    public void testRequestStaticServerIp() throws Exception {
+        final LinkAddress serverLinkAddr = new LinkAddress("192.168.20.1/24");
+        final LinkAddress clientLinkAddr = new LinkAddress("192.168.20.42/24");
+        final String serverAddr = "192.168.20.1";
+        mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
+                  serverLinkAddr, clientLinkAddr), null);
+        mLooper.dispatchAll();
+        verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
+        mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
+        sendUsbBroadcast(true, true, true, TETHERING_USB);
+        mLooper.dispatchAll();
+        verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr)));
+
+        // TODO: test static client address.
+    }
+
     // TODO: Test that a request for hotspot mode doesn't interfere with an
     // already operating tethering mode interface.
 }
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index 5ed75bf..7c98f62 100644
--- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -212,8 +212,8 @@
         mUNM.updateMobileRequiresDun(true);
         mUNM.registerMobileNetworkRequest();
         verify(mCM, times(1)).requestNetwork(
-                any(NetworkRequest.class), any(NetworkCallback.class), anyInt(), anyInt(),
-                any(Handler.class));
+                any(NetworkRequest.class), anyInt(), anyInt(), any(Handler.class),
+                any(NetworkCallback.class));
 
         assertTrue(mUNM.mobileNetworkRequested());
         assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
@@ -649,8 +649,8 @@
         }
 
         @Override
-        public void requestNetwork(NetworkRequest req, NetworkCallback cb,
-                int timeoutMs, int legacyType, Handler h) {
+        public void requestNetwork(NetworkRequest req,
+                int timeoutMs, int legacyType, Handler h, NetworkCallback cb) {
             assertFalse(allCallbacks.containsKey(cb));
             allCallbacks.put(cb, h);
             assertFalse(requested.containsKey(cb));