Merge "Add product apk support from libnativeloader"
diff --git a/Android.bp b/Android.bp
index b099bab..85ce41b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -202,6 +202,7 @@
         "core/java/android/net/INetworkStatsService.aidl",
         "core/java/android/net/INetworkStatsSession.aidl",
         "core/java/android/net/ITestNetworkManager.aidl",
+        "core/java/android/net/ITetheringEventCallback.aidl",
         "core/java/android/net/ITetheringStatsProvider.aidl",
         "core/java/android/net/nsd/INsdManager.aidl",
         "core/java/android/nfc/IAppCallback.aidl",
@@ -558,6 +559,7 @@
         "telephony/java/com/android/internal/telephony/IOns.aidl",
         "telephony/java/com/android/internal/telephony/ITelephony.aidl",
         "telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl",
+        "telephony/java/com/android/internal/telephony/IUpdateAvailableNetworksCallback.aidl",
         "telephony/java/com/android/internal/telephony/IWapPushManager.aidl",
         "telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl",
         "telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl",
diff --git a/api/current.txt b/api/current.txt
index 55f885c..482a221 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -27723,7 +27723,7 @@
     method public abstract boolean isRelative();
     method public android.net.Uri normalizeScheme();
     method public static android.net.Uri parse(String);
-    method public String toSafeString();
+    method @NonNull public String toSafeString();
     method public abstract String toString();
     method public static android.net.Uri withAppendedPath(android.net.Uri, String);
     method public static void writeToParcel(android.os.Parcel, android.net.Uri);
@@ -37321,13 +37321,13 @@
   }
 
   public static final class Telephony.CarrierId implements android.provider.BaseColumns {
-    method @NonNull public static android.net.Uri getPreciseCarrierIdUriForSubscriptionId(int);
+    method @NonNull public static android.net.Uri getSpecificCarrierIdUriForSubscriptionId(int);
     method public static android.net.Uri getUriForSubscriptionId(int);
     field public static final String CARRIER_ID = "carrier_id";
     field public static final String CARRIER_NAME = "carrier_name";
     field public static final android.net.Uri CONTENT_URI;
-    field public static final String PRECISE_CARRIER_ID = "precise_carrier_id";
-    field public static final String PRECISE_CARRIER_ID_NAME = "precise_carrier_id_name";
+    field public static final String SPECIFIC_CARRIER_ID = "specific_carrier_id";
+    field public static final String SPECIFIC_CARRIER_ID_NAME = "specific_carrier_id_name";
   }
 
   public static final class Telephony.Carriers implements android.provider.BaseColumns {
@@ -39449,7 +39449,7 @@
     method @Nullable public String getImsi();
     method public String getMcc();
     method public String getMnc();
-    method public int getPreciseCarrierId();
+    method public int getSpecificCarrierId();
     method @Nullable public String getSpn();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.carrier.CarrierIdentifier> CREATOR;
@@ -43069,9 +43069,9 @@
     method public String getSimCountryIso();
     method public String getSimOperator();
     method public String getSimOperatorName();
-    method public int getSimPreciseCarrierId();
-    method @Nullable public CharSequence getSimPreciseCarrierIdName();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getSimSerialNumber();
+    method public int getSimSpecificCarrierId();
+    method @Nullable public CharSequence getSimSpecificCarrierIdName();
     method public int getSimState();
     method public int getSimState(int);
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getSubscriberId();
@@ -43123,7 +43123,7 @@
     method @Deprecated public void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
     method @Deprecated public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void switchMultiSimConfig(int);
-    method public boolean updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>);
+    method public void updateAvailableNetworks(@NonNull java.util.List<android.telephony.AvailableNetworkInfo>, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
     field public static final String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
     field public static final String ACTION_NETWORK_COUNTRY_CHANGED = "android.telephony.action.NETWORK_COUNTRY_CHANGED";
     field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
@@ -43131,7 +43131,7 @@
     field public static final String ACTION_SECRET_CODE = "android.telephony.action.SECRET_CODE";
     field public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
     field public static final String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED";
-    field public static final String ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED";
+    field public static final String ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED";
     field public static final int APPTYPE_CSIM = 4; // 0x4
     field public static final int APPTYPE_ISIM = 5; // 0x5
     field public static final int APPTYPE_RUIM = 3; // 0x3
@@ -43166,8 +43166,8 @@
     field public static final String EXTRA_NETWORK_COUNTRY = "android.telephony.extra.NETWORK_COUNTRY";
     field public static final String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
     field public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telephony.extra.PHONE_ACCOUNT_HANDLE";
-    field public static final String EXTRA_PRECISE_CARRIER_ID = "android.telephony.extra.PRECISE_CARRIER_ID";
-    field public static final String EXTRA_PRECISE_CARRIER_NAME = "android.telephony.extra.PRECISE_CARRIER_NAME";
+    field public static final String EXTRA_SPECIFIC_CARRIER_ID = "android.telephony.extra.SPECIFIC_CARRIER_ID";
+    field public static final String EXTRA_SPECIFIC_CARRIER_NAME = "android.telephony.extra.SPECIFIC_CARRIER_NAME";
     field public static final String EXTRA_STATE = "state";
     field public static final String EXTRA_STATE_IDLE;
     field public static final String EXTRA_STATE_OFFHOOK;
@@ -43215,6 +43215,11 @@
     field public static final int UNINITIALIZED_CARD_ID = -2; // 0xfffffffe
     field public static final int UNKNOWN_CARRIER_ID = -1; // 0xffffffff
     field public static final int UNSUPPORTED_CARD_ID = -1; // 0xffffffff
+    field public static final int UPDATE_AVAILABLE_NETWORKS_ABORTED = 2; // 0x2
+    field public static final int UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS = 3; // 0x3
+    field public static final int UPDATE_AVAILABLE_NETWORKS_NO_CARRIER_PRIVILEGE = 4; // 0x4
+    field public static final int UPDATE_AVAILABLE_NETWORKS_SUCCESS = 0; // 0x0
+    field public static final int UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE = 1; // 0x1
     field public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; // 0xfffffffe
     field public static final int USSD_RETURN_FAILURE = -1; // 0xffffffff
     field public static final String VVM_TYPE_CVVM = "vvm_type_cvvm";
diff --git a/api/system-current.txt b/api/system-current.txt
index 24df5f1..b89a1da 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3100,11 +3100,13 @@
     method @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS) public String getCaptivePortalServerUrl();
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
+    method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
     method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean);
     method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.net.Network, android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
+    method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
     field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
     field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
     field public static final int TETHERING_BLUETOOTH = 2; // 0x2
@@ -3125,6 +3127,11 @@
     method public void onEntitlementResult(int);
   }
 
+  public abstract static class ConnectivityManager.OnTetheringEventCallback {
+    ctor public ConnectivityManager.OnTetheringEventCallback();
+    method public void onUpstreamChanged(@Nullable android.net.Network);
+  }
+
   public final class IpPrefix implements android.os.Parcelable {
     ctor public IpPrefix(java.net.InetAddress, int);
     ctor public IpPrefix(String);
@@ -3186,6 +3193,7 @@
     method public int[] getTransportTypes();
     method public boolean satisfiedByNetworkCapabilities(android.net.NetworkCapabilities);
     field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
+    field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
   }
 
   public class NetworkKey implements android.os.Parcelable {
@@ -3334,10 +3342,12 @@
     ctor public CaptivePortalProbeResult(int, String, String);
     ctor public CaptivePortalProbeResult(int, String, String, android.net.captiveportal.CaptivePortalProbeSpec);
     method public boolean isFailed();
+    method public boolean isPartialConnectivity();
     method public boolean isPortal();
     method public boolean isSuccessful();
     field public static final android.net.captiveportal.CaptivePortalProbeResult FAILED;
     field public static final int FAILED_CODE = 599; // 0x257
+    field public static final android.net.captiveportal.CaptivePortalProbeResult PARTIAL;
     field public static final int PORTAL_CODE = 302; // 0x12e
     field public static final android.net.captiveportal.CaptivePortalProbeResult SUCCESS;
     field public static final int SUCCESS_CODE = 204; // 0xcc
@@ -3469,6 +3479,7 @@
     field public static final int NETWORK_FIRST_VALIDATION_PORTAL_FOUND = 10; // 0xa
     field public static final int NETWORK_FIRST_VALIDATION_SUCCESS = 8; // 0x8
     field public static final int NETWORK_LINGER = 5; // 0x5
+    field public static final int NETWORK_PARTIAL_CONNECTIVITY = 13; // 0xd
     field public static final int NETWORK_REVALIDATION_PORTAL_FOUND = 11; // 0xb
     field public static final int NETWORK_REVALIDATION_SUCCESS = 9; // 0x9
     field public static final int NETWORK_UNLINGER = 6; // 0x6
diff --git a/api/test-current.txt b/api/test-current.txt
index 0550663..d43b1c4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -751,10 +751,12 @@
     ctor public CaptivePortalProbeResult(int, String, String);
     ctor public CaptivePortalProbeResult(int, String, String, android.net.captiveportal.CaptivePortalProbeSpec);
     method public boolean isFailed();
+    method public boolean isPartialConnectivity();
     method public boolean isPortal();
     method public boolean isSuccessful();
     field public static final android.net.captiveportal.CaptivePortalProbeResult FAILED;
     field public static final int FAILED_CODE = 599; // 0x257
+    field public static final android.net.captiveportal.CaptivePortalProbeResult PARTIAL;
     field public static final int PORTAL_CODE = 302; // 0x12e
     field public static final android.net.captiveportal.CaptivePortalProbeResult SUCCESS;
     field public static final int SUCCESS_CODE = 204; // 0xcc
@@ -886,6 +888,7 @@
     field public static final int NETWORK_FIRST_VALIDATION_PORTAL_FOUND = 10; // 0xa
     field public static final int NETWORK_FIRST_VALIDATION_SUCCESS = 8; // 0x8
     field public static final int NETWORK_LINGER = 5; // 0x5
+    field public static final int NETWORK_PARTIAL_CONNECTIVITY = 13; // 0xd
     field public static final int NETWORK_REVALIDATION_PORTAL_FOUND = 11; // 0xb
     field public static final int NETWORK_REVALIDATION_SUCCESS = 9; // 0x9
     field public static final int NETWORK_UNLINGER = 6; // 0x6
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 524077b..91fefa2 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -56,6 +56,7 @@
 import android.util.Log;
 import android.util.SparseIntArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.Preconditions;
@@ -426,6 +427,16 @@
             "android.net.conn.PROMPT_LOST_VALIDATION";
 
     /**
+     * Action used to display a dialog that asks the user whether to stay connected to a network
+     * that has not validated. This intent is used to start the dialog in settings via
+     * startActivity.
+     *
+     * @hide
+     */
+    public static final String ACTION_PROMPT_PARTIAL_CONNECTIVITY =
+            "android.net.conn.PROMPT_PARTIAL_CONNECTIVITY";
+
+    /**
      * Invalid tethering type.
      * @see #startTethering(int, boolean, OnStartTetheringCallback)
      * @hide
@@ -2542,6 +2553,94 @@
     }
 
     /**
+     * Callback for use with {@link registerTetheringEventCallback} to find out tethering
+     * upstream status.
+     *
+     *@hide
+     */
+    @SystemApi
+    public abstract static class OnTetheringEventCallback {
+
+        /**
+         * Called when tethering upstream changed. This can be called multiple times and can be
+         * called any time.
+         *
+         * @param network the {@link Network} of tethering upstream. Null means tethering doesn't
+         * have any upstream.
+         */
+        public void onUpstreamChanged(@Nullable Network network) {}
+    }
+
+    @GuardedBy("mTetheringEventCallbacks")
+    private final ArrayMap<OnTetheringEventCallback, ITetheringEventCallback>
+            mTetheringEventCallbacks = new ArrayMap<>();
+
+    /**
+     * Start listening to tethering change events. Any new added callback will receive the last
+     * tethering status right away. If callback is registered when tethering loses its upstream or
+     * disabled, {@link OnTetheringEventCallback#onUpstreamChanged} will immediately be called
+     * with a null argument. The same callback object cannot be registered twice.
+     *
+     * @param executor the executor on which callback will be invoked.
+     * @param callback the callback to be called when tethering has change events.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+    public void registerTetheringEventCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull final OnTetheringEventCallback callback) {
+        Preconditions.checkNotNull(callback, "OnTetheringEventCallback cannot be null.");
+
+        synchronized (mTetheringEventCallbacks) {
+            Preconditions.checkArgument(!mTetheringEventCallbacks.containsKey(callback),
+                    "callback was already registered.");
+            ITetheringEventCallback remoteCallback = new ITetheringEventCallback.Stub() {
+                @Override
+                public void onUpstreamChanged(Network network) throws RemoteException {
+                    Binder.withCleanCallingIdentity(() ->
+                            executor.execute(() -> {
+                                callback.onUpstreamChanged(network);
+                            }));
+                }
+            };
+            try {
+                String pkgName = mContext.getOpPackageName();
+                Log.i(TAG, "registerTetheringUpstreamCallback:" + pkgName);
+                mService.registerTetheringEventCallback(remoteCallback, pkgName);
+                mTetheringEventCallbacks.put(callback, remoteCallback);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Remove tethering event callback previously registered with
+     * {@link #registerTetheringEventCallback}.
+     *
+     * @param callback previously registered callback.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+    public void unregisterTetheringEventCallback(
+            @NonNull final OnTetheringEventCallback callback) {
+        synchronized (mTetheringEventCallbacks) {
+            ITetheringEventCallback remoteCallback = mTetheringEventCallbacks.remove(callback);
+            Preconditions.checkNotNull(remoteCallback, "callback was not registered.");
+            try {
+                String pkgName = mContext.getOpPackageName();
+                Log.i(TAG, "unregisterTetheringEventCallback:" + pkgName);
+                mService.unregisterTetheringEventCallback(remoteCallback, pkgName);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+
+    /**
      * Get the list of regular expressions that define any tetherable
      * USB network interfaces.  If USB tethering is not supported by the
      * device, this list should be empty.
@@ -3929,7 +4028,7 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
         try {
             mService.setAcceptUnvalidated(network, accept, always);
@@ -3939,6 +4038,29 @@
     }
 
     /**
+     * Informs the system whether it should consider the network as validated even if it only has
+     * partial connectivity. If {@code accept} is true, then the network will be considered as
+     * validated even if connectivity is only partial. If {@code always} is true, then the choice
+     * is remembered, so that the next time the user connects to this network, the system will
+     * switch to it.
+     *
+     * @param network The network to accept.
+     * @param accept Whether to consider the network as validated even if it has partial
+     *               connectivity.
+     * @param always Whether to remember this choice in the future.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
+    public void setAcceptPartialConnectivity(Network network, boolean accept, boolean always) {
+        try {
+            mService.setAcceptPartialConnectivity(network, accept, always);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Informs the system to penalize {@code network}'s score when it becomes unvalidated. This is
      * only meaningful if the system is configured not to penalize such networks, e.g., if the
      * {@code config_networkAvoidBadWifi} configuration variable is set to 0 and the {@code
@@ -3948,7 +4070,7 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public void setAvoidUnvalidated(Network network) {
         try {
             mService.setAvoidUnvalidated(network);
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index ad903d9..2df4e75 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -19,6 +19,7 @@
 import android.app.PendingIntent;
 import android.net.ConnectionInfo;
 import android.net.LinkProperties;
+import android.net.ITetheringEventCallback;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
@@ -175,6 +176,7 @@
     void releaseNetworkRequest(in NetworkRequest networkRequest);
 
     void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
+    void setAcceptPartialConnectivity(in Network network, boolean accept, boolean always);
     void setAvoidUnvalidated(in Network network);
     void startCaptivePortalApp(in Network network);
     void startCaptivePortalAppInternal(in Network network, in Bundle appExtras);
@@ -214,4 +216,7 @@
 
     void getLatestTetheringEntitlementResult(int type, in ResultReceiver receiver,
             boolean showEntitlementUi, String callerPkg);
+
+    void registerTetheringEventCallback(ITetheringEventCallback callback, String callerPkg);
+    void unregisterTetheringEventCallback(ITetheringEventCallback callback, String callerPkg);
 }
diff --git a/core/java/android/net/INetworkMonitor.aidl b/core/java/android/net/INetworkMonitor.aidl
index c94cdde..5d1ab98 100644
--- a/core/java/android/net/INetworkMonitor.aidl
+++ b/core/java/android/net/INetworkMonitor.aidl
@@ -32,9 +32,16 @@
     // 3. a broken network (e.g. DNS failed, connect failed, HTTP request failed).
     const int NETWORK_TEST_RESULT_INVALID = 1;
 
+    // After a network has been tested, this result can be sent with EVENT_NETWORK_TESTED.
+    // The network may be used as a default internet connection, but it was found to be a partial
+    // connectivity network which can get the pass result for http probe but get the failed result
+    // for https probe.
+    const int NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY = 2;
+
     void start();
     void launchCaptivePortalApp();
     void notifyCaptivePortalAppFinished(int response);
+    void notifyAcceptPartialConnectivity();
     void forceReevaluation(int uid);
     void notifyPrivateDnsChanged(in PrivateDnsConfigParcel config);
     void notifyDnsResponse(int returnCode);
diff --git a/core/java/android/net/ITetheringEventCallback.aidl b/core/java/android/net/ITetheringEventCallback.aidl
new file mode 100644
index 0000000..d502088
--- /dev/null
+++ b/core/java/android/net/ITetheringEventCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.Network;
+
+/**
+ * Callback class for receiving tethering changed events
+ * @hide
+ */
+oneway interface ITetheringEventCallback
+{
+    void onUpstreamChanged(in Network network);
+}
diff --git a/core/java/android/net/IpMemoryStore.java b/core/java/android/net/IpMemoryStore.java
index b35f097..2f4d9bc 100644
--- a/core/java/android/net/IpMemoryStore.java
+++ b/core/java/android/net/IpMemoryStore.java
@@ -171,4 +171,9 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /** Gets an instance of the memory store */
+    public static IpMemoryStore getMemoryStore(final Context context) {
+        return (IpMemoryStore) context.getSystemService(Context.IP_MEMORY_STORE_SERVICE);
+    }
 }
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 0a63e75..e1cfe99 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -143,7 +143,8 @@
             NET_CAPABILITY_NOT_CONGESTED,
             NET_CAPABILITY_NOT_SUSPENDED,
             NET_CAPABILITY_OEM_PAID,
