diff options
26 files changed, 1228 insertions, 476 deletions
diff --git a/Android.bp b/Android.bp index 25db33befd93..2966c2b4c734 100644 --- a/Android.bp +++ b/Android.bp @@ -824,8 +824,11 @@ aidl_interface { name: "networkstack-aidl-interfaces", local_include_dir: "core/java", srcs: [ + "core/java/android/net/INetworkMonitor.aidl", + "core/java/android/net/INetworkMonitorCallbacks.aidl", "core/java/android/net/INetworkStackConnector.aidl", "core/java/android/net/INetworkStackStatusCallback.aidl", + "core/java/android/net/PrivateDnsConfigParcel.aidl", "core/java/android/net/dhcp/DhcpServingParamsParcel.aidl", "core/java/android/net/dhcp/IDhcpServer.aidl", "core/java/android/net/dhcp/IDhcpServerCallbacks.aidl", diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 436b4a15e7c1..abc00feeb212 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -2051,6 +2051,16 @@ public class ConnectivityManager { return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); } + /** @hide */ + public NetworkRequest getDefaultRequest() { + try { + // This is not racy as the default request is final in ConnectivityService. + return mService.getDefaultRequest(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /* TODO: These permissions checks don't belong in client-side code. Move them to * services.jar, possibly in com.android.server.net. */ diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index e7d441df82a6..da5d96e49d02 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -167,6 +167,8 @@ interface IConnectivityManager int getMultipathPreference(in Network Network); + NetworkRequest getDefaultRequest(); + int getRestoreDefaultNetworkDelay(int networkType); boolean addVpnAddress(String address, int prefixLength); diff --git a/core/java/android/net/INetworkMonitor.aidl b/core/java/android/net/INetworkMonitor.aidl new file mode 100644 index 000000000000..41f969acad6f --- /dev/null +++ b/core/java/android/net/INetworkMonitor.aidl @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2018, 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 perNmissions and + * limitations under the License. + */ +package android.net; + +import android.net.PrivateDnsConfigParcel; + +/** @hide */ +oneway interface INetworkMonitor { + // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED. + // The network should be used as a default internet connection. It was found to be: + // 1. a functioning network providing internet access, or + // 2. a captive portal and the user decided to use it as is. + const int NETWORK_TEST_RESULT_VALID = 0; + + // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED. + // The network should not be used as a default internet connection. It was found to be: + // 1. a captive portal and the user is prompted to sign-in, or + // 2. a captive portal and the user did not want to use it, or + // 3. a broken network (e.g. DNS failed, connect failed, HTTP request failed). + const int NETWORK_TEST_RESULT_INVALID = 1; + + void start(); + void launchCaptivePortalApp(); + void forceReevaluation(int uid); + void notifyPrivateDnsChanged(in PrivateDnsConfigParcel config); + void notifyDnsResponse(int returnCode); + void notifySystemReady(); + void notifyNetworkConnected(); + void notifyNetworkDisconnected(); + void notifyLinkPropertiesChanged(); + void notifyNetworkCapabilitiesChanged(); +}
\ No newline at end of file diff --git a/core/java/android/net/INetworkMonitorCallbacks.aidl b/core/java/android/net/INetworkMonitorCallbacks.aidl new file mode 100644 index 000000000000..0bc25750129b --- /dev/null +++ b/core/java/android/net/INetworkMonitorCallbacks.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 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.INetworkMonitor; +import android.net.PrivateDnsConfigParcel; + +/** @hide */ +oneway interface INetworkMonitorCallbacks { + void onNetworkMonitorCreated(in INetworkMonitor networkMonitor); + void notifyNetworkTested(int testResult, @nullable String redirectUrl); + void notifyPrivateDnsConfigResolved(in PrivateDnsConfigParcel config); + void showProvisioningNotification(String action); + void hideProvisioningNotification(); +}
\ No newline at end of file diff --git a/core/java/android/net/INetworkStackConnector.aidl b/core/java/android/net/INetworkStackConnector.aidl index be0dc07f4b23..2df8ab7ec198 100644 --- a/core/java/android/net/INetworkStackConnector.aidl +++ b/core/java/android/net/INetworkStackConnector.aidl @@ -15,6 +15,7 @@ */ package android.net; +import android.net.INetworkMonitorCallbacks; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServerCallbacks; @@ -22,4 +23,5 @@ import android.net.dhcp.IDhcpServerCallbacks; oneway interface INetworkStackConnector { void makeDhcpServer(in String ifName, in DhcpServingParamsParcel params, in IDhcpServerCallbacks cb); + void makeNetworkMonitor(int netId, String name, in INetworkMonitorCallbacks cb); }
\ No newline at end of file diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java index d4a0ec632383..2eac6de56346 100644 --- a/core/java/android/net/NetworkStack.java +++ b/core/java/android/net/NetworkStack.java @@ -48,14 +48,16 @@ import java.util.ArrayList; public class NetworkStack { private static final String TAG = NetworkStack.class.getSimpleName(); + public static final String NETWORKSTACK_PACKAGE_NAME = "com.android.mainline.networkstack"; + @NonNull @GuardedBy("mPendingNetStackRequests") - private final ArrayList<NetworkStackRequest> mPendingNetStackRequests = new ArrayList<>(); + private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>(); @Nullable @GuardedBy("mPendingNetStackRequests") private INetworkStackConnector mConnector; - private interface NetworkStackRequest { + private interface NetworkStackCallback { void onNetworkStackConnected(INetworkStackConnector connector); } @@ -77,6 +79,21 @@ public class NetworkStack { }); } + /** + * Create a NetworkMonitor. + * + * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks. + */ + public void makeNetworkMonitor(Network network, String name, INetworkMonitorCallbacks cb) { + requestConnector(connector -> { + try { + connector.makeNetworkMonitor(network.netId, name, cb); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + }); + } + private class NetworkStackConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { @@ -96,14 +113,14 @@ public class NetworkStack { ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */, DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL); - final ArrayList<NetworkStackRequest> requests; + final ArrayList<NetworkStackCallback> requests; synchronized (mPendingNetStackRequests) { requests = new ArrayList<>(mPendingNetStackRequests); mPendingNetStackRequests.clear(); mConnector = connector; } - for (NetworkStackRequest r : requests) { + for (NetworkStackCallback r : requests) { r.onNetworkStackConnected(connector); } } @@ -124,7 +141,8 @@ public class NetworkStack { "com.android.server.NetworkStackService", true /* initialize */, context.getClassLoader()); - connector = (IBinder) service.getMethod("makeConnector").invoke(null); + connector = (IBinder) service.getMethod("makeConnector", Context.class) + .invoke(null, context); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService"); // TODO: crash/reboot system here ? @@ -153,7 +171,7 @@ public class NetworkStack { } // TODO: use this method to obtain the connector when implementing network stack operations - private void requestConnector(@NonNull NetworkStackRequest request) { + private void requestConnector(@NonNull NetworkStackCallback request) { // TODO: PID check. if (Binder.getCallingUid() != Process.SYSTEM_UID) { // Don't even attempt to obtain the connector and give a nice error message diff --git a/core/java/android/net/PrivateDnsConfigParcel.aidl b/core/java/android/net/PrivateDnsConfigParcel.aidl new file mode 100644 index 000000000000..b52fce643302 --- /dev/null +++ b/core/java/android/net/PrivateDnsConfigParcel.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2018 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; + +parcelable PrivateDnsConfigParcel { + String hostname; + String[] ips; +} diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp index 4688848dee0f..2f7d599c68bf 100644 --- a/packages/NetworkStack/Android.bp +++ b/packages/NetworkStack/Android.bp @@ -21,10 +21,10 @@ java_library { installable: true, srcs: [ "src/**/*.java", + ":services-networkstack-shared-srcs", ], static_libs: [ "dhcp-packet-lib", - "frameworks-net-shared-utils", ] } diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml index 8516d9485f57..0b0f1eca7aa5 100644 --- a/packages/NetworkStack/AndroidManifest.xml +++ b/packages/NetworkStack/AndroidManifest.xml @@ -22,8 +22,11 @@ <uses-permission android:name="android.permission.INTERNET" /> <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" /> + <uses-permission android:name="android.permission.NETWORK_SETTINGS" /> <!-- Launch captive portal app as specific user --> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> + <uses-permission android:name="android.permission.NETWORK_STACK" /> <application android:label="NetworkStack" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/NetworkStack/src/android/net/util/SharedLog.java b/packages/NetworkStack/src/android/net/util/SharedLog.java index 74bc1470293f..4fabf10bbd37 100644 --- a/packages/NetworkStack/src/android/net/util/SharedLog.java +++ b/packages/NetworkStack/src/android/net/util/SharedLog.java @@ -69,6 +69,10 @@ public class SharedLog { mComponent = component; } + public String getTag() { + return mTag; + } + /** * Create a SharedLog based on this log with an additional component prefix on each logged line. */ diff --git a/services/net/java/android/net/util/Stopwatch.java b/packages/NetworkStack/src/android/net/util/Stopwatch.java index cb15ee580a73..c3166999cdca 100644 --- a/services/net/java/android/net/util/Stopwatch.java +++ b/packages/NetworkStack/src/android/net/util/Stopwatch.java @@ -38,9 +38,9 @@ public class Stopwatch { return (isStarted() && !isStopped()); } - // Returning |this| makes possible the following usage pattern: - // - // Stopwatch s = new Stopwatch().start(); + /** + * Start the Stopwatch. + */ public Stopwatch start() { if (!isStarted()) { mStartTimeMs = SystemClock.elapsedRealtime(); @@ -48,7 +48,10 @@ public class Stopwatch { return this; } - // Returns the total time recorded, in milliseconds, or 0 if not started. + /** + * Stop the Stopwatch. + * @return the total time recorded, in milliseconds, or 0 if not started. + */ public long stop() { if (isRunning()) { mStopTimeMs = SystemClock.elapsedRealtime(); @@ -57,9 +60,11 @@ public class Stopwatch { return (mStopTimeMs - mStartTimeMs); } - // Returns the total time recorded to date, in milliseconds. - // If the Stopwatch is not running, returns the same value as stop(), - // i.e. either the total time recorded before stopping or 0. + /** + * Return the total time recorded to date, in milliseconds. + * If the Stopwatch is not running, returns the same value as stop(), + * i.e. either the total time recorded before stopping or 0. + */ public long lap() { if (isRunning()) { return (SystemClock.elapsedRealtime() - mStartTimeMs); @@ -68,6 +73,9 @@ public class Stopwatch { } } + /** + * Reset the Stopwatch. It will be stopped when this method returns. + */ public void reset() { mStartTimeMs = 0; mStopTimeMs = 0; diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java index 7fea1e038cee..057012de433a 100644 --- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java +++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java @@ -25,18 +25,31 @@ import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPer import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Service; +import android.content.Context; import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.INetworkMonitor; +import android.net.INetworkMonitorCallbacks; import android.net.INetworkStackConnector; +import android.net.Network; +import android.net.NetworkRequest; +import android.net.PrivateDnsConfigParcel; import android.net.dhcp.DhcpServer; import android.net.dhcp.DhcpServingParams; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServerCallbacks; +import android.net.shared.PrivateDnsConfig; import android.net.util.SharedLog; import android.os.IBinder; import android.os.RemoteException; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.IndentingPrintWriter; +import com.android.server.connectivity.NetworkMonitor; + import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayDeque; /** * Android service used to start the network stack when bound to via an intent. @@ -52,17 +65,41 @@ public class NetworkStackService extends Service { * <p>On platforms where the network stack runs in the system server process, this method may * be called directly instead of obtaining the connector by binding to the service. */ - public static IBinder makeConnector() { - return new NetworkStackConnector(); + public static IBinder makeConnector(Context context) { + return new NetworkStackConnector(context); } @NonNull @Override public IBinder onBind(Intent intent) { - return makeConnector(); + return makeConnector(this); } private static class NetworkStackConnector extends INetworkStackConnector.Stub { + private static final int NUM_VALIDATION_LOG_LINES = 20; + private final Context mContext; + private final ConnectivityManager mCm; + + private static final int MAX_VALIDATION_LOGS = 10; + @GuardedBy("mValidationLogs") + private final ArrayDeque<SharedLog> mValidationLogs = new ArrayDeque<>(MAX_VALIDATION_LOGS); + + private SharedLog addValidationLogs(Network network, String name) { + final SharedLog log = new SharedLog(NUM_VALIDATION_LOG_LINES, network + " - " + name); + synchronized (mValidationLogs) { + while (mValidationLogs.size() >= MAX_VALIDATION_LOGS) { + mValidationLogs.removeLast(); + } + mValidationLogs.addFirst(log); + } + return log; + } + + NetworkStackConnector(Context context) { + mContext = context; + mCm = context.getSystemService(ConnectivityManager.class); + } + @NonNull private final SharedLog mLog = new SharedLog(TAG); @@ -89,11 +126,102 @@ public class NetworkStackService extends Service { } @Override + public void makeNetworkMonitor(int netId, String name, INetworkMonitorCallbacks cb) + throws RemoteException { + final Network network = new Network(netId, false /* privateDnsBypass */); + final NetworkRequest defaultRequest = mCm.getDefaultRequest(); + final SharedLog log = addValidationLogs(network, name); + final NetworkMonitor nm = new NetworkMonitor( + mContext, cb, network, defaultRequest, log); + cb.onNetworkMonitorCreated(new NetworkMonitorImpl(nm)); + } + + @Override protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout, @Nullable String[] args) { checkNetworkStackCallingPermission(); - fout.println("NetworkStack logs:"); - mLog.dump(fd, fout, args); + final IndentingPrintWriter pw = new IndentingPrintWriter(fout, " "); + pw.println("NetworkStack logs:"); + mLog.dump(fd, pw, args); + + pw.println(); + pw.println("Validation logs (most recent first):"); + synchronized (mValidationLogs) { + for (SharedLog p : mValidationLogs) { + pw.println(p.getTag()); + pw.increaseIndent(); + p.dump(fd, pw, args); + pw.decreaseIndent(); + } + } + } + } + + private static class NetworkMonitorImpl extends INetworkMonitor.Stub { + private final NetworkMonitor mNm; + + NetworkMonitorImpl(NetworkMonitor nm) { + mNm = nm; + } + + @Override + public void start() { + checkNetworkStackCallingPermission(); + mNm.start(); + } + + @Override + public void launchCaptivePortalApp() { + checkNetworkStackCallingPermission(); + mNm.launchCaptivePortalApp(); + } + + @Override + public void forceReevaluation(int uid) { + checkNetworkStackCallingPermission(); + mNm.forceReevaluation(uid); + } + + @Override + public void notifyPrivateDnsChanged(PrivateDnsConfigParcel config) { + checkNetworkStackCallingPermission(); + mNm.notifyPrivateDnsSettingsChanged(PrivateDnsConfig.fromParcel(config)); + } + + @Override + public void notifyDnsResponse(int returnCode) { + checkNetworkStackCallingPermission(); + mNm.notifyDnsResponse(returnCode); + } + + @Override + public void notifySystemReady() { + checkNetworkStackCallingPermission(); + mNm.notifySystemReady(); + } + + @Override + public void notifyNetworkConnected() { + checkNetworkStackCallingPermission(); + mNm.notifyNetworkConnected(); + } + + @Override + public void notifyNetworkDisconnected() { + checkNetworkStackCallingPermission(); + mNm.notifyNetworkDisconnected(); + } + + @Override + public void notifyLinkPropertiesChanged() { + checkNetworkStackCallingPermission(); + mNm.notifyLinkPropertiesChanged(); + } + + @Override + public void notifyNetworkCapabilitiesChanged() { + checkNetworkStackCallingPermission(); + mNm.notifyNetworkCapabilitiesChanged(); } } } diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java index 2a000252d6f4..94ea1b931348 100644 --- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java @@ -21,6 +21,11 @@ import static android.net.CaptivePortal.APP_RETURN_UNWANTED; import static android.net.CaptivePortal.APP_RETURN_WANTED_AS_IS; import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_PROBE_SPEC; import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL; +import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.metrics.ValidationProbeEvent.DNS_FAILURE; import static android.net.metrics.ValidationProbeEvent.DNS_SUCCESS; import static android.net.metrics.ValidationProbeEvent.PROBE_FALLBACK; @@ -35,6 +40,9 @@ import android.content.IntentFilter; import android.net.CaptivePortal; import android.net.ConnectivityManager; import android.net.ICaptivePortal; +import android.net.INetworkMonitor; +import android.net.INetworkMonitorCallbacks; +import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; @@ -46,11 +54,14 @@ import android.net.captiveportal.CaptivePortalProbeSpec; import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; import android.net.metrics.ValidationProbeEvent; +import android.net.shared.NetworkMonitorUtils; +import android.net.shared.PrivateDnsConfig; +import android.net.util.SharedLog; import android.net.util.Stopwatch; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; -import android.os.Handler; import android.os.Message; +import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; @@ -65,8 +76,6 @@ import android.telephony.CellInfoLte; import android.telephony.CellInfoWcdma; import android.telephony.TelephonyManager; import android.text.TextUtils; -import android.util.LocalLog; -import android.util.LocalLog.ReadOnlyLocalLog; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -75,7 +84,6 @@ import com.android.internal.util.Protocol; import com.android.internal.util.RingBufferIndices; import com.android.internal.util.State; import com.android.internal.util.StateMachine; -import com.android.server.connectivity.DnsManager.PrivateDnsConfig; import java.io.IOException; import java.net.HttpURLConnection; @@ -104,9 +112,7 @@ public class NetworkMonitor extends StateMachine { // Default configuration values for captive portal detection probes. // TODO: append a random length parameter to the default HTTPS url. // TODO: randomize browser version ids in the default User-Agent String. - private static final String DEFAULT_HTTPS_URL = "https://www.google.com/generate_204"; - private static final String DEFAULT_HTTP_URL = - "http://connectivitycheck.gstatic.com/generate_204"; + private static final String DEFAULT_HTTPS_URL = "https://www.google.com/generate_204"; private static final String DEFAULT_FALLBACK_URL = "http://www.google.com/gen_204"; private static final String DEFAULT_OTHER_FALLBACK_URLS = "http://play.googleapis.com/generate_204"; @@ -144,33 +150,12 @@ public class NetworkMonitor extends StateMachine { } } - // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED. - // The network should be used as a default internet connection. It was found to be: - // 1. a functioning network providing internet access, or - // 2. a captive portal and the user decided to use it as is. - public static final int NETWORK_TEST_RESULT_VALID = 0; - // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED. - // The network should not be used as a default internet connection. It was found to be: - // 1. a captive portal and the user is prompted to sign-in, or - // 2. a captive portal and the user did not want to use it, or - // 3. a broken network (e.g. DNS failed, connect failed, HTTP request failed). - public static final int NETWORK_TEST_RESULT_INVALID = 1; - private static final int BASE = Protocol.BASE_NETWORK_MONITOR; - /** - * Inform NetworkMonitor that their network is connected. + * ConnectivityService has sent a notification to indicate that network has connected. * Initiates Network Validation. */ - public static final int CMD_NETWORK_CONNECTED = BASE + 1; - - /** - * Inform ConnectivityService that the network has been tested. - * obj = String representing URL that Internet probe was redirect to, if it was redirected. - * arg1 = One of the NETWORK_TESTED_RESULT_* constants. - * arg2 = NetID. - */ - public static final int EVENT_NETWORK_TESTED = BASE + 2; + private static final int CMD_NETWORK_CONNECTED = BASE + 1; /** * Message to self indicating it's time to evaluate a network's connectivity. @@ -179,9 +164,9 @@ public class NetworkMonitor extends StateMachine { private static final int CMD_REEVALUATE = BASE + 6; /** - * Inform NetworkMonitor that the network has disconnected. + * ConnectivityService has sent a notification to indicate that network has disconnected. */ - public static final int CMD_NETWORK_DISCONNECTED = BASE + 7; + private static final int CMD_NETWORK_DISCONNECTED = BASE + 7; /** * Force evaluation even if it has succeeded in the past. @@ -199,21 +184,13 @@ public class NetworkMonitor extends StateMachine { private static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = BASE + 9; /** - * Request ConnectivityService display provisioning notification. - * arg1 = Whether to make the notification visible. - * arg2 = NetID. - * obj = Intent to be launched when notification selected by user, null if !arg1. - */ - public static final int EVENT_PROVISIONING_NOTIFICATION = BASE + 10; - - /** * Message indicating sign-in app should be launched. * Sent by mLaunchCaptivePortalAppBroadcastReceiver when the * user touches the sign in notification, or sent by * ConnectivityService when the user touches the "sign into * network" button in the wifi access point detail page. */ - public static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = BASE + 11; + private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = BASE + 11; /** * Retest network to see if captive portal is still in place. @@ -234,7 +211,6 @@ public class NetworkMonitor extends StateMachine { * validation phase is completed. */ private static final int CMD_PRIVATE_DNS_SETTINGS_CHANGED = BASE + 13; - public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = BASE + 14; private static final int CMD_EVALUATE_PRIVATE_DNS = BASE + 15; /** @@ -263,23 +239,16 @@ public class NetworkMonitor extends StateMachine { // Delay between reevaluations once a captive portal has been found. private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10 * 60 * 1000; - private static final int NUM_VALIDATION_LOG_LINES = 20; - private String mPrivateDnsProviderHostname = ""; - public static boolean isValidationRequired( - NetworkCapabilities dfltNetCap, NetworkCapabilities nc) { - // TODO: Consider requiring validation for DUN networks. - return dfltNetCap.satisfiedByNetworkCapabilities(nc); - } - private final Context mContext; - private final Handler mConnectivityServiceHandler; - private final NetworkAgentInfo mNetworkAgentInfo; + private final INetworkMonitorCallbacks mCallback; private final Network mNetwork; + private final Network mNonPrivateDnsBypassNetwork; private final int mNetId; private final TelephonyManager mTelephonyManager; private final WifiManager mWifiManager; + private final ConnectivityManager mCm; private final NetworkRequest mDefaultRequest; private final IpConnectivityLog mMetricsLog; private final Dependencies mDependencies; @@ -292,6 +261,9 @@ public class NetworkMonitor extends StateMachine { @Nullable private final CaptivePortalProbeSpec[] mCaptivePortalFallbackSpecs; + private NetworkCapabilities mNetworkCapabilities; + private LinkProperties mLinkProperties; + @VisibleForTesting protected boolean mIsCaptivePortalCheckEnabled; @@ -304,7 +276,7 @@ public class NetworkMonitor extends StateMachine { // Avoids surfacing "Sign in to network" notification. private boolean mDontDisplaySigninNotification = false; - public boolean systemReady = false; + private volatile boolean mSystemReady = false; private final State mDefaultState = new DefaultState(); private final State mValidatedState = new ValidatedState(); @@ -317,7 +289,7 @@ public class NetworkMonitor extends StateMachine { private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null; - private final LocalLog validationLogs = new LocalLog(NUM_VALIDATION_LOG_LINES); + private final SharedLog mValidationLogs; private final Stopwatch mEvaluationTimer = new Stopwatch(); @@ -328,6 +300,7 @@ public class NetworkMonitor extends StateMachine { private final Random mRandom; private int mNextFallbackUrlIndex = 0; + private int mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS; private int mEvaluateAttempts = 0; private volatile int mProbeToken = 0; @@ -338,17 +311,18 @@ public class NetworkMonitor extends StateMachine { private final DnsStallDetector mDnsStallDetector; private long mLastProbeTime; - public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo, - NetworkRequest defaultRequest) { - this(context, handler, networkAgentInfo, defaultRequest, new IpConnectivityLog(), + public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, + NetworkRequest defaultRequest, SharedLog validationLog) { + this(context, cb, network, defaultRequest, new IpConnectivityLog(), validationLog, Dependencies.DEFAULT); } @VisibleForTesting - protected NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo, - NetworkRequest defaultRequest, IpConnectivityLog logger, Dependencies deps) { + protected NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, + NetworkRequest defaultRequest, IpConnectivityLog logger, SharedLog validationLogs, + Dependencies deps) { // Add suffix indicating which NetworkMonitor we're talking about. - super(TAG + networkAgentInfo.name()); + super(TAG + "/" + network.netId); // Logs with a tag of the form given just above, e.g. // <timestamp> 862 2402 D NetworkMonitor/NetworkAgentInfo [WIFI () - 100]: ... @@ -356,15 +330,18 @@ public class NetworkMonitor extends StateMachine { mContext = context; mMetricsLog = logger; - mConnectivityServiceHandler = handler; + mValidationLogs = validationLogs; + mCallback = cb; mDependencies = deps; - mNetworkAgentInfo = networkAgentInfo; - mNetwork = deps.getNetwork(networkAgentInfo).getPrivateDnsBypassingCopy(); + mNonPrivateDnsBypassNetwork = network; + mNetwork = deps.getPrivateDnsBypassNetwork(network); mNetId = mNetwork.netId; mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + mCm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); mDefaultRequest = defaultRequest; + // CHECKSTYLE:OFF IndentationCheck addState(mDefaultState); addState(mMaybeNotifyState, mDefaultState); addState(mEvaluatingState, mMaybeNotifyState); @@ -374,12 +351,13 @@ public class NetworkMonitor extends StateMachine { addState(mEvaluatingPrivateDnsState, mDefaultState); addState(mValidatedState, mDefaultState); setInitialState(mDefaultState); + // CHECKSTYLE:ON IndentationCheck mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled(); mUseHttps = getUseHttpsValidation(); mCaptivePortalUserAgent = getCaptivePortalUserAgent(); mCaptivePortalHttpsUrl = makeURL(getCaptivePortalServerHttpsUrl()); - mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl(deps, context)); + mCaptivePortalHttpUrl = makeURL(deps.getCaptivePortalServerHttpUrl(context)); mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls(); mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs(); mRandom = deps.getRandom(); @@ -390,7 +368,13 @@ public class NetworkMonitor extends StateMachine { mDataStallValidDnsTimeThreshold = getDataStallValidDnsTimeThreshold(); mDataStallEvaluationType = getDataStallEvalutionType(); - start(); + // mLinkProperties and mNetworkCapbilities must never be null or we will NPE. + // Provide empty objects in case we are started and the network disconnects before + // we can ever fetch them. + // TODO: Delete ASAP. + mLinkProperties = new LinkProperties(); + mNetworkCapabilities = new NetworkCapabilities(); + mNetworkCapabilities.clearAll(); } /** @@ -401,6 +385,14 @@ public class NetworkMonitor extends StateMachine { } /** + * Send a notification to NetworkMonitor indicating that there was a DNS query response event. + * @param returnCode the DNS return code of the response. + */ + public void notifyDnsResponse(int returnCode) { + sendMessage(EVENT_DNS_NOTIFICATION, returnCode); + } + + /** * Send a notification to NetworkMonitor indicating that private DNS settings have changed. * @param newCfg The new private DNS configuration. */ @@ -411,9 +403,75 @@ public class NetworkMonitor extends StateMachine { sendMessage(CMD_PRIVATE_DNS_SETTINGS_CHANGED, newCfg); } + /** + * Send a notification to NetworkMonitor indicating that the system is ready. + */ + public void notifySystemReady() { + // No need to run on the handler thread: mSystemReady is volatile and read only once on the + // isCaptivePortal() thread. + mSystemReady = true; + } + + /** + * Send a notification to NetworkMonitor indicating that the network is now connected. + */ + public void notifyNetworkConnected() { + sendMessage(CMD_NETWORK_CONNECTED); + } + + /** + * Send a notification to NetworkMonitor indicating that the network is now disconnected. + */ + public void notifyNetworkDisconnected() { + sendMessage(CMD_NETWORK_DISCONNECTED); + } + + /** + * Send a notification to NetworkMonitor indicating that link properties have changed. + */ + public void notifyLinkPropertiesChanged() { + getHandler().post(() -> { + updateLinkProperties(); + }); + } + + private void updateLinkProperties() { + final LinkProperties lp = mCm.getLinkProperties(mNetwork); + // If null, we should soon get a message that the network was disconnected, and will stop. + if (lp != null) { + // TODO: send LinkProperties parceled in notifyLinkPropertiesChanged() and start(). + mLinkProperties = lp; + } + } + + /** + * Send a notification to NetworkMonitor indicating that network capabilities have changed. + */ + public void notifyNetworkCapabilitiesChanged() { + getHandler().post(() -> { + updateNetworkCapabilities(); + }); + } + + private void updateNetworkCapabilities() { + final NetworkCapabilities nc = mCm.getNetworkCapabilities(mNetwork); + // If null, we should soon get a message that the network was disconnected, and will stop. + if (nc != null) { + // TODO: send NetworkCapabilities parceled in notifyNetworkCapsChanged() and start(). + mNetworkCapabilities = nc; + } + } + + /** + * Request the captive portal application to be launched. + */ + public void launchCaptivePortalApp() { + sendMessage(CMD_LAUNCH_CAPTIVE_PORTAL_APP); + } + @Override protected void log(String s) { - if (DBG) Log.d(TAG + "/" + mNetworkAgentInfo.name(), s); + if (DBG) Log.d(TAG + "/" + mNetwork.netId, s); } private void validationLog(int probeType, Object url, String msg) { @@ -423,11 +481,7 @@ public class NetworkMonitor extends StateMachine { private void validationLog(String s) { if (DBG) log(s); - validationLogs.log(s); - } - - public ReadOnlyLocalLog getValidationLogs() { - return validationLogs.readOnlyLocalLog(); + mValidationLogs.log(s); } private ValidationStage validationStage() { @@ -435,20 +489,46 @@ public class NetworkMonitor extends StateMachine { } private boolean isValidationRequired() { - return isValidationRequired( - mDefaultRequest.networkCapabilities, mNetworkAgentInfo.networkCapabilities); + return NetworkMonitorUtils.isValidationRequired( + mDefaultRequest.networkCapabilities, mNetworkCapabilities); + } + + + private void notifyNetworkTested(int result, @Nullable String redirectUrl) { + try { + mCallback.notifyNetworkTested(result, redirectUrl); + } catch (RemoteException e) { + Log.e(TAG, "Error sending network test result", e); + } } + private void showProvisioningNotification(String action) { + try { + mCallback.showProvisioningNotification(action); + } catch (RemoteException e) { + Log.e(TAG, "Error showing provisioning notification", e); + } + } - private void notifyNetworkTestResultInvalid(Object obj) { - mConnectivityServiceHandler.sendMessage(obtainMessage( - EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, mNetId, obj)); + private void hideProvisioningNotification() { + try { + mCallback.hideProvisioningNotification(); + } catch (RemoteException e) { + Log.e(TAG, "Error hiding provisioning notification", e); + } } // DefaultState is the parent of all States. It exists only to handle CMD_* messages but // does not entail any real state (hence no enter() or exit() routines). private class DefaultState extends State { @Override + public void enter() { + // TODO: have those passed parceled in start() and remove this + updateLinkProperties(); + updateNetworkCapabilities(); + } + + @Override public boolean processMessage(Message message) { switch (message.what) { case CMD_NETWORK_CONNECTED: @@ -499,7 +579,7 @@ public class NetworkMonitor extends StateMachine { case APP_RETURN_UNWANTED: mDontDisplaySigninNotification = true; mUserDoesNotWant = true; - notifyNetworkTestResultInvalid(null); + notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, null); // TODO: Should teardown network. mUidResponsibleForReeval = 0; transitionTo(mEvaluatingState); @@ -563,8 +643,7 @@ public class NetworkMonitor extends StateMachine { public void enter() { maybeLogEvaluationResult( networkEventType(validationStage(), EvaluationResult.VALIDATED)); - mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, - NETWORK_TEST_RESULT_VALID, mNetId, null)); + notifyNetworkTested(INetworkMonitor.NETWORK_TEST_RESULT_VALID, null); mValidations++; } @@ -633,8 +712,7 @@ public class NetworkMonitor extends StateMachine { @Override public void exit() { - Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0, mNetId, null); - mConnectivityServiceHandler.sendMessage(message); + hideProvisioningNotification(); } } @@ -751,9 +829,7 @@ public class NetworkMonitor extends StateMachine { CMD_LAUNCH_CAPTIVE_PORTAL_APP); } // Display the sign in notification. - Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1, mNetId, - mLaunchCaptivePortalAppBroadcastReceiver.getPendingIntent()); - mConnectivityServiceHandler.sendMessage(message); + showProvisioningNotification(mLaunchCaptivePortalAppBroadcastReceiver.mAction); // Retest for captive portal occasionally. sendMessageDelayed(CMD_CAPTIVE_PORTAL_RECHECK, 0 /* no UID */, CAPTIVE_PORTAL_REEVALUATE_DELAY_MS); @@ -839,12 +915,15 @@ public class NetworkMonitor extends StateMachine { } private void notifyPrivateDnsConfigResolved() { - mConnectivityServiceHandler.sendMessage(obtainMessage( - EVENT_PRIVATE_DNS_CONFIG_RESOLVED, 0, mNetId, mPrivateDnsConfig)); + try { + mCallback.notifyPrivateDnsConfigResolved(mPrivateDnsConfig.toParcel()); + } catch (RemoteException e) { + Log.e(TAG, "Error sending private DNS config resolved notification", e); + } } private void handlePrivateDnsEvaluationFailure() { - notifyNetworkTestResultInvalid(null); + notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, null); // Queue up a re-evaluation with backoff. // @@ -865,7 +944,7 @@ public class NetworkMonitor extends StateMachine { + oneTimeHostnameSuffix; final Stopwatch watch = new Stopwatch().start(); try { - final InetAddress[] ips = mNetworkAgentInfo.network().getAllByName(host); + final InetAddress[] ips = mNonPrivateDnsBypassNetwork.getAllByName(host); final long time = watch.stop(); final String strIps = Arrays.toString(ips); final boolean success = (ips != null && ips.length > 0); @@ -915,12 +994,12 @@ public class NetworkMonitor extends StateMachine { // state (even if no Private DNS validation required). transitionTo(mEvaluatingPrivateDnsState); } else if (probeResult.isPortal()) { - notifyNetworkTestResultInvalid(probeResult.redirectUrl); + notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, probeResult.redirectUrl); mLastPortalProbeResult = probeResult; transitionTo(mCaptivePortalState); } else { logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED); - notifyNetworkTestResultInvalid(probeResult.redirectUrl); + notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, probeResult.redirectUrl); transitionTo(mWaitingForNextProbeState); } return HANDLED; @@ -996,18 +1075,18 @@ public class NetworkMonitor extends StateMachine { } } - public boolean getIsCaptivePortalCheckEnabled() { + private boolean getIsCaptivePortalCheckEnabled() { String symbol = Settings.Global.CAPTIVE_PORTAL_MODE; int defaultValue = Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT; int mode = mDependencies.getSetting(mContext, symbol, defaultValue); return mode != Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE; } - public boolean getUseHttpsValidation() { + private boolean getUseHttpsValidation() { return mDependencies.getSetting(mContext, Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1; } - public boolean getWifiScansAlwaysAvailableDisabled() { + private boolean getWifiScansAlwaysAvailableDisabled() { return mDependencies.getSetting( mContext, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0; } @@ -1040,15 +1119,6 @@ public class NetworkMonitor extends StateMachine { DEFAULT_DATA_STALL_EVALUATION_TYPES); } - // Static for direct access by ConnectivityService - public static String getCaptivePortalServerHttpUrl(Context context) { - return getCaptivePortalServerHttpUrl(Dependencies.DEFAULT, context); - } - - public static String getCaptivePortalServerHttpUrl(Dependencies deps, Context context) { - return deps.getSetting(context, Settings.Global.CAPTIVE_PORTAL_HTTP_URL, DEFAULT_HTTP_URL); - } - private URL[] makeCaptivePortalFallbackUrls() { try { String separator = ","; @@ -1144,7 +1214,7 @@ public class NetworkMonitor extends StateMachine { // 3. PAC scripts are sometimes used to block or restrict Internet access and may in // fact block fetching of the generate_204 URL which would lead to false negative // results for network validation. - final ProxyInfo proxyInfo = mNetworkAgentInfo.linkProperties.getHttpProxy(); + final ProxyInfo proxyInfo = mLinkProperties.getHttpProxy(); if (proxyInfo != null && !Uri.EMPTY.equals(proxyInfo.getPacFileUrl())) { pacUrl = makeURL(proxyInfo.getPacFileUrl().toString()); if (pacUrl == null) { @@ -1416,89 +1486,86 @@ public class NetworkMonitor extends StateMachine { return; } - if (!systemReady) { + if (!mSystemReady) { return; } Intent latencyBroadcast = - new Intent(ConnectivityConstants.ACTION_NETWORK_CONDITIONS_MEASURED); - switch (mNetworkAgentInfo.networkInfo.getType()) { - case ConnectivityManager.TYPE_WIFI: - WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo(); - if (currentWifiInfo != null) { - // NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not - // surrounded by double quotation marks (thus violating the Javadoc), but this - // was changed to match the Javadoc in API 17. Since clients may have started - // sanitizing the output of this method since API 17 was released, we should - // not change it here as it would become impossible to tell whether the SSID is - // simply being surrounded by quotes due to the API, or whether those quotes - // are actually part of the SSID. - latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_SSID, - currentWifiInfo.getSSID()); - latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_BSSID, - currentWifiInfo.getBSSID()); - } else { - if (VDBG) logw("network info is TYPE_WIFI but no ConnectionInfo found"); - return; - } - break; - case ConnectivityManager.TYPE_MOBILE: - latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_NETWORK_TYPE, - mTelephonyManager.getNetworkType()); - List<CellInfo> info = mTelephonyManager.getAllCellInfo(); - if (info == null) return; - int numRegisteredCellInfo = 0; - for (CellInfo cellInfo : info) { - if (cellInfo.isRegistered()) { - numRegisteredCellInfo++; - if (numRegisteredCellInfo > 1) { - if (VDBG) { - logw("more than one registered CellInfo." - + " Can't tell which is active. Bailing."); - } - return; - } - if (cellInfo instanceof CellInfoCdma) { - CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity(); - latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId); - } else if (cellInfo instanceof CellInfoGsm) { - CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity(); - latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId); - } else if (cellInfo instanceof CellInfoLte) { - CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity(); - latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId); - } else if (cellInfo instanceof CellInfoWcdma) { - CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity(); - latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId); - } else { - if (VDBG) logw("Registered cellinfo is unrecognized"); - return; + new Intent(NetworkMonitorUtils.ACTION_NETWORK_CONDITIONS_MEASURED); + if (mNetworkCapabilities.hasTransport(TRANSPORT_WIFI)) { + WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo(); + if (currentWifiInfo != null) { + // NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not + // surrounded by double quotation marks (thus violating the Javadoc), but this + // was changed to match the Javadoc in API 17. Since clients may have started + // sanitizing the output of this method since API 17 was released, we should + // not change it here as it would become impossible to tell whether the SSID is + // simply being surrounded by quotes due to the API, or whether those quotes + // are actually part of the SSID. + latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_SSID, + currentWifiInfo.getSSID()); + latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_BSSID, + currentWifiInfo.getBSSID()); + } else { + if (VDBG) logw("network info is TYPE_WIFI but no ConnectionInfo found"); + return; + } + latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_WIFI); + } else if (mNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { + latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_NETWORK_TYPE, + mTelephonyManager.getNetworkType()); + List<CellInfo> info = mTelephonyManager.getAllCellInfo(); + if (info == null) return; + int numRegisteredCellInfo = 0; + for (CellInfo cellInfo : info) { + if (cellInfo.isRegistered()) { + numRegisteredCellInfo++; + if (numRegisteredCellInfo > 1) { + if (VDBG) { + logw("more than one registered CellInfo." + + " Can't tell which is active. Bailing."); } + return; + } + if (cellInfo instanceof CellInfoCdma) { + CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity(); + latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId); + } else if (cellInfo instanceof CellInfoGsm) { + CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity(); + latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId); + } else if (cellInfo instanceof CellInfoLte) { + CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity(); + latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId); + } else if (cellInfo instanceof CellInfoWcdma) { + CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity(); + latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId); + } else { + if (VDBG) logw("Registered cellinfo is unrecognized"); + return; } } - break; - default: - return; + } + latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_MOBILE); + } else { + return; } - latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CONNECTIVITY_TYPE, - mNetworkAgentInfo.networkInfo.getType()); - latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_RESPONSE_RECEIVED, + latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_RESPONSE_RECEIVED, responseReceived); - latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_REQUEST_TIMESTAMP_MS, + latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_REQUEST_TIMESTAMP_MS, requestTimestampMs); if (responseReceived) { - latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_IS_CAPTIVE_PORTAL, + latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_IS_CAPTIVE_PORTAL, isCaptivePortal); - latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_RESPONSE_TIMESTAMP_MS, + latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_RESPONSE_TIMESTAMP_MS, responseTimestampMs); } mContext.sendBroadcastAsUser(latencyBroadcast, UserHandle.CURRENT, - ConnectivityConstants.PERMISSION_ACCESS_NETWORK_CONDITIONS); + NetworkMonitorUtils.PERMISSION_ACCESS_NETWORK_CONDITIONS); } private void logNetworkEvent(int evtype) { - int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes(); + int[] transports = mNetworkCapabilities.getTransportTypes(); mMetricsLog.log(mNetId, transports, new NetworkEvent(evtype)); } @@ -1520,14 +1587,14 @@ public class NetworkMonitor extends StateMachine { private void maybeLogEvaluationResult(int evtype) { if (mEvaluationTimer.isRunning()) { - int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes(); + int[] transports = mNetworkCapabilities.getTransportTypes(); mMetricsLog.log(mNetId, transports, new NetworkEvent(evtype, mEvaluationTimer.stop())); mEvaluationTimer.reset(); } } private void logValidationProbe(long durationMs, int probeType, int probeResult) { - int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes(); + int[] transports = mNetworkCapabilities.getTransportTypes(); boolean isFirstValidation = validationStage().mIsFirstValidation; ValidationProbeEvent ev = new ValidationProbeEvent(); ev.probeType = ValidationProbeEvent.makeProbeType(probeType, isFirstValidation); @@ -1537,9 +1604,9 @@ public class NetworkMonitor extends StateMachine { } @VisibleForTesting - public static class Dependencies { - public Network getNetwork(NetworkAgentInfo networkAgentInfo) { - return new OneAddressPerFamilyNetwork(networkAgentInfo.network()); + static class Dependencies { + public Network getPrivateDnsBypassNetwork(Network network) { + return new OneAddressPerFamilyNetwork(network); } public Random getRandom() { @@ -1547,6 +1614,13 @@ public class NetworkMonitor extends StateMachine { } /** + * Get the captive portal server HTTP URL that is configured on the device. + */ + public String getCaptivePortalServerHttpUrl(Context context) { + return NetworkMonitorUtils.getCaptivePortalServerHttpUrl(context); + } + + /** * Get the value of a global integer setting. * @param symbol Name of the setting * @param defaultValue Value to return if the setting is not defined. @@ -1666,7 +1740,7 @@ public class NetworkMonitor extends StateMachine { boolean result = false; // Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the // possible traffic cost in metered network. - if (mNetworkAgentInfo.networkCapabilities.isMetered() + if (mNetworkCapabilities.isMetered() && (SystemClock.elapsedRealtime() - getLastProbeTime() < mDataStallMinEvaluateTime)) { return false; diff --git a/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java index 6e07b26e883a..d31fa7732e66 100644 --- a/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java +++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java @@ -16,6 +16,14 @@ package com.android.server.connectivity; +import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN; +import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL; +import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID; +import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; + +import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -24,13 +32,21 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.content.Intent; +import android.net.CaptivePortal; import android.net.ConnectivityManager; +import android.net.INetworkMonitorCallbacks; +import android.net.InetAddresses; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; @@ -38,9 +54,12 @@ import android.net.NetworkInfo; import android.net.NetworkRequest; import android.net.captiveportal.CaptivePortalProbeResult; import android.net.metrics.IpConnectivityLog; +import android.net.util.SharedLog; import android.net.wifi.WifiManager; +import android.os.ConditionVariable; import android.os.Handler; import android.os.SystemClock; +import android.os.UserHandle; import android.provider.Settings; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -50,8 +69,10 @@ import android.util.ArrayMap; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; import java.io.IOException; import java.net.HttpURLConnection; @@ -68,21 +89,23 @@ public class NetworkMonitorTest { private static final String LOCATION_HEADER = "location"; private @Mock Context mContext; - private @Mock Handler mHandler; private @Mock IpConnectivityLog mLogger; - private @Mock NetworkAgentInfo mAgent; - private @Mock NetworkAgentInfo mNotMeteredAgent; + private @Mock SharedLog mValidationLogger; private @Mock NetworkInfo mNetworkInfo; - private @Mock NetworkRequest mRequest; + private @Mock ConnectivityManager mCm; private @Mock TelephonyManager mTelephony; private @Mock WifiManager mWifi; - private @Mock Network mNetwork; private @Mock HttpURLConnection mHttpConnection; private @Mock HttpURLConnection mHttpsConnection; private @Mock HttpURLConnection mFallbackConnection; private @Mock HttpURLConnection mOtherFallbackConnection; private @Mock Random mRandom; private @Mock NetworkMonitor.Dependencies mDependencies; + private @Mock INetworkMonitorCallbacks mCallbacks; + private @Spy Network mNetwork = new Network(TEST_NETID); + private NetworkRequest mRequest; + + private static final int TEST_NETID = 4242; private static final String TEST_HTTP_URL = "http://www.google.com/gen_204"; private static final String TEST_HTTPS_URL = "https://www.google.com/gen_204"; @@ -93,33 +116,37 @@ public class NetworkMonitorTest { private static final int RETURN_CODE_DNS_SUCCESS = 0; private static final int RETURN_CODE_DNS_TIMEOUT = 255; - @Before - public void setUp() throws IOException { - MockitoAnnotations.initMocks(this); - mAgent.linkProperties = new LinkProperties(); - mAgent.networkCapabilities = new NetworkCapabilities() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - mAgent.networkInfo = mNetworkInfo; + private static final int HANDLER_TIMEOUT_MS = 1000; + + private static final LinkProperties TEST_LINKPROPERTIES = new LinkProperties(); + + private static final NetworkCapabilities METERED_CAPABILITIES = new NetworkCapabilities() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET); - mNotMeteredAgent.linkProperties = new LinkProperties(); - mNotMeteredAgent.networkCapabilities = new NetworkCapabilities() + private static final NetworkCapabilities NOT_METERED_CAPABILITIES = new NetworkCapabilities() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET) .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); - mNotMeteredAgent.networkInfo = mNetworkInfo; - when(mAgent.network()).thenReturn(mNetwork); - when(mDependencies.getNetwork(any())).thenReturn(mNetwork); + private static final NetworkCapabilities NO_INTERNET_CAPABILITIES = new NetworkCapabilities() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + + @Before + public void setUp() throws IOException { + MockitoAnnotations.initMocks(this); + when(mDependencies.getPrivateDnsBypassNetwork(any())).thenReturn(mNetwork); when(mDependencies.getRandom()).thenReturn(mRandom); when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())) .thenReturn(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_USE_HTTPS), anyInt())).thenReturn(1); - when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), - anyString())).thenReturn(TEST_HTTP_URL); + when(mDependencies.getCaptivePortalServerHttpUrl(any())).thenReturn(TEST_HTTP_URL); when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTPS_URL), anyString())).thenReturn(TEST_HTTPS_URL); - when(mNetwork.getPrivateDnsBypassingCopy()).thenReturn(mNetwork); + doReturn(mNetwork).when(mNetwork).getPrivateDnsBypassingCopy(); + when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mCm); when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephony); when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifi); @@ -129,7 +156,7 @@ public class NetworkMonitorTest { setFallbackSpecs(null); // Test with no fallback spec by default when(mRandom.nextInt()).thenReturn(0); - when(mNetwork.openConnection(any())).then((invocation) -> { + doAnswer((invocation) -> { URL url = invocation.getArgument(0); switch(url.toString()) { case TEST_HTTP_URL: @@ -144,12 +171,20 @@ public class NetworkMonitorTest { fail("URL not mocked: " + url.toString()); return null; } - }); + }).when(mNetwork).openConnection(any()); when(mHttpConnection.getRequestProperties()).thenReturn(new ArrayMap<>()); when(mHttpsConnection.getRequestProperties()).thenReturn(new ArrayMap<>()); - when(mNetwork.getAllByName(any())).thenReturn(new InetAddress[] { - InetAddress.parseNumericAddress("192.168.0.0") - }); + doReturn(new InetAddress[] { + InetAddresses.parseNumericAddress("192.168.0.0") + }).when(mNetwork).getAllByName(any()); + + mRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_RESTRICTED) + .build(); + // Default values. Individual tests can override these. + when(mCm.getLinkProperties(any())).thenReturn(TEST_LINKPROPERTIES); + when(mCm.getNetworkCapabilities(any())).thenReturn(METERED_CAPABILITIES); setMinDataStallEvaluateInterval(500); setDataStallEvaluationType(1 << DATA_STALL_EVALUATION_TYPE_DNS); @@ -160,10 +195,10 @@ public class NetworkMonitorTest { private class WrappedNetworkMonitor extends NetworkMonitor { private long mProbeTime = 0; - WrappedNetworkMonitor(Context context, Handler handler, - NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest, + WrappedNetworkMonitor(Context context, Network network, NetworkRequest defaultRequest, IpConnectivityLog logger, Dependencies deps) { - super(context, handler, networkAgentInfo, defaultRequest, logger, deps); + super(context, mCallbacks, network, defaultRequest, logger, + new SharedLog("test_nm"), deps); } @Override @@ -176,19 +211,39 @@ public class NetworkMonitorTest { } } - WrappedNetworkMonitor makeMeteredWrappedNetworkMonitor() { - return new WrappedNetworkMonitor( - mContext, mHandler, mAgent, mRequest, mLogger, mDependencies); + private WrappedNetworkMonitor makeMeteredWrappedNetworkMonitor() { + final WrappedNetworkMonitor nm = new WrappedNetworkMonitor( + mContext, mNetwork, mRequest, mLogger, mDependencies); + when(mCm.getNetworkCapabilities(any())).thenReturn(METERED_CAPABILITIES); + nm.start(); + waitForIdle(nm.getHandler()); + return nm; } - WrappedNetworkMonitor makeNotMeteredWrappedNetworkMonitor() { - return new WrappedNetworkMonitor( - mContext, mHandler, mNotMeteredAgent, mRequest, mLogger, mDependencies); + private WrappedNetworkMonitor makeNotMeteredWrappedNetworkMonitor() { + final WrappedNetworkMonitor nm = new WrappedNetworkMonitor( + mContext, mNetwork, mRequest, mLogger, mDependencies); + when(mCm.getNetworkCapabilities(any())).thenReturn(NOT_METERED_CAPABILITIES); + nm.start(); + waitForIdle(nm.getHandler()); + return nm; } - NetworkMonitor makeMonitor() { - return new NetworkMonitor( - mContext, mHandler, mAgent, mRequest, mLogger, mDependencies); + private NetworkMonitor makeMonitor() { + final NetworkMonitor nm = new NetworkMonitor( + mContext, mCallbacks, mNetwork, mRequest, mLogger, mValidationLogger, + mDependencies); + nm.start(); + waitForIdle(nm.getHandler()); + return nm; + } + + private void waitForIdle(Handler handler) { + final ConditionVariable cv = new ConditionVariable(false); + handler.post(cv::open); + if (!cv.block(HANDLER_TIMEOUT_MS)) { + fail("Timed out waiting for handler"); + } } @Test @@ -319,6 +374,15 @@ public class NetworkMonitorTest { } @Test + public void testIsCaptivePortal_IgnorePortals() throws IOException { + setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE); + setSslException(mHttpsConnection); + setPortal302(mHttpConnection); + + assertNotPortal(makeMonitor().isCaptivePortal()); + } + + @Test public void testIsDataStall_EvaluationDisabled() { setDataStallEvaluationType(0); WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor(); @@ -390,6 +454,63 @@ public class NetworkMonitorTest { assertFalse(wrappedMonitor.isDataStall()); } + @Test + public void testBrokenNetworkNotValidated() throws Exception { + setSslException(mHttpsConnection); + setStatus(mHttpConnection, 500); + setStatus(mFallbackConnection, 404); + when(mCm.getNetworkCapabilities(any())).thenReturn(METERED_CAPABILITIES); + + final NetworkMonitor nm = makeMonitor(); + nm.notifyNetworkConnected(); + + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) + .notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, null); + } + + @Test + public void testNoInternetCapabilityValidated() throws Exception { + when(mCm.getNetworkCapabilities(any())).thenReturn(NO_INTERNET_CAPABILITIES); + + final NetworkMonitor nm = makeMonitor(); + nm.notifyNetworkConnected(); + + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) + .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null); + verify(mNetwork, never()).openConnection(any()); + } + + @Test + public void testLaunchCaptivePortalApp() throws Exception { + setSslException(mHttpsConnection); + setPortal302(mHttpConnection); + + final NetworkMonitor nm = makeMonitor(); + nm.notifyNetworkConnected(); + + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) + .showProvisioningNotification(any()); + + // Check that startCaptivePortalApp sends the expected intent. + nm.launchCaptivePortalApp(); + + final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mContext, timeout(HANDLER_TIMEOUT_MS).times(1)) + .startActivityAsUser(intentCaptor.capture(), eq(UserHandle.CURRENT)); + final Intent intent = intentCaptor.getValue(); + assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, intent.getAction()); + final Network network = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK); + assertEquals(TEST_NETID, network.netId); + + // Have the app report that the captive portal is dismissed, and check that we revalidate. + setStatus(mHttpsConnection, 204); + setStatus(mHttpConnection, 204); + final CaptivePortal captivePortal = intent.getParcelableExtra(EXTRA_CAPTIVE_PORTAL); + captivePortal.reportCaptivePortalDismissed(); + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) + .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null); + } + private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) { for (int i = 0; i < count; i++) { wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount( @@ -440,6 +561,11 @@ public class NetworkMonitorTest { eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs); } + private void setCaptivePortalMode(int mode) { + when(mDependencies.getSetting(any(), + eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())).thenReturn(mode); + } + private void assertPortal(CaptivePortalProbeResult result) { assertTrue(result.isPortal()); assertFalse(result.isFailed()); @@ -459,12 +585,12 @@ public class NetworkMonitorTest { } private void setSslException(HttpURLConnection connection) throws IOException { - when(connection.getResponseCode()).thenThrow(new SSLHandshakeException("Invalid cert")); + doThrow(new SSLHandshakeException("Invalid cert")).when(connection).getResponseCode(); } private void set302(HttpURLConnection connection, String location) throws IOException { setStatus(connection, 302); - when(connection.getHeaderField(LOCATION_HEADER)).thenReturn(location); + doReturn(location).when(connection).getHeaderField(LOCATION_HEADER); } private void setPortal302(HttpURLConnection connection) throws IOException { @@ -472,7 +598,7 @@ public class NetworkMonitorTest { } private void setStatus(HttpURLConnection connection, int status) throws IOException { - when(connection.getResponseCode()).thenReturn(status); + doReturn(status).when(connection).getResponseCode(); } } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 66ceae432c6e..d0666b98c0e0 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_NONE; 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_VALID; import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; @@ -37,6 +38,8 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkPolicyManager.RULE_NONE; import static android.net.NetworkPolicyManager.uidRulesToString; +import static android.net.NetworkStack.NETWORKSTACK_PACKAGE_NAME; +import static android.net.shared.NetworkMonitorUtils.isValidationRequired; import static android.os.Process.INVALID_UID; import static android.system.OsConstants.IPPROTO_TCP; import static android.system.OsConstants.IPPROTO_UDP; @@ -62,6 +65,8 @@ import android.net.IIpConnectivityMetrics; import android.net.INetd; import android.net.INetdEventCallback; import android.net.INetworkManagementEventObserver; +import android.net.INetworkMonitor; +import android.net.INetworkMonitorCallbacks; import android.net.INetworkPolicyListener; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; @@ -79,9 +84,11 @@ import android.net.NetworkPolicyManager; import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; import android.net.NetworkSpecifier; +import android.net.NetworkStack; import android.net.NetworkState; import android.net.NetworkUtils; import android.net.NetworkWatchlistManager; +import android.net.PrivateDnsConfigParcel; import android.net.ProxyInfo; import android.net.RouteInfo; import android.net.UidRange; @@ -90,12 +97,13 @@ import android.net.VpnService; import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; import android.net.netlink.InetDiagMessage; +import android.net.shared.NetworkMonitorUtils; +import android.net.shared.PrivateDnsConfig; import android.net.util.MultinetworkPolicyTracker; import android.net.util.NetdService; import android.os.Binder; import android.os.Build; import android.os.Bundle; -import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -123,8 +131,8 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArraySet; import android.util.LocalLog; -import android.util.LocalLog.ReadOnlyLocalLog; import android.util.Log; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -149,7 +157,6 @@ import com.android.internal.util.XmlUtils; import com.android.server.am.BatteryStatsService; import com.android.server.connectivity.DataConnectionStats; import com.android.server.connectivity.DnsManager; -import com.android.server.connectivity.DnsManager.PrivateDnsConfig; import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate; import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.connectivity.KeepaliveTracker; @@ -158,7 +165,6 @@ import com.android.server.connectivity.MockableSystemProperties; import com.android.server.connectivity.MultipathPolicyTracker; import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkDiagnostics; -import com.android.server.connectivity.NetworkMonitor; import com.android.server.connectivity.NetworkNotificationManager; import com.android.server.connectivity.NetworkNotificationManager.NotificationType; import com.android.server.connectivity.PermissionMonitor; @@ -186,7 +192,6 @@ import java.io.PrintWriter; import java.net.Inet4Address; import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -442,6 +447,43 @@ public class ConnectivityService extends IConnectivityManager.Stub */ private static final int EVENT_DATA_SAVER_CHANGED = 40; + /** + * Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the network has + * been tested. + * obj = String representing URL that Internet probe was redirect to, if it was redirected. + * arg1 = One of the NETWORK_TESTED_RESULT_* constants. + * arg2 = NetID. + */ + public static final int EVENT_NETWORK_TESTED = 41; + + /** + * Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the private DNS + * config was resolved. + * obj = PrivateDnsConfig + * arg2 = netid + */ + public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = 42; + + /** + * Request ConnectivityService display provisioning notification. + * arg1 = Whether to make the notification visible. + * arg2 = NetID. + * obj = Intent to be launched when notification selected by user, null if !arg1. + */ + public static final int EVENT_PROVISIONING_NOTIFICATION = 43; + + /** + * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification + * should be shown. + */ + public static final int PROVISIONING_NOTIFICATION_SHOW = 1; + + /** + * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification + * should be hidden. + */ + public static final int PROVISIONING_NOTIFICATION_HIDE = 0; + private static String eventName(int what) { return sMagicDecoderRing.get(what, Integer.toString(what)); } @@ -506,30 +548,6 @@ public class ConnectivityService extends IConnectivityManager.Stub private long mMaxWakelockDurationMs = 0; private long mLastWakeLockAcquireTimestamp = 0; - // Array of <Network,ReadOnlyLocalLogs> tracking network validation and results - private static final int MAX_VALIDATION_LOGS = 10; - private static class ValidationLog { - final Network mNetwork; - final String mName; - final ReadOnlyLocalLog mLog; - - ValidationLog(Network network, String name, ReadOnlyLocalLog log) { - mNetwork = network; - mName = name; - mLog = log; - } - } - private final ArrayDeque<ValidationLog> mValidationLogs = new ArrayDeque<>(MAX_VALIDATION_LOGS); - - private void addValidationLogs(ReadOnlyLocalLog log, Network network, String name) { - synchronized (mValidationLogs) { - while (mValidationLogs.size() >= MAX_VALIDATION_LOGS) { - mValidationLogs.removeLast(); - } - mValidationLogs.addFirst(new ValidationLog(network, name, log)); - } - } - private final IpConnectivityLog mMetricsLog; @GuardedBy("mBandwidthRequests") @@ -1684,7 +1702,11 @@ public class ConnectivityService extends IConnectivityManager.Stub // caller type. Need to re-factor NetdEventListenerService to allow multiple // NetworkMonitor registrants. if (nai != null && nai.satisfies(mDefaultRequest)) { - nai.networkMonitor.sendMessage(NetworkMonitor.EVENT_DNS_NOTIFICATION, returnCode); + try { + nai.networkMonitor().notifyDnsResponse(returnCode); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } } }; @@ -2266,17 +2288,6 @@ public class ConnectivityService extends IConnectivityManager.Stub if (ArrayUtils.contains(args, SHORT_ARG) == false) { pw.println(); - synchronized (mValidationLogs) { - pw.println("mValidationLogs (most recent first):"); - for (ValidationLog p : mValidationLogs) { - pw.println(p.mNetwork + " - " + p.mName); - pw.increaseIndent(); - p.mLog.dump(fd, pw, args); - pw.decreaseIndent(); - } - } - - pw.println(); pw.println("mNetworkRequestInfoLogs (most recent first):"); pw.increaseIndent(); mNetworkRequestInfoLogs.reverseDump(fd, pw, args); @@ -2455,11 +2466,11 @@ public class ConnectivityService extends IConnectivityManager.Stub switch (msg.what) { default: return false; - case NetworkMonitor.EVENT_NETWORK_TESTED: { + case EVENT_NETWORK_TESTED: { final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2); if (nai == null) break; - final boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID); + final boolean valid = (msg.arg1 == NETWORK_TEST_RESULT_VALID); final boolean wasValidated = nai.lastValidated; final boolean wasDefault = isDefaultNetwork(nai); @@ -2497,7 +2508,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } break; } - case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: { + case EVENT_PROVISIONING_NOTIFICATION: { final int netId = msg.arg2; final boolean visible = toBool(msg.arg1); final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId); @@ -2530,7 +2541,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } break; } - case NetworkMonitor.EVENT_PRIVATE_DNS_CONFIG_RESOLVED: { + case EVENT_PRIVATE_DNS_CONFIG_RESOLVED: { final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2); if (nai == null) break; @@ -2572,8 +2583,61 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private class NetworkMonitorCallbacks extends INetworkMonitorCallbacks.Stub { + private final NetworkAgentInfo mNai; + + private NetworkMonitorCallbacks(NetworkAgentInfo nai) { + mNai = nai; + } + + @Override + public void onNetworkMonitorCreated(INetworkMonitor networkMonitor) { + mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, + new Pair<>(mNai, networkMonitor))); + } + + @Override + public void notifyNetworkTested(int testResult, @Nullable String redirectUrl) { + mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(EVENT_NETWORK_TESTED, + testResult, mNai.network.netId, redirectUrl)); + } + + @Override + public void notifyPrivateDnsConfigResolved(PrivateDnsConfigParcel config) { + mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage( + EVENT_PRIVATE_DNS_CONFIG_RESOLVED, + 0, mNai.network.netId, PrivateDnsConfig.fromParcel(config))); + } + + @Override + public void showProvisioningNotification(String action) { + final Intent intent = new Intent(action); + intent.setPackage(NETWORKSTACK_PACKAGE_NAME); + + final PendingIntent pendingIntent; + // Only the system server can register notifications with package "android" + final long token = Binder.clearCallingIdentity(); + try { + pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); + } finally { + Binder.restoreCallingIdentity(token); + } + mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage( + EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_SHOW, + mNai.network.netId, + pendingIntent)); + } + + @Override + public void hideProvisioningNotification() { + mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage( + EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE, + mNai.network.netId)); + } + } + private boolean networkRequiresValidation(NetworkAgentInfo nai) { - return NetworkMonitor.isValidationRequired( + return isValidationRequired( mDefaultRequest.networkCapabilities, nai.networkCapabilities); } @@ -2603,10 +2667,14 @@ public class ConnectivityService extends IConnectivityManager.Stub // Internet access and therefore also require validation. if (!networkRequiresValidation(nai)) return; - // Notify the NetworkMonitor thread in case it needs to cancel or + // Notify the NetworkAgentInfo/NetworkMonitor in case NetworkMonitor needs to cancel or // schedule DNS resolutions. If a DNS resolution is required the // result will be sent back to us. - nai.networkMonitor.notifyPrivateDnsSettingsChanged(cfg); + try { + nai.networkMonitor().notifyPrivateDnsChanged(cfg.toParcel()); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } // With Private DNS bypass support, we can proceed to update the // Private DNS config immediately, even if we're in strict mode @@ -2736,7 +2804,11 @@ public class ConnectivityService extends IConnectivityManager.Stub // Disable wakeup packet monitoring for each interface. wakeupModifyInterface(iface, nai.networkCapabilities, false); } - nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED); + try { + nai.networkMonitor().notifyNetworkDisconnected(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } mNetworkAgentInfos.remove(nai.messenger); nai.maybeStopClat(); synchronized (mNetworkForNetId) { @@ -3096,7 +3168,11 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); if (nai == null) return; if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) return; - nai.networkMonitor.sendMessage(NetworkMonitor.CMD_LAUNCH_CAPTIVE_PORTAL_APP); + try { + nai.networkMonitor().launchCaptivePortalApp(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } }); } @@ -3217,6 +3293,11 @@ public class ConnectivityService extends IConnectivityManager.Stub return mMultinetworkPolicyTracker.getMeteredMultipathPreference(); } + @Override + public NetworkRequest getDefaultRequest() { + return mDefaultRequest; + } + private class InternalHandler extends Handler { public InternalHandler(Looper looper) { super(looper); @@ -3247,7 +3328,9 @@ public class ConnectivityService extends IConnectivityManager.Stub break; } case EVENT_REGISTER_NETWORK_AGENT: { - handleRegisterNetworkAgent((NetworkAgentInfo)msg.obj); + final Pair<NetworkAgentInfo, INetworkMonitor> arg = + (Pair<NetworkAgentInfo, INetworkMonitor>) msg.obj; + handleRegisterNetworkAgent(arg.first, arg.second); break; } case EVENT_REGISTER_NETWORK_REQUEST: @@ -3305,7 +3388,14 @@ public class ConnectivityService extends IConnectivityManager.Stub } case EVENT_SYSTEM_READY: { for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { - nai.networkMonitor.systemReady = true; + // Might have been called already in handleRegisterNetworkAgent since + // mSystemReady is set before sending EVENT_SYSTEM_READY, but calling + // this several times is fine. + try { + nai.networkMonitor().notifySystemReady(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } mMultipathPolicyTracker.start(); break; @@ -3577,7 +3667,11 @@ public class ConnectivityService extends IConnectivityManager.Stub if (isNetworkWithLinkPropertiesBlocked(lp, uid, false)) { return; } - nai.networkMonitor.forceReevaluation(uid); + try { + nai.networkMonitor().forceReevaluation(uid); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } @Override @@ -4785,27 +4879,49 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), new Network(reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore, - mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest, this); + mContext, mTrackerHandler, new NetworkMisc(networkMisc), this); // Make sure the network capabilities reflect what the agent info says. nai.networkCapabilities = mixInCapabilities(nai, nc); - synchronized (this) { - nai.networkMonitor.systemReady = mSystemReady; - } final String extraInfo = networkInfo.getExtraInfo(); final String name = TextUtils.isEmpty(extraInfo) ? nai.networkCapabilities.getSSID() : extraInfo; - addValidationLogs(nai.networkMonitor.getValidationLogs(), nai.network, name); if (DBG) log("registerNetworkAgent " + nai); - mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai)); + final long token = Binder.clearCallingIdentity(); + try { + mContext.getSystemService(NetworkStack.class) + .makeNetworkMonitor(nai.network, name, new NetworkMonitorCallbacks(nai)); + } finally { + Binder.restoreCallingIdentity(token); + } + // NetworkAgentInfo registration will finish when the NetworkMonitor is created. + // If the network disconnects or sends any other event before that, messages are deferred by + // NetworkAgent until nai.asyncChannel.connect(), which will be called when finalizing the + // registration. return nai.network.netId; } - private void handleRegisterNetworkAgent(NetworkAgentInfo nai) { + private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) { + nai.onNetworkMonitorCreated(networkMonitor); if (VDBG) log("Got NetworkAgent Messenger"); mNetworkAgentInfos.put(nai.messenger, nai); synchronized (mNetworkForNetId) { mNetworkForNetId.put(nai.network.netId, nai); } + synchronized (this) { + if (mSystemReady) { + try { + networkMonitor.notifySystemReady(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + } + + try { + networkMonitor.start(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } nai.asyncChannel.connect(mContext, mTrackerHandler, nai.messenger); NetworkInfo networkInfo = nai.networkInfo; nai.networkInfo = null; @@ -4855,6 +4971,11 @@ public class ConnectivityService extends IConnectivityManager.Stub networkAgent.updateClat(mNMS); notifyIfacesChangedForNetworkStats(); if (networkAgent.everConnected) { + try { + networkAgent.networkMonitor().notifyLinkPropertiesChanged(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED); } } @@ -5092,6 +5213,11 @@ public class ConnectivityService extends IConnectivityManager.Stub // If the requestable capabilities have changed or the score changed, we can't have been // called by rematchNetworkAndRequests, so it's safe to start a rematch. rematchAllNetworksAndRequests(nai, oldScore); + try { + nai.networkMonitor().notifyNetworkCapabilitiesChanged(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED); } @@ -5339,6 +5465,11 @@ public class ConnectivityService extends IConnectivityManager.Stub } if (capabilitiesChanged) { + try { + nai.networkMonitor().notifyNetworkCapabilitiesChanged(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED); } @@ -5739,7 +5870,15 @@ public class ConnectivityService extends IConnectivityManager.Stub updateLinkProperties(networkAgent, new LinkProperties(networkAgent.linkProperties), null); - networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED); + // Until parceled LinkProperties are sent directly to NetworkMonitor, the connect + // command must be sent after updating LinkProperties to maximize chances of + // NetworkMonitor seeing the correct LinkProperties when starting. + // TODO: pass LinkProperties to the NetworkMonitor in the notifyNetworkConnected call. + try { + networkAgent.networkMonitor().notifyNetworkConnected(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } scheduleUnvalidatedPrompt(networkAgent); if (networkAgent.isVPN()) { @@ -6020,7 +6159,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public String getCaptivePortalServerUrl() { enforceConnectivityInternalPermission(); - return NetworkMonitor.getCaptivePortalServerHttpUrl(mContext); + return NetworkMonitorUtils.getCaptivePortalServerHttpUrl(mContext); } @Override @@ -6113,12 +6252,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } @VisibleForTesting - public NetworkMonitor createNetworkMonitor(Context context, Handler handler, - NetworkAgentInfo nai, NetworkRequest defaultRequest) { - return new NetworkMonitor(context, handler, nai, defaultRequest); - } - - @VisibleForTesting MultinetworkPolicyTracker createMultinetworkPolicyTracker(Context c, Handler h, Runnable r) { return new MultinetworkPolicyTracker(c, h, r); } diff --git a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java index 24865bcd9a09..6fa98b8e8ad7 100644 --- a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java +++ b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java @@ -21,22 +21,6 @@ package com.android.server.connectivity; * @hide */ public class ConnectivityConstants { - // IPC constants - public static final String ACTION_NETWORK_CONDITIONS_MEASURED = - "android.net.conn.NETWORK_CONDITIONS_MEASURED"; - public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type"; - public static final String EXTRA_NETWORK_TYPE = "extra_network_type"; - public static final String EXTRA_RESPONSE_RECEIVED = "extra_response_received"; - public static final String EXTRA_IS_CAPTIVE_PORTAL = "extra_is_captive_portal"; - public static final String EXTRA_CELL_ID = "extra_cellid"; - public static final String EXTRA_SSID = "extra_ssid"; - public static final String EXTRA_BSSID = "extra_bssid"; - /** real time since boot */ - public static final String EXTRA_REQUEST_TIMESTAMP_MS = "extra_request_timestamp_ms"; - public static final String EXTRA_RESPONSE_TIMESTAMP_MS = "extra_response_timestamp_ms"; - - public static final String PERMISSION_ACCESS_NETWORK_CONDITIONS = - "android.permission.ACCESS_NETWORK_CONDITIONS"; // Penalty applied to scores of Networks that have not been validated. public static final int UNVALIDATED_SCORE_PENALTY = 40; diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java index b8f057db290a..d8bb635f2ce8 100644 --- a/services/core/java/com/android/server/connectivity/DnsManager.java +++ b/services/core/java/com/android/server/connectivity/DnsManager.java @@ -18,10 +18,9 @@ package com.android.server.connectivity; import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE_FALLBACK; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF; -import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; -import static android.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES; import static android.provider.Settings.Global.DNS_RESOLVER_MAX_SAMPLES; +import static android.provider.Settings.Global.DNS_RESOLVER_MIN_SAMPLES; import static android.provider.Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS; import static android.provider.Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT; import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE; @@ -35,6 +34,7 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkUtils; import android.net.Uri; +import android.net.shared.PrivateDnsConfig; import android.os.Binder; import android.os.INetworkManagementService; import android.os.UserHandle; @@ -43,10 +43,7 @@ import android.text.TextUtils; import android.util.Pair; import android.util.Slog; -import com.android.server.connectivity.MockableSystemProperties; - import java.net.InetAddress; -import java.net.UnknownHostException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -54,10 +51,8 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; import java.util.Set; -import java.util.StringJoiner; +import java.util.stream.Collectors; /** @@ -123,43 +118,6 @@ public class DnsManager { private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8; private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64; - public static class PrivateDnsConfig { - public final boolean useTls; - public final String hostname; - public final InetAddress[] ips; - - public PrivateDnsConfig() { - this(false); - } - - public PrivateDnsConfig(boolean useTls) { - this.useTls = useTls; - this.hostname = ""; - this.ips = new InetAddress[0]; - } - - public PrivateDnsConfig(String hostname, InetAddress[] ips) { - this.useTls = !TextUtils.isEmpty(hostname); - this.hostname = useTls ? hostname : ""; - this.ips = (ips != null) ? ips : new InetAddress[0]; - } - - public PrivateDnsConfig(PrivateDnsConfig cfg) { - useTls = cfg.useTls; - hostname = cfg.hostname; - ips = cfg.ips; - } - - public boolean inStrictMode() { - return useTls && !TextUtils.isEmpty(hostname); - } - - public String toString() { - return PrivateDnsConfig.class.getSimpleName() + - "{" + useTls + ":" + hostname + "/" + Arrays.toString(ips) + "}"; - } - } - public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) { final String mode = getPrivateDnsMode(cr); diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 262184b0b12d..54c89aa04111 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -16,9 +16,8 @@ package com.android.server.connectivity; -import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; - import android.content.Context; +import android.net.INetworkMonitor; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; @@ -29,7 +28,6 @@ import android.net.NetworkState; import android.os.Handler; import android.os.INetworkManagementService; import android.os.Messenger; -import android.os.RemoteException; import android.os.SystemClock; import android.util.Log; import android.util.SparseArray; @@ -37,11 +35,8 @@ import android.util.SparseArray; import com.android.internal.util.AsyncChannel; import com.android.internal.util.WakeupMessage; import com.android.server.ConnectivityService; -import com.android.server.connectivity.NetworkMonitor; import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Comparator; import java.util.Objects; import java.util.SortedSet; import java.util.TreeSet; @@ -126,7 +121,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { public LinkProperties linkProperties; // This should only be modified via ConnectivityService.updateCapabilities(). public NetworkCapabilities networkCapabilities; - public final NetworkMonitor networkMonitor; public final NetworkMisc networkMisc; // Indicates if netd has been told to create this Network. From this point on the appropriate // routing rules are setup and routes are added so packets can begin flowing over the Network. @@ -239,6 +233,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // Used by ConnectivityService to keep track of 464xlat. public Nat464Xlat clatd; + // Set after asynchronous creation of the NetworkMonitor. + private volatile INetworkMonitor mNetworkMonitor; + private static final String TAG = ConnectivityService.class.getSimpleName(); private static final boolean VDBG = false; private final ConnectivityService mConnService; @@ -247,7 +244,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info, LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, - NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) { + NetworkMisc misc, ConnectivityService connService) { this.messenger = messenger; asyncChannel = ac; network = net; @@ -258,10 +255,16 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { mConnService = connService; mContext = context; mHandler = handler; - networkMonitor = mConnService.createNetworkMonitor(context, handler, this, defaultRequest); networkMisc = misc; } + /** + * Inform NetworkAgentInfo that a new NetworkMonitor was created. + */ + public void onNetworkMonitorCreated(INetworkMonitor networkMonitor) { + mNetworkMonitor = networkMonitor; + } + public ConnectivityService connService() { return mConnService; } @@ -278,6 +281,15 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { return network; } + /** + * Get the INetworkMonitor in this NetworkAgentInfo. + * + * <p>This will be null before {@link #onNetworkMonitorCreated(INetworkMonitor)} is called. + */ + public INetworkMonitor networkMonitor() { + return mNetworkMonitor; + } + // Functions for manipulating the requests satisfied by this network. // // These functions must only called on ConnectivityService's main thread. diff --git a/services/net/Android.bp b/services/net/Android.bp index ae697b7f093a..3b4d6a75591f 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -11,10 +11,10 @@ java_library { ] } -// TODO: move to networking module with IpNeighborMonitor/ConnectivityPacketTracker and remove lib -java_library { - name: "frameworks-net-shared-utils", +filegroup { + name: "services-networkstack-shared-srcs", srcs: [ - "java/android/net/util/FdEventsReader.java", + "java/android/net/util/FdEventsReader.java", // TODO: move to NetworkStack with IpClient + "java/android/net/shared/*.java", ] -}
\ No newline at end of file +} diff --git a/services/net/java/android/net/shared/NetworkMonitorUtils.java b/services/net/java/android/net/shared/NetworkMonitorUtils.java new file mode 100644 index 000000000000..463cf2af2897 --- /dev/null +++ b/services/net/java/android/net/shared/NetworkMonitorUtils.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2018 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.shared; + +import android.content.Context; +import android.net.NetworkCapabilities; +import android.provider.Settings; + +/** @hide */ +public class NetworkMonitorUtils { + + // Network conditions broadcast constants + public static final String ACTION_NETWORK_CONDITIONS_MEASURED = + "android.net.conn.NETWORK_CONDITIONS_MEASURED"; + public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type"; + public static final String EXTRA_NETWORK_TYPE = "extra_network_type"; + public static final String EXTRA_RESPONSE_RECEIVED = "extra_response_received"; + public static final String EXTRA_IS_CAPTIVE_PORTAL = "extra_is_captive_portal"; + public static final String EXTRA_CELL_ID = "extra_cellid"; + public static final String EXTRA_SSID = "extra_ssid"; + public static final String EXTRA_BSSID = "extra_bssid"; + /** real time since boot */ + public static final String EXTRA_REQUEST_TIMESTAMP_MS = "extra_request_timestamp_ms"; + public static final String EXTRA_RESPONSE_TIMESTAMP_MS = "extra_response_timestamp_ms"; + public static final String PERMISSION_ACCESS_NETWORK_CONDITIONS = + "android.permission.ACCESS_NETWORK_CONDITIONS"; + + // TODO: once the URL is a resource overlay, remove and have the resource define the default + private static final String DEFAULT_HTTP_URL = + "http://connectivitycheck.gstatic.com/generate_204"; + + /** + * Get the captive portal server HTTP URL that is configured on the device. + */ + public static String getCaptivePortalServerHttpUrl(Context context) { + final String settingUrl = Settings.Global.getString( + context.getContentResolver(), + Settings.Global.CAPTIVE_PORTAL_HTTP_URL); + return settingUrl != null ? settingUrl : DEFAULT_HTTP_URL; + } + + /** + * Return whether validation is required for a network. + * @param dfltNetCap Default requested network capabilities. + * @param nc Network capabilities of the network to test. + */ + public static boolean isValidationRequired( + NetworkCapabilities dfltNetCap, NetworkCapabilities nc) { + // TODO: Consider requiring validation for DUN networks. + return dfltNetCap.satisfiedByNetworkCapabilities(nc); + } +} diff --git a/services/net/java/android/net/shared/PrivateDnsConfig.java b/services/net/java/android/net/shared/PrivateDnsConfig.java new file mode 100644 index 000000000000..41e0bad7d746 --- /dev/null +++ b/services/net/java/android/net/shared/PrivateDnsConfig.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018 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.shared; + +import android.net.InetAddresses; +import android.net.PrivateDnsConfigParcel; +import android.text.TextUtils; + +import java.net.InetAddress; +import java.util.Arrays; + +/** @hide */ +public class PrivateDnsConfig { + public final boolean useTls; + public final String hostname; + public final InetAddress[] ips; + + public PrivateDnsConfig() { + this(false); + } + + public PrivateDnsConfig(boolean useTls) { + this.useTls = useTls; + this.hostname = ""; + this.ips = new InetAddress[0]; + } + + public PrivateDnsConfig(String hostname, InetAddress[] ips) { + this.useTls = !TextUtils.isEmpty(hostname); + this.hostname = useTls ? hostname : ""; + this.ips = (ips != null) ? ips : new InetAddress[0]; + } + + public PrivateDnsConfig(PrivateDnsConfig cfg) { + useTls = cfg.useTls; + hostname = cfg.hostname; + ips = cfg.ips; + } + + /** + * Indicates whether this is a strict mode private DNS configuration. + */ + public boolean inStrictMode() { + return useTls && !TextUtils.isEmpty(hostname); + } + + @Override + public String toString() { + return PrivateDnsConfig.class.getSimpleName() + + "{" + useTls + ":" + hostname + "/" + Arrays.toString(ips) + "}"; + } + + /** + * Create a stable AIDL-compatible parcel from the current instance. + */ + public PrivateDnsConfigParcel toParcel() { + final PrivateDnsConfigParcel parcel = new PrivateDnsConfigParcel(); + parcel.hostname = hostname; + + final String[] parceledIps = new String[ips.length]; + for (int i = 0; i < ips.length; i++) { + parceledIps[i] = ips[i].getHostAddress(); + } + parcel.ips = parceledIps; + + return parcel; + } + + /** + * Build a configuration from a stable AIDL-compatible parcel. + */ + public static PrivateDnsConfig fromParcel(PrivateDnsConfigParcel parcel) { + final InetAddress[] ips = new InetAddress[parcel.ips.length]; + for (int i = 0; i < ips.length; i++) { + ips[i] = InetAddresses.parseNumericAddress(parcel.ips[i]); + } + + return new PrivateDnsConfig(parcel.hostname, ips); + } +} diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java index 8b7b59d20978..2cdb2b04f8f7 100644 --- a/services/net/java/android/net/util/SharedLog.java +++ b/services/net/java/android/net/util/SharedLog.java @@ -70,6 +70,10 @@ public class SharedLog { mComponent = component; } + public String getTag() { + return mTag; + } + /** * Create a SharedLog based on this log with an additional component prefix on each logged line. */ diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 71529fdffd5f..bf3964416e11 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -26,6 +26,8 @@ import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; 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_VALID; import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; @@ -69,17 +71,19 @@ import static org.junit.Assert.fail; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; - import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -89,7 +93,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; -import android.net.CaptivePortal; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.ConnectivityManager.PacketKeepalive; @@ -97,6 +100,8 @@ import android.net.ConnectivityManager.PacketKeepaliveCallback; import android.net.ConnectivityManager.TooManyRequestsException; import android.net.ConnectivityThread; import android.net.INetd; +import android.net.INetworkMonitor; +import android.net.INetworkMonitorCallbacks; import android.net.INetworkPolicyListener; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; @@ -114,12 +119,14 @@ import android.net.NetworkInfo.DetailedState; import android.net.NetworkMisc; import android.net.NetworkRequest; import android.net.NetworkSpecifier; +import android.net.NetworkStack; import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.StringNetworkSpecifier; import android.net.UidRange; -import android.net.captiveportal.CaptivePortalProbeResult; import android.net.metrics.IpConnectivityLog; +import android.net.shared.NetworkMonitorUtils; +import android.net.shared.PrivateDnsConfig; import android.net.util.MultinetworkPolicyTracker; import android.os.ConditionVariable; import android.os.Handler; @@ -148,12 +155,9 @@ import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.connectivity.ConnectivityConstants; import com.android.server.connectivity.DefaultNetworkMetrics; -import com.android.server.connectivity.DnsManager; import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.connectivity.MockableSystemProperties; import com.android.server.connectivity.Nat464Xlat; -import com.android.server.connectivity.NetworkAgentInfo; -import com.android.server.connectivity.NetworkMonitor; import com.android.server.connectivity.Tethering; import com.android.server.connectivity.Vpn; import com.android.server.net.NetworkPinner; @@ -168,6 +172,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; +import org.mockito.stubbing.Answer; import java.net.Inet4Address; import java.net.InetAddress; @@ -230,6 +235,7 @@ public class ConnectivityServiceTest { @Mock INetworkStatsService mStatsService; @Mock INetworkPolicyManager mNpm; @Mock INetd mMockNetd; + @Mock NetworkStack mNetworkStack; private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class); @@ -299,6 +305,7 @@ public class ConnectivityServiceTest { public Object getSystemService(String name) { if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm; if (Context.NOTIFICATION_SERVICE.equals(name)) return mock(NotificationManager.class); + if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack; return super.getSystemService(name); } @@ -386,7 +393,7 @@ public class ConnectivityServiceTest { } private class MockNetworkAgent { - private final WrappedNetworkMonitor mWrappedNetworkMonitor; + private final INetworkMonitor mNetworkMonitor; private final NetworkInfo mNetworkInfo; private final NetworkCapabilities mNetworkCapabilities; private final HandlerThread mHandlerThread; @@ -402,6 +409,26 @@ public class ConnectivityServiceTest { // mNetworkStatusReceived. private String mRedirectUrl; + private INetworkMonitorCallbacks mNmCallbacks; + private int mNmValidationResult = NETWORK_TEST_RESULT_INVALID; + private String mNmValidationRedirectUrl = null; + private boolean mNmProvNotificationRequested = false; + + void setNetworkValid() { + mNmValidationResult = NETWORK_TEST_RESULT_VALID; + mNmValidationRedirectUrl = null; + } + + void setNetworkInvalid() { + mNmValidationResult = NETWORK_TEST_RESULT_INVALID; + mNmValidationRedirectUrl = null; + } + + void setNetworkPortal(String redirectUrl) { + setNetworkInvalid(); + mNmValidationRedirectUrl = redirectUrl; + } + MockNetworkAgent(int transport) { this(transport, new LinkProperties()); } @@ -434,6 +461,29 @@ public class ConnectivityServiceTest { } mHandlerThread = new HandlerThread("Mock-" + typeName); mHandlerThread.start(); + + mNetworkMonitor = mock(INetworkMonitor.class); + final Answer validateAnswer = inv -> { + new Thread(this::onValidationRequested).start(); + return null; + }; + + try { + doAnswer(validateAnswer).when(mNetworkMonitor).notifyNetworkConnected(); + doAnswer(validateAnswer).when(mNetworkMonitor).forceReevaluation(anyInt()); + } catch (RemoteException e) { + fail(e.getMessage()); + } + + final ArgumentCaptor<Network> nmNetworkCaptor = + ArgumentCaptor.forClass(Network.class); + final ArgumentCaptor<INetworkMonitorCallbacks> nmCbCaptor = + ArgumentCaptor.forClass(INetworkMonitorCallbacks.class); + doNothing().when(mNetworkStack).makeNetworkMonitor( + nmNetworkCaptor.capture(), + any() /* name */, + nmCbCaptor.capture()); + mNetworkAgent = new NetworkAgent(mHandlerThread.getLooper(), mServiceContext, "Mock-" + typeName, mNetworkInfo, mNetworkCapabilities, linkProperties, mScore, new NetworkMisc()) { @@ -465,10 +515,40 @@ public class ConnectivityServiceTest { mPreventReconnectReceived.open(); } }; + + assertEquals(mNetworkAgent.netId, nmNetworkCaptor.getValue().netId); + mNmCallbacks = nmCbCaptor.getValue(); + + try { + mNmCallbacks.onNetworkMonitorCreated(mNetworkMonitor); + } catch (RemoteException e) { + fail(e.getMessage()); + } + // Waits for the NetworkAgent to be registered, which includes the creation of the // NetworkMonitor. waitForIdle(); - mWrappedNetworkMonitor = mService.getLastCreatedWrappedNetworkMonitor(); + } + + private void onValidationRequested() { + try { + if (mNmProvNotificationRequested + && mNmValidationResult == NETWORK_TEST_RESULT_VALID) { + mNmCallbacks.hideProvisioningNotification(); + mNmProvNotificationRequested = false; + } + + mNmCallbacks.notifyNetworkTested( + mNmValidationResult, mNmValidationRedirectUrl); + + if (mNmValidationRedirectUrl != null) { + mNmCallbacks.showProvisioningNotification( + "test_provisioning_notif_action"); + mNmProvNotificationRequested = true; + } + } catch (RemoteException e) { + fail(e.getMessage()); + } } public void adjustScore(int change) { @@ -539,7 +619,7 @@ public class ConnectivityServiceTest { NetworkCallback callback = null; final ConditionVariable validatedCv = new ConditionVariable(); if (validated) { - mWrappedNetworkMonitor.gen204ProbeResult = 204; + setNetworkValid(); NetworkRequest request = new NetworkRequest.Builder() .addTransportType(mNetworkCapabilities.getTransportTypes()[0]) .clearCapabilities() @@ -564,15 +644,14 @@ public class ConnectivityServiceTest { if (validated) { // Wait for network to validate. waitFor(validatedCv); - mWrappedNetworkMonitor.gen204ProbeResult = 500; + setNetworkInvalid(); } if (callback != null) mCm.unregisterNetworkCallback(callback); } public void connectWithCaptivePortal(String redirectUrl) { - mWrappedNetworkMonitor.gen204ProbeResult = 200; - mWrappedNetworkMonitor.gen204ProbeRedirectUrl = redirectUrl; + setNetworkPortal(redirectUrl); connect(false); } @@ -603,10 +682,6 @@ public class ConnectivityServiceTest { return mDisconnected; } - public WrappedNetworkMonitor getWrappedNetworkMonitor() { - return mWrappedNetworkMonitor; - } - public void sendLinkProperties(LinkProperties lp) { mNetworkAgent.sendLinkProperties(lp); } @@ -880,28 +955,6 @@ public class ConnectivityServiceTest { } } - // NetworkMonitor implementation allowing overriding of Internet connectivity probe result. - private class WrappedNetworkMonitor extends NetworkMonitor { - public final Handler connectivityHandler; - // HTTP response code fed back to NetworkMonitor for Internet connectivity probe. - public int gen204ProbeResult = 500; - public String gen204ProbeRedirectUrl = null; - - public WrappedNetworkMonitor(Context context, Handler handler, - NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest, - IpConnectivityLog log) { - super(context, handler, networkAgentInfo, defaultRequest, log, - NetworkMonitor.Dependencies.DEFAULT); - connectivityHandler = handler; - } - - @Override - protected CaptivePortalProbeResult isCaptivePortal() { - if (!mIsCaptivePortalCheckEnabled) { return new CaptivePortalProbeResult(204); } - return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl, null); - } - } - private class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker { public volatile boolean configRestrictsAvoidBadWifi; public volatile int configMeteredMultipathPreference; @@ -923,7 +976,6 @@ public class ConnectivityServiceTest { private class WrappedConnectivityService extends ConnectivityService { public WrappedMultinetworkPolicyTracker wrappedMultinetworkPolicyTracker; - private WrappedNetworkMonitor mLastCreatedNetworkMonitor; private MockableSystemProperties mSystemProperties; public WrappedConnectivityService(Context context, INetworkManagementService netManager, @@ -971,15 +1023,6 @@ public class ConnectivityServiceTest { } } - @Override - public NetworkMonitor createNetworkMonitor(Context context, Handler handler, - NetworkAgentInfo nai, NetworkRequest defaultRequest) { - final WrappedNetworkMonitor monitor = new WrappedNetworkMonitor( - context, handler, nai, defaultRequest, mock(IpConnectivityLog.class)); - mLastCreatedNetworkMonitor = monitor; - return monitor; - } - public Nat464Xlat getNat464Xlat(MockNetworkAgent mna) { return getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd; } @@ -1017,10 +1060,6 @@ public class ConnectivityServiceTest { protected void registerNetdEventCallback() { } - public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() { - return mLastCreatedNetworkMonitor; - } - public void mockVpn(int uid) { synchronized (mVpns) { int userId = UserHandle.getUserId(uid); @@ -2439,7 +2478,7 @@ public class ConnectivityServiceTest { // Make captive portal disappear then revalidate. // Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL. - mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 204; + mWiFiNetworkAgent.setNetworkValid(); mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); @@ -2448,13 +2487,13 @@ public class ConnectivityServiceTest { // Break network connectivity. // Expect NET_CAPABILITY_VALIDATED onLost callback. - mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 500; + mWiFiNetworkAgent.setNetworkInvalid(); mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false); validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); } @Test - public void testCaptivePortalApp() { + public void testCaptivePortalApp() throws RemoteException { final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); @@ -2477,21 +2516,19 @@ public class ConnectivityServiceTest { mServiceContext.expectNoStartActivityIntent(fastTimeoutMs); // Turn into a captive portal. - mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 302; + mWiFiNetworkAgent.setNetworkPortal("http://example.com"); mCm.reportNetworkConnectivity(wifiNetwork, false); captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); validatedCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); - // Check that startCaptivePortalApp sends the expected intent. + // Check that startCaptivePortalApp sends the expected command to NetworkMonitor. mCm.startCaptivePortalApp(wifiNetwork); - Intent intent = mServiceContext.expectStartActivityIntent(TIMEOUT_MS); - assertEquals(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN, intent.getAction()); - assertEquals(wifiNetwork, intent.getExtra(ConnectivityManager.EXTRA_NETWORK)); - - // Have the app report that the captive portal is dismissed, and check that we revalidate. - mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 204; - CaptivePortal c = (CaptivePortal) intent.getExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL); - c.reportCaptivePortalDismissed(); + verify(mWiFiNetworkAgent.mNetworkMonitor, timeout(TIMEOUT_MS).times(1)) + .launchCaptivePortalApp(); + + // Report that the captive portal is dismissed, and check that callbacks are fired + mWiFiNetworkAgent.setNetworkValid(); + mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); @@ -2524,20 +2561,6 @@ public class ConnectivityServiceTest { waitFor(avoidCv); assertNoCallbacks(captivePortalCallback, validatedCallback); - - // Now test ignore mode. - setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE); - - // Bring up a network with a captive portal. - // Since we're ignoring captive portals, the network will validate. - mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); - String secondRedirectUrl = "http://example.com/secondPath"; - mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl); - - // Expect NET_CAPABILITY_VALIDATED onAvailable callback. - validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); - // But there should be no CaptivePortal callback. - captivePortalCallback.assertNoCallback(); } private NetworkRequest.Builder newWifiRequestBuilder() { @@ -3169,7 +3192,7 @@ public class ConnectivityServiceTest { Network wifiNetwork = mWiFiNetworkAgent.getNetwork(); // Fail validation on wifi. - mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 599; + mWiFiNetworkAgent.setNetworkInvalid(); mCm.reportNetworkConnectivity(wifiNetwork, false); defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); @@ -3213,7 +3236,7 @@ public class ConnectivityServiceTest { wifiNetwork = mWiFiNetworkAgent.getNetwork(); // Fail validation on wifi and expect the dialog to appear. - mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 599; + mWiFiNetworkAgent.setNetworkInvalid(); mCm.reportNetworkConnectivity(wifiNetwork, false); defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); @@ -4002,11 +4025,9 @@ public class ConnectivityServiceTest { final String TLS_SERVER6 = "2001:db8:53::53"; final InetAddress[] TLS_IPS = new InetAddress[]{ InetAddress.getByName(TLS_SERVER6) }; final String[] TLS_SERVERS = new String[]{ TLS_SERVER6 }; - final Handler h = mCellNetworkAgent.getWrappedNetworkMonitor().connectivityHandler; - h.sendMessage(h.obtainMessage( - NetworkMonitor.EVENT_PRIVATE_DNS_CONFIG_RESOLVED, 0, - mCellNetworkAgent.getNetwork().netId, - new DnsManager.PrivateDnsConfig(TLS_SPECIFIER, TLS_IPS))); + mCellNetworkAgent.mNmCallbacks.notifyPrivateDnsConfigResolved( + new PrivateDnsConfig(TLS_SPECIFIER, TLS_IPS).toParcel()); + waitForIdle(); verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork( anyInt(), mStringArrayCaptor.capture(), any(), any(), @@ -4294,6 +4315,12 @@ public class ConnectivityServiceTest { ranges.add(new UidRange(uid, uid)); mMockVpn.setNetworkAgent(vpnNetworkAgent); mMockVpn.setUids(ranges); + // VPN networks do not satisfy the default request and are automatically validated + // by NetworkMonitor + assertFalse(NetworkMonitorUtils.isValidationRequired( + mCm.getDefaultRequest().networkCapabilities, vpnNetworkAgent.mNetworkCapabilities)); + vpnNetworkAgent.setNetworkValid(); + vpnNetworkAgent.connect(false); mMockVpn.connect(); diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java index 01b468af9447..38322e925a24 100644 --- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java @@ -17,7 +17,6 @@ package com.android.server.connectivity; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF; -import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; import static android.provider.Settings.Global.PRIVATE_DNS_DEFAULT_MODE; import static android.provider.Settings.Global.PRIVATE_DNS_MODE; @@ -29,13 +28,13 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.when; -import android.content.ContentResolver; import android.content.Context; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; import android.net.RouteInfo; +import android.net.shared.PrivateDnsConfig; import android.os.INetworkManagementService; import android.provider.Settings; import android.support.test.filters.SmallTest; @@ -43,18 +42,16 @@ import android.support.test.runner.AndroidJUnit4; import android.test.mock.MockContentResolver; import com.android.internal.util.test.FakeSettingsProvider; -import com.android.server.connectivity.DnsManager.PrivateDnsConfig; -import com.android.server.connectivity.MockableSystemProperties; -import java.net.InetAddress; -import java.util.Arrays; - -import org.junit.runner.RunWith; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.net.InetAddress; +import java.util.Arrays; + /** * Tests for {@link DnsManager}. * @@ -133,7 +130,7 @@ public class DnsManagerTest { PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_PROVIDER_HOSTNAME); Settings.Global.putString(mContentResolver, PRIVATE_DNS_SPECIFIER, "strictmode.com"); mDnsManager.updatePrivateDns(new Network(TEST_NETID), - new DnsManager.PrivateDnsConfig("strictmode.com", new InetAddress[] { + new PrivateDnsConfig("strictmode.com", new InetAddress[] { InetAddress.parseNumericAddress("6.6.6.6"), InetAddress.parseNumericAddress("2001:db8:66:66::1") })); diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java index 354cf2f2239e..4c52d818269d 100644 --- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java @@ -23,10 +23,10 @@ import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.mockito.Mockito.reset; import android.app.PendingIntent; import android.content.Context; @@ -36,18 +36,18 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkMisc; -import android.support.test.runner.AndroidJUnit4; +import android.net.NetworkStack; import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import android.text.format.DateUtils; import com.android.internal.R; import com.android.server.ConnectivityService; -import com.android.server.connectivity.NetworkNotificationManager; import com.android.server.connectivity.NetworkNotificationManager.NotificationType; -import org.junit.runner.RunWith; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -70,13 +70,16 @@ public class LingerMonitorTest { @Mock NetworkMisc mMisc; @Mock NetworkNotificationManager mNotifier; @Mock Resources mResources; + @Mock NetworkStack mNetworkStack; @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mCtx.getResources()).thenReturn(mResources); when(mCtx.getPackageName()).thenReturn("com.android.server.connectivity"); - when(mConnService.createNetworkMonitor(any(), any(), any(), any())).thenReturn(null); + when(mCtx.getSystemServiceName(NetworkStack.class)) + .thenReturn(Context.NETWORK_STACK_SERVICE); + when(mCtx.getSystemService(Context.NETWORK_STACK_SERVICE)).thenReturn(mNetworkStack); mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT); } @@ -349,7 +352,7 @@ public class LingerMonitorTest { caps.addCapability(0); caps.addTransportType(transport); NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null, - caps, 50, mCtx, null, mMisc, null, mConnService); + caps, 50, mCtx, null, mMisc, mConnService); nai.everValidated = true; return nai; } |