diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt index 1ee79a425e..95ad694293 100644 --- a/framework/api/system-current.txt +++ b/framework/api/system-current.txt @@ -219,7 +219,7 @@ package android.net { method public void onAutomaticReconnectDisabled(); method public void onBandwidthUpdateRequested(); method public void onNetworkCreated(); - method public void onNetworkDisconnected(); + method public void onNetworkDestroyed(); method public void onNetworkUnwanted(); method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter); method public void onQosCallbackUnregistered(int); @@ -238,6 +238,7 @@ package android.net { method public final void sendQosSessionLost(int, int, int); method public final void sendSocketKeepaliveEvent(int, int); method @Deprecated public void setLegacySubtype(int, @NonNull String); + method public void setTeardownDelayMs(@IntRange(from=0, to=0x1388) int); method public final void setUnderlyingNetworks(@Nullable java.util.List); method public void unregister(); field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2 diff --git a/framework/src/android/net/INetworkAgent.aidl b/framework/src/android/net/INetworkAgent.aidl index f9d399459e..d941d4b95b 100644 --- a/framework/src/android/net/INetworkAgent.aidl +++ b/framework/src/android/net/INetworkAgent.aidl @@ -47,5 +47,5 @@ oneway interface INetworkAgent { void onQosFilterCallbackRegistered(int qosCallbackId, in QosFilterParcelable filterParcel); void onQosCallbackUnregistered(int qosCallbackId); void onNetworkCreated(); - void onNetworkDisconnected(); + void onNetworkDestroyed(); } diff --git a/framework/src/android/net/INetworkAgentRegistry.aidl b/framework/src/android/net/INetworkAgentRegistry.aidl index cbd6193744..26cb1ed6b4 100644 --- a/framework/src/android/net/INetworkAgentRegistry.aidl +++ b/framework/src/android/net/INetworkAgentRegistry.aidl @@ -41,4 +41,5 @@ oneway interface INetworkAgentRegistry { void sendNrQosSessionAvailable(int callbackId, in QosSession session, in NrQosSessionAttributes attributes); void sendQosSessionLost(int qosCallbackId, in QosSession session); void sendQosCallbackError(int qosCallbackId, int exceptionType); + void sendTeardownDelayMs(int teardownDelayMs); } diff --git a/framework/src/android/net/NetworkAgent.java b/framework/src/android/net/NetworkAgent.java index 6b55bb771c..c57da53f28 100644 --- a/framework/src/android/net/NetworkAgent.java +++ b/framework/src/android/net/NetworkAgent.java @@ -184,6 +184,20 @@ public abstract class NetworkAgent { */ public static final int EVENT_UNDERLYING_NETWORKS_CHANGED = BASE + 5; + /** + * Sent by the NetworkAgent to ConnectivityService to pass the current value of the teardown + * delay. + * arg1 = teardown delay in milliseconds + * @hide + */ + public static final int EVENT_TEARDOWN_DELAY_CHANGED = BASE + 6; + + /** + * The maximum value for the teardown delay, in milliseconds. + * @hide + */ + public static final int MAX_TEARDOWN_DELAY_MS = 5000; + /** * Sent by ConnectivityService to the NetworkAgent to inform the agent of the * networks status - whether we could use the network or could not, due to @@ -197,7 +211,6 @@ public abstract class NetworkAgent { */ public static final int CMD_REPORT_NETWORK_STATUS = BASE + 7; - /** * Network validation suceeded. * Corresponds to {@link NetworkCapabilities.NET_CAPABILITY_VALIDATED}. @@ -376,7 +389,7 @@ public abstract class NetworkAgent { * * @hide */ - public static final int CMD_NETWORK_DISCONNECTED = BASE + 23; + public static final int CMD_NETWORK_DESTROYED = BASE + 23; private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) { final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType, @@ -581,8 +594,8 @@ public abstract class NetworkAgent { onNetworkCreated(); break; } - case CMD_NETWORK_DISCONNECTED: { - onNetworkDisconnected(); + case CMD_NETWORK_DESTROYED: { + onNetworkDestroyed(); break; } } @@ -732,8 +745,8 @@ public abstract class NetworkAgent { } @Override - public void onNetworkDisconnected() { - mHandler.sendMessage(mHandler.obtainMessage(CMD_NETWORK_DISCONNECTED)); + public void onNetworkDestroyed() { + mHandler.sendMessage(mHandler.obtainMessage(CMD_NETWORK_DESTROYED)); } } @@ -850,6 +863,29 @@ public abstract class NetworkAgent { queueOrSendNetworkInfo(mNetworkInfo); } + /** + * Sets the value of the teardown delay. + * + * The teardown delay is the time between when the network disconnects and when the native + * network corresponding to this {@code NetworkAgent} is destroyed. By default, the native + * network is destroyed immediately. If {@code teardownDelayMs} is non-zero, then when this + * network disconnects, the system will instead immediately mark the network as restricted + * and unavailable to unprivileged apps, but will defer destroying the native network until the + * teardown delay timer expires. + * + * The interfaces in use by this network will remain in use until the native network is + * destroyed and cannot be reused until {@link #onNetworkDestroyed()} is called. + * + * This method may be called at any time while the network is connected. It has no effect if + * the network is already disconnected and the teardown delay timer is running. + * + * @param teardownDelayMs the teardown delay to set, or 0 to disable teardown delay. + */ + public void setTeardownDelayMs( + @IntRange(from = 0, to = MAX_TEARDOWN_DELAY_MS) int teardownDelayMs) { + queueOrSendMessage(reg -> reg.sendTeardownDelayMs(teardownDelayMs)); + } + /** * Change the legacy subtype of this network agent. * @@ -1053,7 +1089,7 @@ public abstract class NetworkAgent { /** * Called when ConnectivityService has successfully destroy this NetworkAgent's native network. */ - public void onNetworkDisconnected() {} + public void onNetworkDestroyed() {} /** * Requests that the network hardware send the specified packet at the specified interval. diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 1a31509c33..b437aac88f 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -3122,6 +3122,13 @@ public class ConnectivityService extends IConnectivityManager.Stub } break; } + case NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED: { + if (msg.arg1 >= 0 && msg.arg1 <= NetworkAgent.MAX_TEARDOWN_DELAY_MS) { + nai.teardownDelayMs = msg.arg1; + } else { + logwtf(nai.toShortString() + " set invalid teardown delay " + msg.arg1); + } + } } } @@ -3692,6 +3699,23 @@ public class ConnectivityService extends IConnectivityManager.Stub mLegacyTypeTracker.remove(nai, wasDefault); rematchAllNetworksAndRequests(); mLingerMonitor.noteDisconnect(nai); + + // Immediate teardown. + if (nai.teardownDelayMs == 0) { + destroyNetwork(nai); + return; + } + + // Delayed teardown. + try { + mNetd.networkSetPermissionForNetwork(nai.network.netId, INetd.PERMISSION_SYSTEM); + } catch (RemoteException e) { + Log.d(TAG, "Error marking network restricted during teardown: " + e); + } + mHandler.postDelayed(() -> destroyNetwork(nai), nai.teardownDelayMs); + } + + private void destroyNetwork(NetworkAgentInfo nai) { if (nai.created) { // Tell netd to clean up the configuration for this network // (routing rules, DNS, etc). @@ -3704,7 +3728,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mDnsManager.removeNetwork(nai.network); } mNetIdManager.releaseNetId(nai.network.getNetId()); - nai.onNetworkDisconnected(); + nai.onNetworkDestroyed(); } private boolean createNativeNetwork(@NonNull NetworkAgentInfo networkAgent) { diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 97df5bff49..ee32fbf00d 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -201,6 +201,9 @@ public class NetworkAgentInfo implements Comparable { // Set to true when partial connectivity was detected. public boolean partialConnectivity; + // Delay between when the network is disconnected and when the native network is destroyed. + public int teardownDelayMs; + // Captive portal info of the network from RFC8908, if any. // Obtained by ConnectivityService and merged into NetworkAgent-provided information. public CaptivePortalData capportApiData; @@ -589,13 +592,13 @@ public class NetworkAgentInfo implements Comparable { } /** - * Notify the NetworkAgent that the network is disconnected and destroyed. + * Notify the NetworkAgent that the native network has been destroyed. */ - public void onNetworkDisconnected() { + public void onNetworkDestroyed() { try { - networkAgent.onNetworkDisconnected(); + networkAgent.onNetworkDestroyed(); } catch (RemoteException e) { - Log.e(TAG, "Error sending network disconnected event", e); + Log.e(TAG, "Error sending network destroyed event", e); } } @@ -675,6 +678,12 @@ public class NetworkAgentInfo implements Comparable { @QosCallbackException.ExceptionType final int exceptionType) { mQosCallbackTracker.sendEventQosCallbackError(qosCallbackId, exceptionType); } + + @Override + public void sendTeardownDelayMs(int teardownDelayMs) { + mHandler.obtainMessage(NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED, + teardownDelayMs, 0, new Pair<>(NetworkAgentInfo.this, null)).sendToTarget(); + } } /**