-            NET_CAPABILITY_MCX
+            NET_CAPABILITY_MCX,
+            NET_CAPABILITY_PARTIAL_CONNECTIVITY,
     })
     public @interface NetCapability { }
 
@@ -304,8 +305,15 @@
      */
     public static final int NET_CAPABILITY_MCX = 23;
 
+    /**
+     * Indicates that this network was tested to only provide partial connectivity.
+     * @hide
+     */
+    @SystemApi
+    public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24;
+
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
-    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_MCX;
+    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 
     /**
      * Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -320,7 +328,8 @@
             | (1 << NET_CAPABILITY_NOT_ROAMING)
             | (1 << NET_CAPABILITY_FOREGROUND)
             | (1 << NET_CAPABILITY_NOT_CONGESTED)
-            | (1 << NET_CAPABILITY_NOT_SUSPENDED);
+            | (1 << NET_CAPABILITY_NOT_SUSPENDED)
+            | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY);
 
     /**
      * Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -375,6 +384,15 @@
             (1 << NET_CAPABILITY_WIFI_P2P);
 
     /**
+     * Capabilities that are managed by ConnectivityService.
+     */
+    private static final long CONNECTIVITY_MANAGED_CAPABILITIES =
+            (1 << NET_CAPABILITY_VALIDATED)
+            | (1 << NET_CAPABILITY_CAPTIVE_PORTAL)
+            | (1 << NET_CAPABILITY_FOREGROUND)
+            | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY);
+
+    /**
      * Adds the given capability to this {@code NetworkCapability} instance.
      * Multiple capabilities may be applied sequentially.  Note that when searching
      * for a network to satisfy a request, all capabilities requested must be satisfied.
@@ -507,6 +525,14 @@
                 && ((mUnwantedNetworkCapabilities & (1 << capability)) != 0);
     }
 
+    /**
+     * Check if this NetworkCapabilities has system managed capabilities or not.
+     * @hide
+     */
+    public boolean hasConnectivityManagedCapability() {
+        return ((mNetworkCapabilities & CONNECTIVITY_MANAGED_CAPABILITIES) != 0);
+    }
+
     /** Note this method may result in having the same capability in wanted and unwanted lists. */
     private void combineNetCapabilities(NetworkCapabilities nc) {
         this.mNetworkCapabilities |= nc.mNetworkCapabilities;
@@ -1599,31 +1625,32 @@
      */
     public static String capabilityNameOf(@NetCapability int capability) {
         switch (capability) {
-            case NET_CAPABILITY_MMS:            return "MMS";
-            case NET_CAPABILITY_SUPL:           return "SUPL";
-            case NET_CAPABILITY_DUN:            return "DUN";
-            case NET_CAPABILITY_FOTA:           return "FOTA";
-            case NET_CAPABILITY_IMS:            return "IMS";
-            case NET_CAPABILITY_CBS:            return "CBS";
-            case NET_CAPABILITY_WIFI_P2P:       return "WIFI_P2P";
-            case NET_CAPABILITY_IA:             return "IA";
-            case NET_CAPABILITY_RCS:            return "RCS";
-            case NET_CAPABILITY_XCAP:           return "XCAP";
-            case NET_CAPABILITY_EIMS:           return "EIMS";
-            case NET_CAPABILITY_NOT_METERED:    return "NOT_METERED";
-            case NET_CAPABILITY_INTERNET:       return "INTERNET";
-            case NET_CAPABILITY_NOT_RESTRICTED: return "NOT_RESTRICTED";
-            case NET_CAPABILITY_TRUSTED:        return "TRUSTED";
-            case NET_CAPABILITY_NOT_VPN:        return "NOT_VPN";
-            case NET_CAPABILITY_VALIDATED:      return "VALIDATED";
-            case NET_CAPABILITY_CAPTIVE_PORTAL: return "CAPTIVE_PORTAL";
-            case NET_CAPABILITY_NOT_ROAMING:    return "NOT_ROAMING";
-            case NET_CAPABILITY_FOREGROUND:     return "FOREGROUND";
-            case NET_CAPABILITY_NOT_CONGESTED:  return "NOT_CONGESTED";
-            case NET_CAPABILITY_NOT_SUSPENDED:  return "NOT_SUSPENDED";
-            case NET_CAPABILITY_OEM_PAID:       return "OEM_PAID";
-            case NET_CAPABILITY_MCX:            return "MCX";
-            default:                            return Integer.toString(capability);
+            case NET_CAPABILITY_MMS:                  return "MMS";
+            case NET_CAPABILITY_SUPL:                 return "SUPL";
+            case NET_CAPABILITY_DUN:                  return "DUN";
+            case NET_CAPABILITY_FOTA:                 return "FOTA";
+            case NET_CAPABILITY_IMS:                  return "IMS";
+            case NET_CAPABILITY_CBS:                  return "CBS";
+            case NET_CAPABILITY_WIFI_P2P:             return "WIFI_P2P";
+            case NET_CAPABILITY_IA:                   return "IA";
+            case NET_CAPABILITY_RCS:                  return "RCS";
+            case NET_CAPABILITY_XCAP:                 return "XCAP";
+            case NET_CAPABILITY_EIMS:                 return "EIMS";
+            case NET_CAPABILITY_NOT_METERED:          return "NOT_METERED";
+            case NET_CAPABILITY_INTERNET:             return "INTERNET";
+            case NET_CAPABILITY_NOT_RESTRICTED:       return "NOT_RESTRICTED";
+            case NET_CAPABILITY_TRUSTED:              return "TRUSTED";
+            case NET_CAPABILITY_NOT_VPN:              return "NOT_VPN";
+            case NET_CAPABILITY_VALIDATED:            return "VALIDATED";
+            case NET_CAPABILITY_CAPTIVE_PORTAL:       return "CAPTIVE_PORTAL";
+            case NET_CAPABILITY_NOT_ROAMING:          return "NOT_ROAMING";
+            case NET_CAPABILITY_FOREGROUND:           return "FOREGROUND";
+            case NET_CAPABILITY_NOT_CONGESTED:        return "NOT_CONGESTED";
+            case NET_CAPABILITY_NOT_SUSPENDED:        return "NOT_SUSPENDED";
+            case NET_CAPABILITY_OEM_PAID:             return "OEM_PAID";
+            case NET_CAPABILITY_MCX:                  return "MCX";
+            case NET_CAPABILITY_PARTIAL_CONNECTIVITY: return "PARTIAL_CONNECTIVITY";
+            default:                                  return Integer.toString(capability);
         }
     }
 
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index c0487b5..6fb2390 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -52,6 +52,12 @@
     public boolean acceptUnvalidated;
 
     /**
+     * Whether the user explicitly set that this network should be validated even if presence of
+     * only partial internet connectivity.
+     */
+    public boolean acceptPartialConnectivity;
+
+    /**
      * Set to avoid surfacing the "Sign in to network" notification.
      * if carrier receivers/apps are registered to handle the carrier-specific provisioning
      * procedure, a carrier specific provisioning notification will be placed.
diff --git a/core/java/android/net/ParseException.java b/core/java/android/net/ParseException.java
index 68b209b..2380e86 100644
--- a/core/java/android/net/ParseException.java
+++ b/core/java/android/net/ParseException.java
@@ -24,6 +24,7 @@
     public String response;
 
     ParseException(String response) {
+        super(response);
         this.response = response;
     }
 }
diff --git a/core/java/android/net/TcpSocketKeepalive.java b/core/java/android/net/TcpSocketKeepalive.java
index 8f6ee7b..f691a0d 100644
--- a/core/java/android/net/TcpSocketKeepalive.java
+++ b/core/java/android/net/TcpSocketKeepalive.java
@@ -45,13 +45,14 @@
      * - The application must not write to or read from the socket after calling this method, until
      *   onDataReceived, onStopped, or onError are called. If it does, the keepalive will fail
      *   with {@link #ERROR_SOCKET_NOT_IDLE}, or {@code #ERROR_INVALID_SOCKET} if the socket
-     *   experienced an error (as in poll(2) returned POLLERR); if this happens, the data received
-     *   from the socket may be invalid, and the socket can't be recovered.
+     *   experienced an error (as in poll(2) returned POLLERR or POLLHUP); if this happens, the data
+     *   received from the socket may be invalid, and the socket can't be recovered.
      * - If the socket has data in the send or receive buffer, then this call will fail with
      *   {@link #ERROR_SOCKET_NOT_IDLE} and can be retried after the data has been processed.
-     *   An app could ensure this by using an application-layer protocol where it can receive
-     *   acknowledgement that it will go into keepalive mode. It could then go into keepalive
-     *   mode after having read the acknowledgement, draining the socket.
+     *   An app could ensure this by using an application-layer protocol to receive acknowledgement
+     *   that indicates all data has been delivered to server, e.g. HTTP 200 OK.
+     *   Then the app could go into keepalive mode after reading all remaining data within the
+     *   acknowledgement.
      */
     @Override
     void startImpl(int intervalSec) {
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 28b2707..9125cfb 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Intent;
@@ -380,6 +381,7 @@
      * returned as {@code http://example.com/...}.
      * @return the common forms PII redacted string of this URI
      */
+    @NonNull
     public String toSafeString() {
         String scheme = getScheme();
         String ssp = getSchemeSpecificPart();
diff --git a/core/java/android/net/captiveportal/CaptivePortalProbeResult.java b/core/java/android/net/captiveportal/CaptivePortalProbeResult.java
index 7432687..3930344 100644
--- a/core/java/android/net/captiveportal/CaptivePortalProbeResult.java
+++ b/core/java/android/net/captiveportal/CaptivePortalProbeResult.java
@@ -30,10 +30,20 @@
     public static final int SUCCESS_CODE = 204;
     public static final int FAILED_CODE = 599;
     public static final int PORTAL_CODE = 302;
+    // Set partial connectivity http response code to -1 to prevent conflict with the other http
+    // response codes. Besides the default http response code of probe result is set as 599 in
+    // NetworkMonitor#sendParallelHttpProbes(), so response code will be set as -1 only when
+    // NetworkMonitor detects partial connectivity.
+    /**
+     * @hide
+     */
+    public static final int PARTIAL_CODE = -1;
 
     public static final CaptivePortalProbeResult FAILED = new CaptivePortalProbeResult(FAILED_CODE);
     public static final CaptivePortalProbeResult SUCCESS =
             new CaptivePortalProbeResult(SUCCESS_CODE);
+    public static final CaptivePortalProbeResult PARTIAL =
+            new CaptivePortalProbeResult(PARTIAL_CODE);
 
     private final int mHttpResponseCode;  // HTTP response code returned from Internet probe.
     public final String redirectUrl;      // Redirect destination returned from Internet probe.
@@ -69,4 +79,8 @@
     public boolean isFailed() {
         return !isSuccessful() && !isPortal();
     }
+
+    public boolean isPartialConnectivity() {
+        return mHttpResponseCode == PARTIAL_CODE;
+    }
 }
diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java
index f5b2ff1..5128115 100644
--- a/core/java/android/net/metrics/NetworkEvent.java
+++ b/core/java/android/net/metrics/NetworkEvent.java
@@ -50,6 +50,8 @@
 
     public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12;
 
+    public static final int NETWORK_PARTIAL_CONNECTIVITY = 13;
+
     /** @hide */
     @IntDef(value = {
             NETWORK_CONNECTED,
@@ -64,6 +66,7 @@
             NETWORK_FIRST_VALIDATION_PORTAL_FOUND,
             NETWORK_REVALIDATION_PORTAL_FOUND,
             NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND,
+            NETWORK_PARTIAL_CONNECTIVITY,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType {}
diff --git a/core/java/android/service/carrier/CarrierIdentifier.java b/core/java/android/service/carrier/CarrierIdentifier.java
index 8c73a87..3b71aa7 100644
--- a/core/java/android/service/carrier/CarrierIdentifier.java
+++ b/core/java/android/service/carrier/CarrierIdentifier.java
@@ -55,7 +55,7 @@
     private @Nullable String mGid1;
     private @Nullable String mGid2;
     private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
-    private int mPreciseCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+    private int mSpecificCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
 
     public CarrierIdentifier(String mcc, String mnc, @Nullable String spn, @Nullable String imsi,
             @Nullable String gid1, @Nullable String gid2) {
@@ -72,12 +72,12 @@
      * @param gid2 group id level 2
      * @param carrierid carrier unique identifier {@link TelephonyManager#getSimCarrierId()}, used
      *                  to uniquely identify the carrier and look up the carrier configurations.
-     * @param preciseCarrierId precise carrier identifier
-     * {@link TelephonyManager#getSimPreciseCarrierId()}
+     * @param specificCarrierId specific carrier identifier
+     * {@link TelephonyManager#getSimSpecificCarrierId()}
      */
     public CarrierIdentifier(@NonNull String mcc, @NonNull String mnc, @Nullable String spn,
                              @Nullable String imsi, @Nullable String gid1, @Nullable String gid2,
-                             int carrierid, int preciseCarrierId) {
+                             int carrierid, int specificCarrierId) {
         mMcc = mcc;
         mMnc = mnc;
         mSpn = spn;
@@ -85,7 +85,7 @@
         mGid1 = gid1;
         mGid2 = gid2;
         mCarrierId = carrierid;
-        mPreciseCarrierId = preciseCarrierId;
+        mSpecificCarrierId = specificCarrierId;
     }
 
     /**
@@ -162,11 +162,17 @@
     }
 
     /**
-     * Returns the precise carrier id.
-     * @see TelephonyManager#getSimPreciseCarrierId()
+     * A specific carrier ID returns the fine-grained carrier ID of the current subscription.
+     * It can represent the fact that a carrier may be in effect an aggregation of other carriers
+     * (ie in an MVNO type scenario) where each of these specific carriers which are used to make
+     * up the actual carrier service may have different carrier configurations.
+     * A specific carrier ID could also be used, for example, in a scenario where a carrier requires
+     * different carrier configuration for different service offering such as a prepaid plan.
+     *
+     * @see TelephonyManager#getSimSpecificCarrierId()
      */
-    public int getPreciseCarrierId() {
-        return mPreciseCarrierId;
+    public int getSpecificCarrierId() {
+        return mSpecificCarrierId;
     }
 
     @Override
@@ -186,12 +192,12 @@
                 && Objects.equals(mGid1, that.mGid1)
                 && Objects.equals(mGid2, that.mGid2)
                 && Objects.equals(mCarrierId, that.mCarrierId)
-                && Objects.equals(mPreciseCarrierId, that.mPreciseCarrierId);
+                && Objects.equals(mSpecificCarrierId, that.mSpecificCarrierId);
     }
 
     @Override
     public int hashCode(){
-        return Objects.hash(mMcc, mMnc, mSpn, mImsi, mGid1, mGid2, mCarrierId, mPreciseCarrierId);
+        return Objects.hash(mMcc, mMnc, mSpn, mImsi, mGid1, mGid2, mCarrierId, mSpecificCarrierId);
     }
 
     @Override
@@ -208,7 +214,7 @@
         out.writeString(mGid1);
         out.writeString(mGid2);
         out.writeInt(mCarrierId);
-        out.writeInt(mPreciseCarrierId);
+        out.writeInt(mSpecificCarrierId);
     }
 
     @Override
@@ -221,7 +227,7 @@
               + ",gid1=" + mGid1
               + ",gid2=" + mGid2
               + ",carrierid=" + mCarrierId
-              + ",mPreciseCarrierId=" + mPreciseCarrierId
+              + ",specificCarrierId=" + mSpecificCarrierId
               + "}";
     }
 
@@ -234,7 +240,7 @@
         mGid1 = in.readString();
         mGid2 = in.readString();
         mCarrierId = in.readInt();
-        mPreciseCarrierId = in.readInt();
+        mSpecificCarrierId = in.readInt();
     }
 
     /** @hide */
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b412f0f..b4e69ad 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3197,13 +3197,18 @@
     <string name="network_available_sign_in_detailed"><xliff:g id="network_ssid">%1$s</xliff:g></string>
 
     <!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's title. -->
-    <string name="wifi_no_internet">Wi-Fi has no internet access</string>
+    <string name="wifi_no_internet"><xliff:g id="network_ssid" example="GoogleGuest">%1$s</xliff:g> has no internet access</string>
 
     <!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. -->
     <string name="wifi_no_internet_detailed">Tap for options</string>
 
     <!-- A notification is shown after the user logs in to a captive portal network, to indicate that the network should now have internet connectivity. This is the message of notification. [CHAR LIMIT=50] -->
     <string name="captive_portal_logged_in_detailed">Connected</string>
+    <!-- A notification is shown when the user connects to a network that doesn't have access to some services (e.g. Push notifications may not work). This is the notification's title. [CHAR LIMIT=50] -->
+    <string name="network_partial_connectivity"><xliff:g id="network_ssid" example="GoogleGuest">%1$s</xliff:g> has limited connectivity</string>
+
+    <!-- A notification is shown when the user connects to a network that doesn't have access to some services (e.g. Push notifications may not work). This is the notification's message. [CHAR LIMIT=50] -->
+    <string name="network_partial_connectivity_detailed">Tap to connect anyway</string>
 
     <!-- A notification is shown when the user's softap config has been changed due to underlying
          hardware restrictions. This is the notifications's title.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4689caa..a95fa54 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1211,6 +1211,8 @@
   <java-symbol type="string" name="mediasize_japanese_kahu" />
   <java-symbol type="string" name="mediasize_japanese_kaku2" />
   <java-symbol type="string" name="mediasize_japanese_you4" />
+  <java-symbol type="string" name="network_partial_connectivity" />
+  <java-symbol type="string" name="network_partial_connectivity_detailed" />
   <java-symbol type="string" name="reason_service_unavailable" />
   <java-symbol type="string" name="reason_unknown" />
   <java-symbol type="string" name="restr_pin_enter_admin_pin" />
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index f210840..190247a 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -14,12 +14,11 @@
 // limitations under the License.
 //
 
-// Library including the network stack, used to compile the network stack app, or linked into the
-// system server on devices that run the stack there
-java_library {
-    name: "NetworkStackLib",
+// Library including the network stack, used to compile both variants of the network stack
+android_library {
+    name: "NetworkStackBase",
     sdk_version: "system_current",
-    installable: true,
+    min_sdk_version: "28",
     srcs: [
         "src/**/*.java",
         ":framework-networkstack-shared-srcs",
@@ -29,7 +28,24 @@
         "netd_aidl_interface-java",
         "networkstack-aidl-interfaces-java",
         "datastallprotosnano",
-    ]
+    ],
+    manifest: "AndroidManifestBase.xml",
+}
+
+// Non-updatable in-process network stack for devices not using the module
+android_app {
+    name: "InProcessNetworkStack",
+    sdk_version: "system_current",
+    min_sdk_version: "28",
+    certificate: "platform",
+    privileged: true,
+    static_libs: [
+        "NetworkStackBase",
+    ],
+    jarjar_rules: "jarjar-rules-shared.txt",
+    // The permission configuration *must* be included to ensure security of the device
+    required: ["NetworkStackPermissionStub"],
+    manifest: "AndroidManifest_InProcess.xml",
 }
 
 // Updatable network stack packaged as an application
@@ -40,9 +56,10 @@
     certificate: "networkstack",
     privileged: true,
     static_libs: [
-        "NetworkStackLib"
+        "NetworkStackBase"
     ],
     jarjar_rules: "jarjar-rules-shared.txt",
-    manifest: "AndroidManifest.xml",
+    // The permission configuration *must* be included to ensure security of the device
     required: ["NetworkStackPermissionStub"],
+    manifest: "AndroidManifest.xml",
 }
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index 003f1e5..a90db11 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * 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.
@@ -18,30 +18,14 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.networkstack"
-          android:sharedUserId="android.uid.networkstack"
-          android:versionCode="11"
-          android:versionName="Q-initial">
-    <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
-    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+          android:sharedUserId="android.uid.networkstack">
     <!-- Signature permission defined in NetworkStackStub -->
     <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" />
-    <!-- Send latency broadcast as current user -->
-    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
-    <uses-permission android:name="android.permission.WAKE_LOCK" />
-    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
-    <application
-        android:label="NetworkStack"
-        android:defaultToDeviceProtectedStorage="true"
-        android:directBootAware="true"
-        android:usesCleartextTraffic="true">
+    <application>
         <service android:name="com.android.server.NetworkStackService">
             <intent-filter>
                 <action android:name="android.net.INetworkStackConnector"/>
             </intent-filter>
         </service>
     </application>
-</manifest>
+</manifest>
\ No newline at end of file
diff --git a/packages/NetworkStack/AndroidManifestBase.xml b/packages/NetworkStack/AndroidManifestBase.xml
new file mode 100644
index 0000000..621d30c
--- /dev/null
+++ b/packages/NetworkStack/AndroidManifestBase.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.networkstack"
+          android:versionCode="11"
+          android:versionName="Q-initial">
+    <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+    <!-- Send latency broadcast as current user -->
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+    <application
+        android:label="NetworkStack"
+        android:defaultToDeviceProtectedStorage="true"
+        android:directBootAware="true"
+        android:usesCleartextTraffic="true">
+    </application>
+</manifest>
diff --git a/packages/NetworkStack/AndroidManifest_InProcess.xml b/packages/NetworkStack/AndroidManifest_InProcess.xml
new file mode 100644
index 0000000..48fcecd
--- /dev/null
+++ b/packages/NetworkStack/AndroidManifest_InProcess.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.networkstack.inprocess"
+          android:sharedUserId="android.uid.system"
+          android:process="system">
+    <application>
+        <service android:name="com.android.server.NetworkStackService" android:process="system">
+            <intent-filter>
+                <action android:name="android.net.INetworkStackConnector.InProcess"/>
+            </intent-filter>
+        </service>
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index 90db207..72955bb 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -253,6 +253,12 @@
         }
 
         @Override
+        public void notifyAcceptPartialConnectivity() {
+            checkNetworkStackCallingPermission();
+            mNm.notifyAcceptPartialConnectivity();
+        }
+
+        @Override
         public void forceReevaluation(int uid) {
             checkNetworkStackCallingPermission();
             mNm.forceReevaluation(uid);
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index c3447fd..fc56da7 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.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
@@ -227,6 +228,12 @@
      */
     public static final int EVENT_DNS_NOTIFICATION = 17;
 
+    /**
+     * ConnectivityService notifies NetworkMonitor that the user accepts partial connectivity and
+     * NetworkMonitor should ignore the https probe.
+     */
+    public static final int EVENT_ACCEPT_PARTIAL_CONNECTIVITY = 18;
+
     // Start mReevaluateDelayMs at this value and double.
     private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
     private static final int MAX_REEVALUATE_DELAY_MS = 10 * 60 * 1000;
@@ -379,6 +386,14 @@
     }
 
     /**
+     * ConnectivityService notifies NetworkMonitor that the user accepts partial connectivity and
+     * NetworkMonitor should ignore the https probe.
+     */
+    public void notifyAcceptPartialConnectivity() {
+        sendMessage(EVENT_ACCEPT_PARTIAL_CONNECTIVITY);
+    }
+
+    /**
      * Request the NetworkMonitor to reevaluate the network.
      */
     public void forceReevaluation(int responsibleUid) {
@@ -636,6 +651,10 @@
                 case EVENT_DNS_NOTIFICATION:
                     mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1);
                     break;
+                case EVENT_ACCEPT_PARTIAL_CONNECTIVITY:
+                    mUseHttps = false;
+                    transitionTo(mEvaluatingPrivateDnsState);
+                    break;
                 default:
                     break;
             }
@@ -1058,6 +1077,11 @@
                         notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, probeResult.redirectUrl);
                         mLastPortalProbeResult = probeResult;
                         transitionTo(mCaptivePortalState);
+                    } else if (probeResult.isPartialConnectivity()) {
+                        logNetworkEvent(NetworkEvent.NETWORK_PARTIAL_CONNECTIVITY);
+                        notifyNetworkTested(NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY,
+                                probeResult.redirectUrl);
+                        transitionTo(mWaitingForNextProbeState);
                     } else {
                         logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
                         notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, probeResult.redirectUrl);
@@ -1065,7 +1089,8 @@
                     }
                     return HANDLED;
                 case EVENT_DNS_NOTIFICATION:
-                    // Leave the event to DefaultState to record correct dns timestamp.
+                case EVENT_ACCEPT_PARTIAL_CONNECTIVITY:
+                    // Leave the event to DefaultState.
                     return NOT_HANDLED;
                 default:
                     // Wait for probe result and defer events to next state by default.
@@ -1504,10 +1529,11 @@
         // If we have new-style probe specs, use those. Otherwise, use the fallback URLs.
         final CaptivePortalProbeSpec probeSpec = nextFallbackSpec();
         final URL fallbackUrl = (probeSpec != null) ? probeSpec.getUrl() : nextFallbackUrl();
+        CaptivePortalProbeResult fallbackProbeResult = null;
         if (fallbackUrl != null) {
-            CaptivePortalProbeResult result = sendHttpProbe(fallbackUrl, PROBE_FALLBACK, probeSpec);
-            if (result.isPortal()) {
-                return result;
+            fallbackProbeResult = sendHttpProbe(fallbackUrl, PROBE_FALLBACK, probeSpec);
+            if (fallbackProbeResult.isPortal()) {
+                return fallbackProbeResult;
             }
         }
         // Otherwise wait until http and https probes completes and use their results.
@@ -1517,6 +1543,12 @@
                 return httpProbe.result();
             }
             httpsProbe.join();
+            final boolean isHttpSuccessful =
+                    (httpProbe.result().isSuccessful()
+                    || (fallbackProbeResult != null && fallbackProbeResult.isSuccessful()));
+            if (httpsProbe.result().isFailed() && isHttpSuccessful) {
+                return CaptivePortalProbeResult.PARTIAL;
+            }
             return httpsProbe.result();
         } catch (InterruptedException e) {
             validationLog("Error: http or https probe wait interrupted!");
diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/Android.bp
index e64f284..aadf99e 100644
--- a/packages/NetworkStack/tests/Android.bp
+++ b/packages/NetworkStack/tests/Android.bp
@@ -23,7 +23,7 @@
     static_libs: [
         "androidx.test.rules",
         "mockito-target-extended-minus-junit4",
-        "NetworkStackLib",
+        "NetworkStackBase",
         "testables",
     ],
     libs: [
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index 34ca6ac..d93aef2 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -18,6 +18,7 @@
 
 import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
 import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
+import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
 import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.provider.Settings.Global.DATA_STALL_EVALUATION_TYPE_DNS;
@@ -572,6 +573,34 @@
                 stats.build());
     }
 
+    @Test
+    public void testIgnoreHttpsProbe() throws Exception {
+        setSslException(mHttpsConnection);
+        setStatus(mHttpConnection, 204);
+
+        final NetworkMonitor nm = makeMonitor();
+        nm.notifyNetworkConnected();
+        verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
+                .notifyNetworkTested(NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY, null);
+
+        nm.notifyAcceptPartialConnectivity();
+        verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
+                .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null);
+    }
+
+    @Test
+    public void testIsPartialConnectivity() throws IOException {
+        setStatus(mHttpsConnection, 500);
+        setStatus(mHttpConnection, 204);
+        setStatus(mFallbackConnection, 500);
+        assertPartialConnectivity(makeMonitor().isCaptivePortal());
+
+        setStatus(mHttpsConnection, 500);
+        setStatus(mHttpConnection, 500);
+        setStatus(mFallbackConnection, 204);
+        assertPartialConnectivity(makeMonitor().isCaptivePortal());
+    }
+
     private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
         for (int i = 0; i < count; i++) {
             wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
@@ -649,6 +678,10 @@
         assertFalse(result.isSuccessful());
     }
 
+    private void assertPartialConnectivity(CaptivePortalProbeResult result) {
+        assertTrue(result.isPartialConnectivity());
+    }
+
     private void setSslException(HttpURLConnection connection) throws IOException {
         doThrow(new SSLHandshakeException("Invalid cert")).when(connection).getResponseCode();
     }
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 8ee55e1..b94600b 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -233,6 +233,8 @@
     NOTE_NETWORK_SWITCH = 743;
     // Device logged-in captive portal network successfully
     NOTE_NETWORK_LOGGED_IN = 744;
+    // A partial connectivity network was detected during network validation
+    NOTE_NETWORK_PARTIAL_CONNECTIVITY = 745;
 
     // Notify the user that their work profile has been deleted
     // Package: android
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 8390263..219cf33 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -25,6 +25,7 @@
 import static android.net.ConnectivityManager.TYPE_VPN;
 import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.ConnectivityManager.isNetworkTypeValid;
+import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
 import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
@@ -34,6 +35,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkPolicyManager.RULE_NONE;
@@ -71,6 +73,7 @@
 import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
+import android.net.ITetheringEventCallback;
 import android.net.InetAddresses;
 import android.net.IpPrefix;
 import android.net.LinkProperties;
@@ -488,6 +491,15 @@
     public static final int EVENT_TIMEOUT_NOTIFICATION = 44;
 
     /**
+     * Used to specify whether a network should be used even if connectivity is partial.
+     * arg1 = whether to accept the network if its connectivity is partial (1 for true or 0 for
+     * false)
+     * arg2 = whether to remember this choice in the future (1 for true or 0 for false)
+     * obj  = network
+     */
+    private static final int EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY = 45;
+
+    /**
      * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
      * should be shown.
      */
@@ -2486,9 +2498,7 @@
             switch (msg.what) {
                 case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
                     final NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
-                    if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL) ||
-                            networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED) ||
-                            networkCapabilities.hasCapability(NET_CAPABILITY_FOREGROUND)) {
+                    if (networkCapabilities.hasConnectivityManagedCapability()) {
                         Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
                     }
                     updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
@@ -2513,6 +2523,14 @@
                     }
                     nai.networkMisc.explicitlySelected = true;
                     nai.networkMisc.acceptUnvalidated = msg.arg1 == 1;
+                    // Mark the network as temporarily accepting partial connectivity so that it
+                    // will be validated (and possibly become default) even if it only provides
+                    // partial internet access. Note that if user connects to partial connectivity
+                    // and choose "don't ask again", then wifi disconnected by some reasons(maybe
+                    // out of wifi coverage) and if the same wifi is available again, the device
+                    // will auto connect to this wifi even though the wifi has "no internet".
+                    // TODO: Evaluate using a separate setting in IpMemoryStore.
+                    nai.networkMisc.acceptPartialConnectivity = msg.arg1 == 1;
                     break;
                 }
                 case NetworkAgent.EVENT_SOCKET_KEEPALIVE: {
@@ -2530,6 +2548,23 @@
                     final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
                     if (nai == null) break;
 
+                    final boolean partialConnectivity =
+                            (msg.arg1 == NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY)
+                                    // If user accepts partial connectivity network, NetworkMonitor
+                                    // will skip https probing. It will make partial connectivity
+                                    // network becomes valid. But user still need to know this
+                                    // network is limited. So, it's needed to refer to
+                                    // acceptPartialConnectivity to add
+                                    // NET_CAPABILITY_PARTIAL_CONNECTIVITY into NetworkCapabilities
+                                    // of this network. So that user can see "Limited connection"
+                                    // in the settings.
+                                    || (nai.networkMisc.acceptPartialConnectivity
+                                            && nai.partialConnectivity);
+                    // Once a network is determined to have partial connectivity, it cannot
+                    // go back to full connectivity without a disconnect.
+                    final boolean partialConnectivityChange =
+                            (partialConnectivity && !nai.partialConnectivity);
+
                     final boolean valid = (msg.arg1 == NETWORK_TEST_RESULT_VALID);
                     final boolean wasValidated = nai.lastValidated;
                     final boolean wasDefault = isDefaultNetwork(nai);
@@ -2538,6 +2573,17 @@
                         nai.captivePortalLoginNotified = true;
                         showNetworkNotification(nai, NotificationType.LOGGED_IN);
                     }
+                    // If this network has just connected and partial connectivity has just been
+                    // detected, tell NetworkMonitor if the user accepted partial connectivity on a
+                    // previous connect.
+                    if ((msg.arg1 == NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY)
+                            && nai.networkMisc.acceptPartialConnectivity) {
+                        try {
+                            nai.networkMonitor().notifyAcceptPartialConnectivity();
+                        } catch (RemoteException e) {
+                            e.rethrowFromSystemServer();
+                        }
+                    }
 
                     final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";
 
@@ -2567,6 +2613,9 @@
                             mNotifier.clearNotification(nai.network.netId,
                                     NotificationType.LOST_INTERNET);
                         }
+                    } else if (partialConnectivityChange) {
+                        nai.partialConnectivity = partialConnectivity;
+                        updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
                     }
                     updateInetCondition(nai);
                     // Let the NetworkAgent know the state of its network
@@ -2605,7 +2654,7 @@
                     }
                     if (!visible) {
                         // Only clear SIGN_IN and NETWORK_SWITCH notifications here, or else other
-                        // notifications belong to the same network may be cleared unexpected.
+                        // notifications belong to the same network may be cleared unexpectedly.
                         mNotifier.clearNotification(netId, NotificationType.SIGN_IN);
                         mNotifier.clearNotification(netId, NotificationType.NETWORK_SWITCH);
                     } else {
@@ -3191,14 +3240,21 @@
 
     @Override
     public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
-        enforceConnectivityInternalPermission();
+        enforceNetworkStackSettingsOrSetup();
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_ACCEPT_UNVALIDATED,
                 encodeBool(accept), encodeBool(always), network));
     }
 
     @Override
+    public void setAcceptPartialConnectivity(Network network, boolean accept, boolean always) {
+        enforceNetworkStackSettingsOrSetup();
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY,
+                encodeBool(accept), encodeBool(always), network));
+    }
+
+    @Override
     public void setAvoidUnvalidated(Network network) {
-        enforceConnectivityInternalPermission();
+        enforceNetworkStackSettingsOrSetup();
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_AVOID_UNVALIDATED, network));
     }
 
@@ -3224,6 +3280,10 @@
         if (accept != nai.networkMisc.acceptUnvalidated) {
             int oldScore = nai.getCurrentScore();
             nai.networkMisc.acceptUnvalidated = accept;
+            // If network becomes partial connectivity and user already accepted to use this
+            // network, we should respect the user's option and don't need to popup the
+            // PARTIAL_CONNECTIVITY notification to user again.
+            nai.networkMisc.acceptPartialConnectivity = accept;
             rematchAllNetworksAndRequests(nai, oldScore);
             sendUpdatedScoreToFactories(nai);
         }
@@ -3242,6 +3302,48 @@
 
     }
 
+    private void handleSetAcceptPartialConnectivity(Network network, boolean accept,
+            boolean always) {
+        if (DBG) {
+            log("handleSetAcceptPartialConnectivity network=" + network + " accept=" + accept
+                    + " always=" + always);
+        }
+
+        final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+        if (nai == null) {
+            // Nothing to do.
+            return;
+        }
+
+        if (nai.lastValidated) {
+            // The network validated while the dialog box was up. Take no action.
+            return;
+        }
+
+        if (accept != nai.networkMisc.acceptPartialConnectivity) {
+            nai.networkMisc.acceptPartialConnectivity = accept;
+        }
+
+        // TODO: Use the current design or save the user choice into IpMemoryStore.
+        if (always) {
+            nai.asyncChannel.sendMessage(
+                    NetworkAgent.CMD_SAVE_ACCEPT_UNVALIDATED, encodeBool(accept));
+        }
+
+        if (!accept) {
+            // Tell the NetworkAgent to not automatically reconnect to the network.
+            nai.asyncChannel.sendMessage(NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
+            // Tear down the network.
+            teardownUnneededNetwork(nai);
+        } else {
+            try {
+                nai.networkMonitor().notifyAcceptPartialConnectivity();
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        }
+    }
+
     private void handleSetAvoidUnvalidated(Network network) {
         NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
         if (nai == null || nai.lastValidated) {
@@ -3412,6 +3514,9 @@
             case LOST_INTERNET:
                 action = ConnectivityManager.ACTION_PROMPT_LOST_VALIDATION;
                 break;
+            case PARTIAL_CONNECTIVITY:
+                action = ConnectivityManager.ACTION_PROMPT_PARTIAL_CONNECTIVITY;
+                break;
             default:
                 Slog.wtf(TAG, "Unknown notification type " + type);
                 return;
@@ -3434,22 +3539,36 @@
         if (VDBG || DDBG) log("handlePromptUnvalidated " + network);
         NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
 
-        // Only prompt if the network is unvalidated and was explicitly selected by the user, and if
-        // we haven't already been told to switch to it regardless of whether it validated or not.
-        // Also don't prompt on captive portals because we're already prompting the user to sign in.
-        if (nai == null || nai.everValidated || nai.everCaptivePortalDetected ||
-                !nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated) {
+        // Only prompt if the network is unvalidated or network has partial internet connectivity
+        // and was explicitly selected by the user, and if we haven't already been told to switch
+        // to it regardless of whether it validated or not. Also don't prompt on captive portals
+        // because we're already prompting the user to sign in.
+        if (nai == null || nai.everValidated || nai.everCaptivePortalDetected
+                || !nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated
+                || nai.networkMisc.acceptPartialConnectivity) {
             return;
         }
-        showNetworkNotification(nai, NotificationType.NO_INTERNET);
+        // TODO: Evaluate if it's needed to wait 8 seconds for triggering notification when
+        // NetworkMonitor detects the network is partial connectivity. Need to change the design to
+        // popup the notification immediately when the network is partial connectivity.
+        if (nai.partialConnectivity) {
+            // Treat PARTIAL_CONNECTIVITY as NO_INTERNET temporary until Settings has been updated.
+            // TODO: Need to change back to PARTIAL_CONNECTIVITY when Settings part is merged.
+            showNetworkNotification(nai, NotificationType.NO_INTERNET);
+        } else {
+            showNetworkNotification(nai, NotificationType.NO_INTERNET);
+        }
     }
 
     private void handleNetworkUnvalidated(NetworkAgentInfo nai) {
         NetworkCapabilities nc = nai.networkCapabilities;
         if (DBG) log("handleNetworkUnvalidated " + nai.name() + " cap=" + nc);
 
-        if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
-            mMultinetworkPolicyTracker.shouldNotifyWifiUnvalidated()) {
+        if (!nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+            return;
+        }
+
+        if (mMultinetworkPolicyTracker.shouldNotifyWifiUnvalidated()) {
             showNetworkNotification(nai, NotificationType.LOST_INTERNET);
         }
     }
@@ -3540,6 +3659,12 @@
                     handleSetAcceptUnvalidated(network, toBool(msg.arg1), toBool(msg.arg2));
                     break;
                 }
+                case EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY: {
+                    Network network = (Network) msg.obj;
+                    handleSetAcceptPartialConnectivity(network, toBool(msg.arg1),
+                            toBool(msg.arg2));
+                    break;
+                }
                 case EVENT_SET_AVOID_UNVALIDATED: {
                     handleSetAvoidUnvalidated((Network) msg.obj);
                     break;
@@ -3764,6 +3889,22 @@
         mTethering.getLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
     }
 
+    /** Register tethering event callback. */
+    @Override
+    public void registerTetheringEventCallback(ITetheringEventCallback callback,
+            String callerPkg) {
+        ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
+        mTethering.registerTetheringEventCallback(callback);
+    }
+
+    /** Unregister tethering event callback. */
+    @Override
+    public void unregisterTetheringEventCallback(ITetheringEventCallback callback,
+            String callerPkg) {
+        ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
+        mTethering.unregisterTetheringEventCallback(callback);
+    }
+
     // Called when we lose the default network and have no replacement yet.
     // This will automatically be cleared after X seconds or a new default network
     // becomes CONNECTED, whichever happens first.  The timer is started by the
@@ -5448,6 +5589,11 @@
         } else {
             newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
         }
+        if (nai.partialConnectivity) {
+            newNc.addCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
+        } else {
+            newNc.removeCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
+        }
 
         return newNc;
     }
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 6cff57d..cc4c173 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -239,7 +239,12 @@
                                 .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket);
                         break;
                     case TYPE_TCP:
-                        mTcpController.startSocketMonitor(mFd, this, mSlot);
+                        try {
+                            mTcpController.startSocketMonitor(mFd, this, mSlot);
+                        } catch (InvalidSocketException e) {
+                            handleStopKeepalive(mNai, mSlot, ERROR_INVALID_SOCKET);
+                            return;
+                        }
                         mNai.asyncChannel
                                 .sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */,
                                         mPacket);
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index f11a0de..65eb158 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -156,6 +156,9 @@
     // last detected.
     public boolean captivePortalLoginNotified;
 
+    // Set to true when partial connectivity was detected.
+    public boolean partialConnectivity;
+
     // Networks are lingered when they become unneeded as a result of their NetworkRequests being
     // satisfied by a higher-scoring network. so as to allow communication to wrap up before the
     // network is taken down.  This usually only happens to the default network. Lingering ends with
@@ -592,6 +595,8 @@
         for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
     }
 
+    // TODO: Print shorter members first and only print the boolean variable which value is true
+    // to improve readability.
     public String toString() {
         return "NetworkAgentInfo{ ni{" + networkInfo + "}  "
                 + "network{" + network + "}  nethandle{" + network.getNetworkHandle() + "}  "
@@ -604,6 +609,8 @@
                 + "everCaptivePortalDetected{" + everCaptivePortalDetected + "} "
                 + "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} "
                 + "captivePortalLoginNotified{" + captivePortalLoginNotified + "} "
+                + "partialConnectivity{" + partialConnectivity + "} "
+                + "acceptPartialConnectivity{" + networkMisc.acceptPartialConnectivity + "} "
                 + "clat{" + clatd + "} "
                 + "}";
     }
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index b50477b..053da0d 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -47,8 +47,9 @@
         LOST_INTERNET(SystemMessage.NOTE_NETWORK_LOST_INTERNET),
         NETWORK_SWITCH(SystemMessage.NOTE_NETWORK_SWITCH),
         NO_INTERNET(SystemMessage.NOTE_NETWORK_NO_INTERNET),
-        SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN),
-        LOGGED_IN(SystemMessage.NOTE_NETWORK_LOGGED_IN);
+        LOGGED_IN(SystemMessage.NOTE_NETWORK_LOGGED_IN),
+        PARTIAL_CONNECTIVITY(SystemMessage.NOTE_NETWORK_PARTIAL_CONNECTIVITY),
+        SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN);
 
         public final int eventId;
 
@@ -169,11 +170,18 @@
         CharSequence details;
         int icon = getIcon(transportType);
         if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
-            title = r.getString(R.string.wifi_no_internet, 0);
+            title = r.getString(R.string.wifi_no_internet,
+                    WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
             details = r.getString(R.string.wifi_no_internet_detailed);
+        } else if (notifyType == NotificationType.PARTIAL_CONNECTIVITY
+                && transportType == TRANSPORT_WIFI) {
+            title = r.getString(R.string.network_partial_connectivity,
+                    WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
+            details = r.getString(R.string.network_partial_connectivity_detailed);
         } else if (notifyType == NotificationType.LOST_INTERNET &&
                 transportType == TRANSPORT_WIFI) {
-            title = r.getString(R.string.wifi_no_internet, 0);
+            title = r.getString(R.string.wifi_no_internet,
+                    WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
             details = r.getString(R.string.wifi_no_internet_detailed);
         } else if (notifyType == NotificationType.SIGN_IN) {
             switch (transportType) {
@@ -316,6 +324,8 @@
         }
         switch (t) {
             case SIGN_IN:
+                return 5;
+            case PARTIAL_CONNECTIVITY:
                 return 4;
             case NO_INTERNET:
                 return 3;
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index 9789688..1ac09ad 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -70,9 +70,8 @@
     private static final long MAX_PAC_SIZE = 20 * 1000 * 1000;
 
     // Return values for #setCurrentProxyScriptUrl
-    enum ToSendOrNotToSendBroadcast {
-        DONT_SEND_BROADCAST, DO_SEND_BROADCAST
-    }
+    public static final boolean DONT_SEND_BROADCAST = false;
+    public static final boolean DO_SEND_BROADCAST = true;
 
     private String mCurrentPac;
     @GuardedBy("mProxyLock")
@@ -176,11 +175,11 @@
      * @param proxy Proxy information that is about to be broadcast.
      * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST
      */
-    synchronized ToSendOrNotToSendBroadcast setCurrentProxyScriptUrl(ProxyInfo proxy) {
+    synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
         if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
             if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
                 // Allow to send broadcast, nothing to do.
-                return ToSendOrNotToSendBroadcast.DO_SEND_BROADCAST;
+                return DO_SEND_BROADCAST;
             }
             mPacUrl = proxy.getPacFileUrl();
             mCurrentDelay = DELAY_1;
@@ -188,7 +187,7 @@
             mHasDownloaded = false;
             getAlarmManager().cancel(mPacRefreshIntent);
             bind();
-            return ToSendOrNotToSendBroadcast.DONT_SEND_BROADCAST;
+            return DONT_SEND_BROADCAST;
         } else {
             getAlarmManager().cancel(mPacRefreshIntent);
             synchronized (mProxyLock) {
@@ -204,7 +203,7 @@
                     }
                 }
             }
-            return ToSendOrNotToSendBroadcast.DO_SEND_BROADCAST;
+            return DO_SEND_BROADCAST;
         }
     }
 
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index a671287..e715890 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -208,8 +208,7 @@
     public void sendProxyBroadcast() {
         final ProxyInfo defaultProxy = getDefaultProxy();
         final ProxyInfo proxyInfo = null != defaultProxy ? defaultProxy : new ProxyInfo("", 0, "");
-        if (mPacManager.setCurrentProxyScriptUrl(proxyInfo)
-                == PacManager.ToSendOrNotToSendBroadcast.DONT_SEND_BROADCAST) {
+        if (mPacManager.setCurrentProxyScriptUrl(proxyInfo) == PacManager.DONT_SEND_BROADCAST) {
             return;
         }
         if (DBG) Slog.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
index 65de83b..3e21b5b 100644
--- a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
+++ b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
@@ -215,18 +215,20 @@
      * Start monitoring incoming packets.
      *
      * @param fd socket fd to monitor.
-     * @param messenger a callback to notify socket status.
+     * @param ki a {@link KeepaliveInfo} that tracks information about a socket keepalive.
      * @param slot keepalive slot.
      */
     public void startSocketMonitor(@NonNull final FileDescriptor fd,
-            @NonNull final KeepaliveInfo ki, final int slot) {
+            @NonNull final KeepaliveInfo ki, final int slot)
+            throws IllegalArgumentException, InvalidSocketException {
         synchronized (mListeners) {
             if (null != mListeners.get(slot)) {
                 throw new IllegalArgumentException("This slot is already taken");
             }
             for (int i = 0; i < mListeners.size(); ++i) {
                 if (fd.equals(mListeners.valueAt(i))) {
-                    throw new IllegalArgumentException("This fd is already registered");
+                    Log.e(TAG, "This fd is already registered.");
+                    throw new InvalidSocketException(ERROR_INVALID_SOCKET);
                 }
             }
             mFdHandlerQueue.addOnFileDescriptorEventListener(fd, FD_EVENTS, (readyFd, events) -> {
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 3b4b6f8..35704d40 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -62,6 +62,7 @@
 import android.hardware.usb.UsbManager;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
+import android.net.ITetheringEventCallback;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -82,6 +83,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Parcel;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
@@ -184,6 +186,9 @@
     private final VersionedBroadcastListener mDefaultSubscriptionChange;
     private final TetheringDependencies mDeps;
     private final EntitlementManager mEntitlementMgr;
+    private final Handler mHandler;
+    private final RemoteCallbackList<ITetheringEventCallback> mTetheringEventCallbacks =
+            new RemoteCallbackList<>();
 
     private volatile TetheringConfiguration mConfig;
     private InterfaceSet mCurrentUpstreamIfaceSet;
@@ -193,6 +198,7 @@
     private boolean mRndisEnabled;       // track the RNDIS function enabled state
     // True iff. WiFi tethering should be started when soft AP is ready.
     private boolean mWifiTetherRequested;
+    private Network mTetherUpstream;
 
     public Tethering(Context context, INetworkManagementService nmService,
             INetworkStatsService statsService, INetworkPolicyManager policyManager,
@@ -213,9 +219,9 @@
         mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps);
         mTetherMasterSM.start();
 
-        final Handler smHandler = mTetherMasterSM.getHandler();
-        mOffloadController = new OffloadController(smHandler,
-                mDeps.getOffloadHardwareInterface(smHandler, mLog),
+        mHandler = mTetherMasterSM.getHandler();
+        mOffloadController = new OffloadController(mHandler,
+                mDeps.getOffloadHardwareInterface(mHandler, mLog),
                 mContext.getContentResolver(), mNMService,
                 mLog);
         mUpstreamNetworkMonitor = deps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
@@ -227,7 +233,7 @@
         mEntitlementMgr = mDeps.getEntitlementManager(mContext, mTetherMasterSM,
                 mLog, systemProperties);
         mCarrierConfigChange = new VersionedBroadcastListener(
-                "CarrierConfigChangeListener", mContext, smHandler, filter,
+                "CarrierConfigChangeListener", mContext, mHandler, filter,
                 (Intent ignored) -> {
                     mLog.log("OBSERVED carrier config change");
                     updateConfiguration();
@@ -237,7 +243,7 @@
         filter = new IntentFilter();
         filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
         mDefaultSubscriptionChange = new VersionedBroadcastListener(
-                "DefaultSubscriptionChangeListener", mContext, smHandler, filter,
+                "DefaultSubscriptionChangeListener", mContext, mHandler, filter,
                 (Intent ignored) -> {
                     mLog.log("OBSERVED default data subscription change");
                     updateConfiguration();
@@ -248,14 +254,13 @@
         // Load tethering configuration.
         updateConfiguration();
 
-        startStateMachineUpdaters();
+        startStateMachineUpdaters(mHandler);
     }
 
-    private void startStateMachineUpdaters() {
+    private void startStateMachineUpdaters(Handler handler) {
         mCarrierConfigChange.startListening();
         mDefaultSubscriptionChange.startListening();
 
-        final Handler handler = mTetherMasterSM.getHandler();
         IntentFilter filter = new IntentFilter();
         filter.addAction(UsbManager.ACTION_USB_STATE);
         filter.addAction(CONNECTIVITY_ACTION);
@@ -1229,8 +1234,13 @@
                     sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
                 }
             }
-            mUpstreamNetworkMonitor.setCurrentUpstream((ns != null) ? ns.network : null);
             setUpstreamNetwork(ns);
+            final Network newUpstream = (ns != null) ? ns.network : null;
+            if (mTetherUpstream != newUpstream) {
+                mTetherUpstream = newUpstream;
+                mUpstreamNetworkMonitor.setCurrentUpstream(mTetherUpstream);
+                reportUpstreamChanged(mTetherUpstream);
+            }
         }
 
         protected void setUpstreamNetwork(NetworkState ns) {
@@ -1413,6 +1423,10 @@
                 mUpstreamNetworkMonitor.stop();
                 notifyDownstreamsOfNewUpstreamIface(null);
                 handleNewUpstreamNetworkState(null);
+                if (mTetherUpstream != null) {
+                    mTetherUpstream = null;
+                    reportUpstreamChanged(null);
+                }
             }
 
             private boolean updateUpstreamWanted() {
@@ -1684,6 +1698,40 @@
         }
     }
 
+    /** Register tethering event callback */
+    public void registerTetheringEventCallback(ITetheringEventCallback callback) {
+        mHandler.post(() -> {
+            try {
+                callback.onUpstreamChanged(mTetherUpstream);
+            } catch (RemoteException e) {
+                // Not really very much to do here.
+            }
+            mTetheringEventCallbacks.register(callback);
+        });
+    }
+
+    /** Unregister tethering event callback */
+    public void unregisterTetheringEventCallback(ITetheringEventCallback callback) {
+        mHandler.post(() -> {
+            mTetheringEventCallbacks.unregister(callback);
+        });
+    }
+
+    private void reportUpstreamChanged(Network network) {
+        final int length = mTetheringEventCallbacks.beginBroadcast();
+        try {
+            for (int i = 0; i < length; i++) {
+                try {
+                    mTetheringEventCallbacks.getBroadcastItem(i).onUpstreamChanged(network);
+                } catch (RemoteException e) {
+                    // Not really very much to do here.
+                }
+            }
+        } finally {
+            mTetheringEventCallbacks.finishBroadcast();
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         // Binder.java closes the resource for us.
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d288910..d33d7f5 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1241,14 +1241,6 @@
             }
             traceEnd();
 
-            traceBeginAndSlog("StartNetworkStack");
-            try {
-                NetworkStackClient.getInstance().start(context);
-            } catch (Throwable e) {
-                reportWtf("starting Network Stack", e);
-            }
-            traceEnd();
-
             traceBeginAndSlog("StartNsdService");
             try {
                 serviceDiscovery = NsdService.create(context);
@@ -1931,6 +1923,14 @@
                     SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
             traceEnd();
 
+            traceBeginAndSlog("StartNetworkStack");
+            try {
+                NetworkStackClient.getInstance().start(context);
+            } catch (Throwable e) {
+                reportWtf("starting Network Stack", e);
+            }
+            traceEnd();
+
             traceBeginAndSlog("MakeLocationServiceReady");
             try {
                 if (locationF != null) locationF.systemRunning();
diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java
index eed01ae..a8f4a77 100644
--- a/services/net/java/android/net/NetworkStackClient.java
+++ b/services/net/java/android/net/NetworkStackClient.java
@@ -42,7 +42,6 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 
 /**
@@ -53,6 +52,7 @@
     private static final String TAG = NetworkStackClient.class.getSimpleName();
 
     private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
+    private static final String IN_PROCESS_SUFFIX = ".InProcess";
 
     private static NetworkStackClient sInstance;
 
@@ -175,42 +175,50 @@
     public void start(Context context) {
         log("Starting network stack");
         mNetworkStackStartRequested = true;
-        // Try to bind in-process if the library is available
-        IBinder connector = null;
-        try {
-            final Class service = Class.forName(
-                    "com.android.server.NetworkStackService",
-                    true /* initialize */,
-                    context.getClassLoader());
-            connector = (IBinder) service.getMethod("makeConnector", Context.class)
-                    .invoke(null, context);
-        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-            logWtf("Could not create network stack connector from NetworkStackService", e);
-            // TODO: crash/reboot system here ?
-            return;
-        } catch (ClassNotFoundException e) {
-            // Normal behavior if stack is provided by the app: fall through
+
+        final PackageManager pm = context.getPackageManager();
+
+        // Try to bind in-process if the device was shipped with an in-process version
+        Intent intent = getNetworkStackIntent(pm, true /* inSystemProcess */);
+
+        // Otherwise use the updatable module version
+        if (intent == null) {
+            intent = getNetworkStackIntent(pm, false /* inSystemProcess */);
+            log("Starting network stack process");
+        } else {
+            log("Starting network stack in-process");
         }
 
-        // In-process network stack. Add the service to the service manager here.
-        if (connector != null) {
-            log("Registering in-process network stack connector");
-            registerNetworkStackService(connector);
-            return;
-        }
-        // Start the network stack process. The service will be added to the service manager in
-        // NetworkStackConnection.onServiceConnected().
-        log("Starting network stack process");
-        final Intent intent = new Intent(INetworkStackConnector.class.getName());
-        final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
-        intent.setComponent(comp);
-
-        if (comp == null) {
-            logWtf("Could not resolve the network stack with " + intent, null);
+        if (intent == null) {
+            logWtf("Could not resolve the network stack", null);
             // TODO: crash/reboot system server ?
             return;
         }
-        final PackageManager pm = context.getPackageManager();
+
+        // Start the network stack. The service will be added to the service manager in
+        // NetworkStackConnection.onServiceConnected().
+        if (!context.bindServiceAsUser(intent, new NetworkStackConnection(),
+                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
+            logWtf("Could not bind to network stack with " + intent, null);
+            return;
+            // TODO: crash/reboot system server if no network stack after a timeout ?
+        }
+
+        log("Network stack service start requested");
+    }
+
+    @Nullable
+    private Intent getNetworkStackIntent(@NonNull PackageManager pm, boolean inSystemProcess) {
+        final String baseAction = INetworkStackConnector.class.getName();
+        final Intent intent =
+                new Intent(inSystemProcess ? baseAction + IN_PROCESS_SUFFIX : baseAction);
+        final ComponentName comp = intent.resolveSystemService(pm, 0);
+
+        if (comp == null) {
+            return null;
+        }
+        intent.setComponent(comp);
+
         int uid = -1;
         try {
             uid = pm.getPackageUidAsUser(comp.getPackageName(), UserHandle.USER_SYSTEM);
@@ -218,25 +226,27 @@
             logWtf("Network stack package not found", e);
             // Fall through
         }
-        if (uid != Process.NETWORK_STACK_UID) {
+
+        final int expectedUid = inSystemProcess ? Process.SYSTEM_UID : Process.NETWORK_STACK_UID;
+        if (uid != expectedUid) {
             throw new SecurityException("Invalid network stack UID: " + uid);
         }
 
+        if (!inSystemProcess) {
+            checkNetworkStackPermission(pm, comp);
+        }
+
+        return intent;
+    }
+
+    private void checkNetworkStackPermission(
+            @NonNull PackageManager pm, @NonNull ComponentName comp) {
         final int hasPermission =
                 pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName());
         if (hasPermission != PERMISSION_GRANTED) {
             throw new SecurityException(
                     "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK);
         }
-
-        if (!context.bindServiceAsUser(intent, new NetworkStackConnection(),
-                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
-            logWtf("Could not bind to network stack in-process, or in app with " + intent, null);
-            return;
-            // TODO: crash/reboot system server if no network stack after a timeout ?
-        }
-
-        log("Network stack service start requested");
     }
 
     private void log(@NonNull String message) {
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index d148ea1..9097430 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -28,6 +28,7 @@
         "services.usage",
         "guava",
         "androidx.test.rules",
+        "hamcrest-library",
         "mockito-target-minus-junit4",
         "platform-test-annotations",
         "ShortcutManagerTestUtils",
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 4939157..983d134 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -4309,24 +4309,24 @@
         }
 
         /**
-         * Generates a content {@link Uri} used to receive updates on precise carrier identity
+         * Generates a content {@link Uri} used to receive updates on specific carrier identity
          * change on the given subscriptionId returned by
-         * {@link TelephonyManager#getSimPreciseCarrierId()}.
-         * @see TelephonyManager#ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED
+         * {@link TelephonyManager#getSimSpecificCarrierId()}.
+         * @see TelephonyManager#ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED
          * <p>
          * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
-         * precise carrier identity {@link TelephonyManager#getSimPreciseCarrierId()}
+         * specific carrier identity {@link TelephonyManager#getSimSpecificCarrierId()}
          * while your app is running. You can also use a {@link JobService} to ensure your app
          * is notified of changes to the {@link Uri} even when it is not running.
          * Note, however, that using a {@link JobService} does not guarantee timely delivery of
          * updates to the {@link Uri}.
          *
          * @param subscriptionId the subscriptionId to receive updates on
-         * @return the Uri used to observe precise carrier identity changes
+         * @return the Uri used to observe specific carrier identity changes
          */
         @NonNull
-        public static Uri getPreciseCarrierIdUriForSubscriptionId(int subscriptionId) {
-            return Uri.withAppendedPath(Uri.withAppendedPath(CONTENT_URI, "precise"),
+        public static Uri getSpecificCarrierIdUriForSubscriptionId(int subscriptionId) {
+            return Uri.withAppendedPath(Uri.withAppendedPath(CONTENT_URI, "specific"),
                     String.valueOf(subscriptionId));
         }
 
@@ -4346,26 +4346,30 @@
 
         /**
          * A fine-grained carrier id.
-         * @see TelephonyManager#getSimPreciseCarrierId()
+         * The specific carrier ID would be used for configuration purposes, but apps wishing to
+         * know about the carrier itself should use the regular carrier ID returned by
+         * {@link TelephonyManager#getSimCarrierId()}.
+         *
+         * @see TelephonyManager#getSimSpecificCarrierId()
          * This is not a database column, only used to notify content observers for
-         * {@link #getPreciseCarrierIdUriForSubscriptionId(int)}
+         * {@link #getSpecificCarrierIdUriForSubscriptionId(int)}
          */
-        public static final String PRECISE_CARRIER_ID = "precise_carrier_id";
+        public static final String SPECIFIC_CARRIER_ID = "specific_carrier_id";
 
         /**
-         * A user facing carrier name for precise carrier id {@link #PRECISE_CARRIER_ID}.
-         * @see TelephonyManager#getSimPreciseCarrierIdName()
+         * A user facing carrier name for specific carrier id {@link #SPECIFIC_CARRIER_ID}.
+         * @see TelephonyManager#getSimSpecificCarrierIdName()
          * This is not a database column, only used to notify content observers for
-         * {@link #getPreciseCarrierIdUriForSubscriptionId(int)}
+         * {@link #getSpecificCarrierIdUriForSubscriptionId(int)}
          */
-        public static final String PRECISE_CARRIER_ID_NAME = "precise_carrier_id_name";
+        public static final String SPECIFIC_CARRIER_ID_NAME = "specific_carrier_id_name";
 
         /**
          * A unique parent carrier id. The parent-child
          * relationship can be used to further differentiate a single carrier by different networks,
-         * by prepaid v.s. postpaid or even by 4G v.s. 3G plan. It's an optional field.
-         * A carrier id with a valid parent_carrier_id is considered fine-grained carrier id, will
-         * not be returned as {@link #CARRIER_ID} but {@link #PRECISE_CARRIER_ID}.
+         * by prepaid v.s. postpaid. It's an optional field.
+         * A carrier id with a valid parent_carrier_id is considered fine-grained specific carrier
+         * ID, will not be returned as {@link #CARRIER_ID} but {@link #SPECIFIC_CARRIER_ID}.
          * <P>Type: INTEGER </P>
          * @hide
          */
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 9cdd53e..89c65a0 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -72,10 +72,10 @@
      * one is available for the slot index. An optional int extra
      * {@link TelephonyManager#EXTRA_CARRIER_ID} is included to indicate the carrier id for the
      * changed carrier configuration. An optional int extra
-     * {@link TelephonyManager#EXTRA_PRECISE_CARRIER_ID} is included to indicate the precise
+     * {@link TelephonyManager#EXTRA_SPECIFIC_CARRIER_ID} is included to indicate the precise
      * carrier id for the changed carrier configuration.
      * @see TelephonyManager#getSimCarrierId()
-     * @see TelephonyManager#getSimPreciseCarrierId()
+     * @see TelephonyManager#getSimSpecificCarrierId()
      */
     public static final String
             ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index a4207c9..e0f1690 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -151,6 +151,17 @@
     }
 
     /**
+     * Return the Received Signal Strength Indicator
+     *
+     * @return the RSSI in dBm (-113, -51) or
+     *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}.
+     * @hide
+     */
+    public int getRssi() {
+        return mRssi;
+    }
+
+    /**
      * Return the Bit Error Rate
      *
      * @return the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 17a4fd8..e6bf5a3 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2701,7 +2701,8 @@
      *  1) Even if it's active, it will be dormant most of the time. The modem will not try
      *     to scan or camp until it knows an available network is nearby to save power.
      *  2) Telephony relies on system app or carrier input to notify nearby available networks.
-     *     See {@link TelephonyManager#updateAvailableNetworks(List)} for more information.
+     *     See {@link TelephonyManager#updateAvailableNetworks(List, Executor, Consumer)}
+     *     for more information.
      *  3) In multi-SIM devices, when the network is nearby and camped, system may automatically
      *     switch internet data between it and default data subscription, based on carrier
      *     recommendation and its signal strength and metered-ness, etc.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 6e762b3..2772049 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -83,6 +83,7 @@
 import com.android.internal.telephony.IPhoneSubInfo;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.ITelephonyRegistry;
+import com.android.internal.telephony.IUpdateAvailableNetworksCallback;
 import com.android.internal.telephony.OperatorInfo;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RILConstants;
@@ -98,6 +99,7 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -1246,30 +1248,34 @@
     public static final String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME";
 
     /**
-     * Broadcast Action: The subscription precise carrier identity has changed.
-     * The precise carrier id can be used to further differentiate a carrier by different
-     * networks, by prepaid v.s.postpaid or even by 4G v.s.3G plan. Each carrier has a unique
-     * carrier id returned by {@link #getSimCarrierId()} but could have multiple precise carrier id.
-     * e.g, {@link #getSimCarrierId()} will always return Tracfone (id 2022) for a Tracfone SIM,
-     * while {@link #getSimPreciseCarrierId()} can return Tracfone AT&T or Tracfone T-Mobile based
-     * on the current subscription IMSI. For carriers without any fine-grained ids, precise carrier
-     * id is same as carrier id.
+     * Broadcast Action: The subscription specific carrier identity has changed.
+     *
+     * A specific carrier ID returns the fine-grained carrier ID of the current subscription.
+     * It can represent the fact that a carrier may be in effect an aggregation of other carriers
+     * (ie in an MVNO type scenario) where each of these specific carriers which are used to make
+     * up the actual carrier service may have different carrier configurations.
+     * A specific carrier ID could also be used, for example, in a scenario where a carrier requires
+     * different carrier configuration for different service offering such as a prepaid plan.
+     *
+     * the specific carrier ID would be used for configuration purposes, but apps wishing to know
+     * about the carrier itself should use the regular carrier ID returned by
+     * {@link #getSimCarrierId()}.
      *
      * <p>Similar like {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED}, this intent will be
      * sent on the event of {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} while its also
      * possible to be sent without {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} when
-     * precise carrier id changes with the same carrier id.
+     * specific carrier ID changes while carrier ID remains the same.
      * e.g, the same subscription switches to different IMSI could potentially change its
-     * precise carrier id while carrier id remains the same.
-     * @see #getSimPreciseCarrierId()
+     * specific carrier ID while carrier id remains the same.
+     * @see #getSimSpecificCarrierId()
      * @see #getSimCarrierId()
      *
      * The intent will have the following extra values:
      * <ul>
-     *   <li>{@link #EXTRA_PRECISE_CARRIER_ID} The up-to-date precise carrier id of the
+     *   <li>{@link #EXTRA_SPECIFIC_CARRIER_ID} The up-to-date specific carrier id of the
      *   current subscription.
      *   </li>
-     *   <li>{@link #EXTRA_PRECISE_CARRIER_NAME} The up-to-date name of the precise carrier id.
+     *   <li>{@link #EXTRA_SPECIFIC_CARRIER_NAME} The up-to-date name of the specific carrier id.
      *   </li>
      *   <li>{@link #EXTRA_SUBSCRIPTION_ID} The subscription id associated with the changed carrier
      *   identity.
@@ -1278,30 +1284,30 @@
      * <p class="note">This is a protected intent that can only be sent by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED =
-            "android.telephony.action.SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED";
+    public static final String ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED =
+            "android.telephony.action.SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED";
 
     /**
-     * An int extra used with {@link #ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED} which
-     * indicates the updated precise carrier id returned by
-     * {@link TelephonyManager#getSimPreciseCarrierId()}. Note, its possible precise carrier id
+     * An int extra used with {@link #ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED} which
+     * indicates the updated specific carrier id returned by
+     * {@link TelephonyManager#getSimSpecificCarrierId()}. Note, its possible specific carrier id
      * changes while {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} remains the same
      * e.g, when subscription switch to different IMSIs.
      * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
      * the carrier cannot be identified.
      */
-    public static final String EXTRA_PRECISE_CARRIER_ID =
-            "android.telephony.extra.PRECISE_CARRIER_ID";
+    public static final String EXTRA_SPECIFIC_CARRIER_ID =
+            "android.telephony.extra.SPECIFIC_CARRIER_ID";
 
     /**
-     * An string extra used with {@link #ACTION_SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED} which
-     * indicates the updated precise carrier name returned by
-     * {@link TelephonyManager#getSimPreciseCarrierIdName()}.
-     * <p>it's a user-facing name of the precise carrier id {@link #EXTRA_PRECISE_CARRIER_ID}, e.g,
-     * Tracfone-AT&T.
+     * An string extra used with {@link #ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED}
+     * which indicates the updated specific carrier name returned by
+     * {@link TelephonyManager#getSimSpecificCarrierIdName()}.
+     * <p>it's a user-facing name of the specific carrier id {@link #EXTRA_SPECIFIC_CARRIER_ID}
+     * e.g, Tracfone-AT&T
      */
-    public static final String EXTRA_PRECISE_CARRIER_NAME =
-            "android.telephony.extra.PRECISE_CARRIER_NAME";
+    public static final String EXTRA_SPECIFIC_CARRIER_NAME =
+            "android.telephony.extra.SPECIFIC_CARRIER_NAME";
 
     /**
      * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} to indicate the
@@ -8913,17 +8919,23 @@
     }
 
     /**
-     * Returns fine-grained carrier id of the current subscription.
+     * Returns fine-grained carrier ID of the current subscription.
      *
-     * <p>The precise carrier id can be used to further differentiate a carrier by different
-     * networks, by prepaid v.s.postpaid or even by 4G v.s.3G plan. Each carrier has a unique
-     * carrier id returned by {@link #getSimCarrierId()} but could have multiple precise carrier id.
-     * e.g, {@link #getSimCarrierId()} will always return Tracfone (id 2022) for a Tracfone SIM,
-     * while {@link #getSimPreciseCarrierId()} can return Tracfone AT&T or Tracfone T-Mobile based
-     * on the current subscription IMSI.
+     * A specific carrier ID can represent the fact that a carrier may be in effect an aggregation
+     * of other carriers (ie in an MVNO type scenario) where each of these specific carriers which
+     * are used to make up the actual carrier service may have different carrier configurations.
+     * A specific carrier ID could also be used, for example, in a scenario where a carrier requires
+     * different carrier configuration for different service offering such as a prepaid plan.
      *
-     * <p>For carriers without any fine-grained carrier ids, return {@link #getSimCarrierId()}
-     * <p>Precise carrier ids are defined in the same way as carrier id
+     * the specific carrier ID would be used for configuration purposes, but apps wishing to know
+     * about the carrier itself should use the regular carrier ID returned by
+     * {@link #getSimCarrierId()}.
+     *
+     * e.g, Tracfone SIMs could return different specific carrier ID based on IMSI from current
+     * subscription while carrier ID remains the same.
+     *
+     * <p>For carriers without fine-grained specific carrier ids, return {@link #getSimCarrierId()}
+     * <p>Specific carrier ids are defined in the same way as carrier id
      * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
      * except each with a "parent" id linking to its top-level carrier id.
      *
@@ -8931,11 +8943,11 @@
      * Return {@link #UNKNOWN_CARRIER_ID} if the subscription is unavailable or the carrier cannot
      * be identified.
      */
-    public int getSimPreciseCarrierId() {
+    public int getSimSpecificCarrierId() {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                return service.getSubscriptionPreciseCarrierId(getSubId());
+                return service.getSubscriptionSpecificCarrierId(getSubId());
             }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
@@ -8945,18 +8957,22 @@
 
     /**
      * Similar like {@link #getSimCarrierIdName()}, returns user-facing name of the
-     * precise carrier id returned by {@link #getSimPreciseCarrierId()}.
+     * specific carrier id returned by {@link #getSimSpecificCarrierId()}.
+     *
+     * The specific carrier ID would be used for configuration purposes, but apps wishing to know
+     * about the carrier itself should use the regular carrier ID returned by
+     * {@link #getSimCarrierIdName()}.
      *
      * <p>The returned name is unlocalized.
      *
-     * @return user-facing name of the subscription precise carrier id. Return {@code null} if the
+     * @return user-facing name of the subscription specific carrier id. Return {@code null} if the
      * subscription is unavailable or the carrier cannot be identified.
      */
-    public @Nullable CharSequence getSimPreciseCarrierIdName() {
+    public @Nullable CharSequence getSimSpecificCarrierIdName() {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                return service.getSubscriptionPreciseCarrierName(getSubId());
+                return service.getSubscriptionSpecificCarrierName(getSubId());
             }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
@@ -10132,6 +10148,41 @@
      */
     public static final int SET_OPPORTUNISTIC_SUB_INVALID_PARAMETER = 2;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"UPDATE_AVAILABLE_NETWORKS"}, value = {
+            UPDATE_AVAILABLE_NETWORKS_SUCCESS,
+            UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE,
+            UPDATE_AVAILABLE_NETWORKS_ABORTED,
+            UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS,
+            UPDATE_AVAILABLE_NETWORKS_NO_CARRIER_PRIVILEGE})
+    public @interface UpdateAvailableNetworksResult {}
+
+    /**
+     * No error. Operation succeeded.
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_SUCCESS = 0;
+
+    /**
+     * There is a unknown failure happened.
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE = 1;
+
+    /**
+     * The request is aborted.
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_ABORTED = 2;
+
+    /**
+     * The parameter passed in is invalid.
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS = 3;
+
+    /**
+     * No carrier privilege.
+     */
+    public static final int UPDATE_AVAILABLE_NETWORKS_NO_CARRIER_PRIVILEGE = 4;
+
     /**
      * Set preferred opportunistic data subscription id.
      *
@@ -10192,31 +10243,49 @@
     /**
      * Update availability of a list of networks in the current location.
      *
-     * This api should be called to inform OpportunisticNetwork Service about the availability
-     * of a network at the current location. This information will be used by OpportunisticNetwork
-     * service to decide to attach to the network opportunistically. If an empty list is passed,
-     * it is assumed that no network is available.
+     * This api should be called by opportunistic network selection app to inform
+     * OpportunisticNetwork Service about the availability of a network at the current location.
+     * This information will be used by OpportunisticNetwork service to decide to attach to the
+     * network opportunistically.
+     * If an empty list is passed, it is assumed that no network is available.
      * Requires that the calling app has carrier privileges on both primary and
      * secondary subscriptions (see {@link #hasCarrierPrivileges}), or has permission
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
      * @param availableNetworks is a list of available network information.
-     * @return true if request is accepted
+     * @param executor The executor of where the callback will execute.
+     * @param callback Callback will be triggered once it succeeds or failed.
      *
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
-    public boolean updateAvailableNetworks(List<AvailableNetworkInfo> availableNetworks) {
+    public void updateAvailableNetworks(@NonNull List<AvailableNetworkInfo> availableNetworks,
+            @Nullable @CallbackExecutor Executor executor,
+            @UpdateAvailableNetworksResult @Nullable Consumer<Integer> callback) {
         String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
-        boolean ret = false;
         try {
             IOns iOpportunisticNetworkService = getIOns();
-            if (iOpportunisticNetworkService != null && availableNetworks != null) {
-                ret = iOpportunisticNetworkService.updateAvailableNetworks(availableNetworks,
-                        pkgForDebug);
+            if (iOpportunisticNetworkService == null || availableNetworks == null) {
+                Binder.withCleanCallingIdentity(() -> executor.execute(() -> {
+                    callback.accept(UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
+                }));
+                return;
             }
+            IUpdateAvailableNetworksCallback callbackStub =
+                    new IUpdateAvailableNetworksCallback.Stub() {
+                        @Override
+                        public void onComplete(int result) {
+                            if (executor == null || callback == null) {
+                                return;
+                            }
+                            Binder.withCleanCallingIdentity(() -> executor.execute(() -> {
+                                callback.accept(result);
+                            }));
+                        }
+                    };
+            iOpportunisticNetworkService.updateAvailableNetworks(availableNetworks, callbackStub,
+                    pkgForDebug);
         } catch (RemoteException ex) {
             Rlog.e(TAG, "updateAvailableNetworks RemoteException", ex);
         }
-        return ret;
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/IOns.aidl b/telephony/java/com/android/internal/telephony/IOns.aidl
index 0e3d12b..4672e2d 100755
--- a/telephony/java/com/android/internal/telephony/IOns.aidl
+++ b/telephony/java/com/android/internal/telephony/IOns.aidl
@@ -17,6 +17,7 @@
 package com.android.internal.telephony;
 
 import android.telephony.AvailableNetworkInfo;
+import com.android.internal.telephony.IUpdateAvailableNetworksCallback;
 
 interface IOns {
 
@@ -93,9 +94,9 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
      *  @param availableNetworks is a list of available network information.
      *  @param callingPackage caller's package name
-     *  @return true if request is accepted
+     *  @param callback callback upon request completion.
      *
      */
-    boolean updateAvailableNetworks(in List<AvailableNetworkInfo> availableNetworks,
-            String callingPackage);
+    void updateAvailableNetworks(in List<AvailableNetworkInfo> availableNetworks,
+            IUpdateAvailableNetworksCallback callbackStub, String callingPackage);
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 6366b91..9e1332a 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1369,7 +1369,7 @@
     /**
      * Returns fine-grained carrier id of the current subscription.
      *
-     * <p>The precise carrier id can be used to further differentiate a carrier by different
+     * <p>The specific carrier id can be used to further differentiate a carrier by different
      * networks, by prepaid v.s.postpaid or even by 4G v.s.3G plan. Each carrier has a unique
      * carrier id {@link #getSimCarrierId()} but can have multiple precise carrier id. e.g,
      * {@link #getSimCarrierId()} will always return Tracfone (id 2022) for a Tracfone SIM, while
@@ -1383,19 +1383,19 @@
      * be identified.
      * @hide
      */
-    int getSubscriptionPreciseCarrierId(int subId);
+    int getSubscriptionSpecificCarrierId(int subId);
 
     /**
      * Similar like {@link #getSimCarrierIdName()}, returns user-facing name of the
-     * precise carrier id {@link #getSimPreciseCarrierId()}
+     * specific carrier id {@link #getSimSpecificCarrierId()}
      *
      * <p>The returned name is unlocalized.
      *
-     * @return user-facing name of the subscription precise carrier id. Return {@code null} if the
+     * @return user-facing name of the subscription specific carrier id. Return {@code null} if the
      * subscription is unavailable or the carrier cannot be identified.
      * @hide
      */
-    String getSubscriptionPreciseCarrierName(int subId);
+    String getSubscriptionSpecificCarrierName(int subId);
 
     /**
      * Returns carrier id based on MCCMNC only. This will return a MNO carrier id used for fallback
diff --git a/telephony/java/com/android/internal/telephony/IUpdateAvailableNetworksCallback.aidl b/telephony/java/com/android/internal/telephony/IUpdateAvailableNetworksCallback.aidl
new file mode 100644
index 0000000..ed77ff31
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IUpdateAvailableNetworksCallback.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+/**
+ * Callback to provide asynchronous result of updateAvailableNetworks.
+ * @hide
+ */
+oneway interface IUpdateAvailableNetworksCallback {
+    void onComplete(int result);
+}
\ No newline at end of file
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index 4d4915b..ad76388 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -20,6 +20,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
@@ -27,6 +28,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
 import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
@@ -335,6 +337,24 @@
     }
 
     @Test
+    public void testConnectivityManagedCapabilities() {
+        NetworkCapabilities nc = new NetworkCapabilities();
+        assertFalse(nc.hasConnectivityManagedCapability());
+        // Check every single system managed capability.
+        nc.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+        assertTrue(nc.hasConnectivityManagedCapability());
+        nc.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
+        nc.addCapability(NET_CAPABILITY_FOREGROUND);
+        assertTrue(nc.hasConnectivityManagedCapability());
+        nc.removeCapability(NET_CAPABILITY_FOREGROUND);
+        nc.addCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
+        assertTrue(nc.hasConnectivityManagedCapability());
+        nc.removeCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
+        nc.addCapability(NET_CAPABILITY_VALIDATED);
+        assertTrue(nc.hasConnectivityManagedCapability());
+    }
+
+    @Test
     public void testEqualsNetCapabilities() {
         NetworkCapabilities nc1 = new NetworkCapabilities();
         NetworkCapabilities nc2 = new NetworkCapabilities();
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 9453a80..70eb70b 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -28,6 +28,7 @@
 import static android.net.ConnectivityManager.TYPE_NONE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
+import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
 import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
@@ -43,6 +44,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
@@ -442,6 +444,11 @@
             mNmValidationRedirectUrl = redirectUrl;
         }
 
+        void setNetworkPartial() {
+            mNmValidationResult = NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY;
+            mNmValidationRedirectUrl = null;
+        }
+
         MockNetworkAgent(int transport) {
             this(transport, new LinkProperties());
         }
@@ -484,6 +491,7 @@
             try {
                 doAnswer(validateAnswer).when(mNetworkMonitor).notifyNetworkConnected();
                 doAnswer(validateAnswer).when(mNetworkMonitor).forceReevaluation(anyInt());
+                doAnswer(validateAnswer).when(mNetworkMonitor).notifyAcceptPartialConnectivity();
             } catch (RemoteException e) {
                 fail(e.getMessage());
             }
@@ -669,6 +677,11 @@
             connect(false);
         }
 
+        public void connectWithPartialConnectivity() {
+            setNetworkPartial();
+            connect(false);
+        }
+
         public void suspend() {
             mNetworkInfo.setDetailedState(DetailedState.SUSPENDED, null, null);
             mNetworkAgent.sendNetworkInfo(mNetworkInfo);
@@ -2498,6 +2511,106 @@
     }
 
     @Test
+    public void testPartialConnectivity() {
+        // Register network callback.
+        NetworkRequest request = new NetworkRequest.Builder()
+                .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
+                .build();
+        TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(request, callback);
+
+        // Bring up validated mobile data.
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+        // Bring up wifi with partial connectivity.
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connectWithPartialConnectivity();
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
+
+        // Mobile data should be the default network.
+        assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        callback.assertNoCallback();
+
+        // If the user chooses yes to use this partial connectivity wifi, switch the default
+        // network to wifi and check if wifi becomes valid or not.
+        mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */,
+                false /* always */);
+        // With https probe disabled, NetworkMonitor should pass the network validation with http
+        // probe.
+        mWiFiNetworkAgent.setNetworkValid();
+        waitForIdle();
+        try {
+            verify(mWiFiNetworkAgent.mNetworkMonitor,
+                    timeout(TIMEOUT_MS).times(1)).notifyAcceptPartialConnectivity();
+        } catch (RemoteException e) {
+            fail(e.getMessage());
+        }
+        callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+        NetworkCapabilities nc = callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED,
+                mWiFiNetworkAgent);
+        assertTrue(nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY));
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+        // Disconnect and reconnect wifi with partial connectivity again.
+        mWiFiNetworkAgent.disconnect();
+        callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connectWithPartialConnectivity();
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
+
+        // Mobile data should be the default network.
+        assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+
+        // If the user chooses no, disconnect wifi immediately.
+        mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), false/* accept */,
+                false /* always */);
+        callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+
+        // If user accepted partial connectivity before, and device reconnects to that network
+        // again, but now the network has full connectivity. The network shouldn't contain
+        // NET_CAPABILITY_PARTIAL_CONNECTIVITY.
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        // acceptUnvalidated is also used as setting for accepting partial networks.
+        mWiFiNetworkAgent.explicitlySelected(true /* acceptUnvalidated */);
+        mWiFiNetworkAgent.connect(true);
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+        nc = callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+        assertFalse(nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY));
+        // Wifi should be the default network.
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        mWiFiNetworkAgent.disconnect();
+        callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+
+        // If user accepted partial connectivity before, and now the device reconnects to the
+        // partial connectivity network. The network should be valid and contain
+        // NET_CAPABILITY_PARTIAL_CONNECTIVITY.
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.explicitlySelected(true /* acceptUnvalidated */);
+        mWiFiNetworkAgent.connectWithPartialConnectivity();
+        callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        // TODO: If the user accepted partial connectivity, we shouldn't switch to wifi until
+        // NetworkMonitor detects partial connectivity
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+        mWiFiNetworkAgent.setNetworkValid();
+        waitForIdle();
+        try {
+            verify(mWiFiNetworkAgent.mNetworkMonitor,
+                    timeout(TIMEOUT_MS).times(1)).notifyAcceptPartialConnectivity();
+        } catch (RemoteException e) {
+            fail(e.getMessage());
+        }
+        callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+        callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
+        // Wifi should be the default network.
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+    }
+
+    @Test
     public void testCaptivePortal() {
         final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
         final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index a12b0a0..fdba723 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -68,6 +68,7 @@
 import android.net.INetd;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
+import android.net.ITetheringEventCallback;
 import android.net.InterfaceConfiguration;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
@@ -123,6 +124,7 @@
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Vector;
 
 @RunWith(AndroidJUnit4.class)
@@ -918,6 +920,67 @@
                 expectedInteractionsWithShowNotification);
     }
 
+    private class TestTetheringEventCallback extends ITetheringEventCallback.Stub {
+        private final ArrayList<Network> mActualUpstreams = new ArrayList<>();
+
+        public void expectUpstreamChanged(Network... networks) {
+            final ArrayList<Network> expectedUpstreams =
+                    new ArrayList<Network>(Arrays.asList(networks));
+            for (Network upstream : expectedUpstreams) {
+                // throws OOB if no expectations
+                assertEquals(mActualUpstreams.remove(0), upstream);
+            }
+            assertNoCallback();
+        }
+
+        @Override
+        public void onUpstreamChanged(Network network) {
+            mActualUpstreams.add(network);
+        }
+
+        public void assertNoCallback() {
+            assertTrue(mActualUpstreams.isEmpty());
+        }
+    }
+
+    @Test
+    public void testRegisterTetheringEventCallback() throws Exception {
+        TestTetheringEventCallback callback1 = new TestTetheringEventCallback();
+        TestTetheringEventCallback callback2 = new TestTetheringEventCallback();
+
+        // 1. Register one callback and run usb tethering.
+        mTethering.registerTetheringEventCallback(callback1);
+        mLooper.dispatchAll();
+        callback1.expectUpstreamChanged(new Network[] {null});
+        NetworkState upstreamState = buildMobileDualStackUpstreamState();
+        runUsbTethering(upstreamState);
+        callback1.expectUpstreamChanged(upstreamState.network);
+        // 2. Register second callback.
+        mTethering.registerTetheringEventCallback(callback2);
+        mLooper.dispatchAll();
+        callback2.expectUpstreamChanged(upstreamState.network);
+        // 3. Disable usb tethering.
+        mTethering.stopTethering(TETHERING_USB);
+        mLooper.dispatchAll();
+        sendUsbBroadcast(false, false, false);
+        mLooper.dispatchAll();
+        callback1.expectUpstreamChanged(new Network[] {null});
+        callback2.expectUpstreamChanged(new Network[] {null});
+        // 4. Unregister first callback and run hotspot.
+        mTethering.unregisterTetheringEventCallback(callback1);
+        mLooper.dispatchAll();
+        when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
+        when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
+                .thenReturn(upstreamState);
+        when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
+        mTethering.startTethering(TETHERING_WIFI, null, false);
+        mLooper.dispatchAll();
+        mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
+        sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
+        mLooper.dispatchAll();
+        callback1.assertNoCallback();
+        callback2.expectUpstreamChanged(upstreamState.network);
+    }
 
     // TODO: Test that a request for hotspot mode doesn't interfere with an
     // already operating tethering mode interface.
diff --git a/tests/utils/testutils/Android.bp b/tests/utils/testutils/Android.bp
index 4be6534..0a9e964 100644
--- a/tests/utils/testutils/Android.bp
+++ b/tests/utils/testutils/Android.bp
@@ -19,7 +19,7 @@
 
     srcs: ["java/**/*.java"],
 
-    static_libs: ["android-support-test"],
+    static_libs: ["junit"],
 
     libs: [
         "android.test.runner",