1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.vcn.routeselection;
18 
19 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
20 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
21 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
22 import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
23 
24 import static com.android.server.VcnManagementService.LOCAL_LOG;
25 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiEntryRssiThreshold;
26 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiExitRssiThreshold;
27 import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
28 
29 import android.annotation.NonNull;
30 import android.annotation.Nullable;
31 import android.net.ConnectivityManager;
32 import android.net.ConnectivityManager.NetworkCallback;
33 import android.net.IpSecTransform;
34 import android.net.LinkProperties;
35 import android.net.Network;
36 import android.net.NetworkCapabilities;
37 import android.net.NetworkRequest;
38 import android.net.TelephonyNetworkSpecifier;
39 import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
40 import android.net.vcn.VcnGatewayConnectionConfig;
41 import android.net.vcn.VcnUnderlyingNetworkTemplate;
42 import android.os.Handler;
43 import android.os.HandlerExecutor;
44 import android.os.ParcelUuid;
45 import android.telephony.TelephonyCallback;
46 import android.telephony.TelephonyManager;
47 import android.util.ArrayMap;
48 import android.util.ArraySet;
49 import android.util.Slog;
50 
51 import com.android.internal.annotations.VisibleForTesting;
52 import com.android.internal.annotations.VisibleForTesting.Visibility;
53 import com.android.internal.util.IndentingPrintWriter;
54 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
55 import com.android.server.vcn.VcnContext;
56 import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback;
57 import com.android.server.vcn.util.LogUtils;
58 
59 import java.util.ArrayList;
60 import java.util.Collections;
61 import java.util.List;
62 import java.util.Map;
63 import java.util.Objects;
64 import java.util.Set;
65 import java.util.TreeSet;
66 
67 /**
68  * Tracks a set of Networks underpinning a VcnGatewayConnection.
69  *
70  * <p>A single UnderlyingNetworkController is built to serve a SINGLE VCN Gateway Connection, and
71  * MUST be torn down with the VcnGatewayConnection in order to ensure underlying networks are
72  * allowed to be reaped.
73  *
74  * @hide
75  */
76 public class UnderlyingNetworkController {
77     @NonNull private static final String TAG = UnderlyingNetworkController.class.getSimpleName();
78 
79     @NonNull private final VcnContext mVcnContext;
80     @NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
81     @NonNull private final ParcelUuid mSubscriptionGroup;
82     @NonNull private final UnderlyingNetworkControllerCallback mCb;
83     @NonNull private final Dependencies mDeps;
84     @NonNull private final Handler mHandler;
85     @NonNull private final ConnectivityManager mConnectivityManager;
86     @NonNull private final TelephonyCallback mActiveDataSubIdListener =
87             new VcnActiveDataSubscriptionIdListener();
88 
89     private final Map<Network, UnderlyingNetworkEvaluator> mUnderlyingNetworkRecords =
90             new ArrayMap<>();
91 
92     @NonNull private final List<NetworkCallback> mCellBringupCallbacks = new ArrayList<>();
93     @Nullable private NetworkCallback mWifiBringupCallback;
94     @Nullable private NetworkCallback mWifiEntryRssiThresholdCallback;
95     @Nullable private NetworkCallback mWifiExitRssiThresholdCallback;
96     @Nullable private UnderlyingNetworkListener mRouteSelectionCallback;
97 
98     @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
99     @Nullable private PersistableBundleWrapper mCarrierConfig;
100     private boolean mIsQuitting = false;
101 
102     @Nullable private UnderlyingNetworkRecord mCurrentRecord;
103     @Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress;
104 
UnderlyingNetworkController( @onNull VcnContext vcnContext, @NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull UnderlyingNetworkControllerCallback cb)105     public UnderlyingNetworkController(
106             @NonNull VcnContext vcnContext,
107             @NonNull VcnGatewayConnectionConfig connectionConfig,
108             @NonNull ParcelUuid subscriptionGroup,
109             @NonNull TelephonySubscriptionSnapshot snapshot,
110             @NonNull UnderlyingNetworkControllerCallback cb) {
111         this(vcnContext, connectionConfig, subscriptionGroup, snapshot, cb, new Dependencies());
112     }
113 
114     @VisibleForTesting(visibility = Visibility.PRIVATE)
UnderlyingNetworkController( @onNull VcnContext vcnContext, @NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull UnderlyingNetworkControllerCallback cb, @NonNull Dependencies deps)115     UnderlyingNetworkController(
116             @NonNull VcnContext vcnContext,
117             @NonNull VcnGatewayConnectionConfig connectionConfig,
118             @NonNull ParcelUuid subscriptionGroup,
119             @NonNull TelephonySubscriptionSnapshot snapshot,
120             @NonNull UnderlyingNetworkControllerCallback cb,
121             @NonNull Dependencies deps) {
122         mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
123         mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
124         mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
125         mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
126         mCb = Objects.requireNonNull(cb, "Missing cb");
127         mDeps = Objects.requireNonNull(deps, "Missing deps");
128 
129         mHandler = new Handler(mVcnContext.getLooper());
130 
131         mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class);
132         mVcnContext
133                 .getContext()
134                 .getSystemService(TelephonyManager.class)
135                 .registerTelephonyCallback(new HandlerExecutor(mHandler), mActiveDataSubIdListener);
136 
137         mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup);
138 
139         registerOrUpdateNetworkRequests();
140     }
141 
142     private static class CapabilityMatchCriteria {
143         public final int capability;
144         public final int matchCriteria;
145 
CapabilityMatchCriteria(int capability, int matchCriteria)146         CapabilityMatchCriteria(int capability, int matchCriteria) {
147             this.capability = capability;
148             this.matchCriteria = matchCriteria;
149         }
150 
151         @Override
hashCode()152         public int hashCode() {
153             return Objects.hash(capability, matchCriteria);
154         }
155 
156         @Override
equals(@ullable Object other)157         public boolean equals(@Nullable Object other) {
158             if (!(other instanceof CapabilityMatchCriteria)) {
159                 return false;
160             }
161 
162             final CapabilityMatchCriteria rhs = (CapabilityMatchCriteria) other;
163             return capability == rhs.capability && matchCriteria == rhs.matchCriteria;
164         }
165     }
166 
dedupAndGetCapRequirementsForCell( VcnGatewayConnectionConfig connectionConfig)167     private static Set<Set<CapabilityMatchCriteria>> dedupAndGetCapRequirementsForCell(
168             VcnGatewayConnectionConfig connectionConfig) {
169         final Set<Set<CapabilityMatchCriteria>> dedupedCapsMatchSets = new ArraySet<>();
170 
171         for (VcnUnderlyingNetworkTemplate template :
172                 connectionConfig.getVcnUnderlyingNetworkPriorities()) {
173             if (template instanceof VcnCellUnderlyingNetworkTemplate) {
174                 final Set<CapabilityMatchCriteria> capsMatchSet = new ArraySet<>();
175 
176                 for (Map.Entry<Integer, Integer> entry :
177                         ((VcnCellUnderlyingNetworkTemplate) template)
178                                 .getCapabilitiesMatchCriteria()
179                                 .entrySet()) {
180 
181                     final int capability = entry.getKey();
182                     final int matchCriteria = entry.getValue();
183                     if (matchCriteria != MATCH_ANY) {
184                         capsMatchSet.add(new CapabilityMatchCriteria(capability, matchCriteria));
185                     }
186                 }
187 
188                 dedupedCapsMatchSets.add(capsMatchSet);
189             }
190         }
191 
192         dedupedCapsMatchSets.add(
193                 Collections.singleton(
194                         new CapabilityMatchCriteria(
195                                 NetworkCapabilities.NET_CAPABILITY_INTERNET, MATCH_REQUIRED)));
196         return dedupedCapsMatchSets;
197     }
198 
registerOrUpdateNetworkRequests()199     private void registerOrUpdateNetworkRequests() {
200         NetworkCallback oldRouteSelectionCallback = mRouteSelectionCallback;
201         NetworkCallback oldWifiCallback = mWifiBringupCallback;
202         NetworkCallback oldWifiEntryRssiThresholdCallback = mWifiEntryRssiThresholdCallback;
203         NetworkCallback oldWifiExitRssiThresholdCallback = mWifiExitRssiThresholdCallback;
204         List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks);
205         mCellBringupCallbacks.clear();
206 
207         if (mVcnContext.isFlagNetworkMetricMonitorEnabled()
208                 && mVcnContext.isFlagIpSecTransformStateEnabled()) {
209             for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
210                 evaluator.close();
211             }
212         }
213 
214         mUnderlyingNetworkRecords.clear();
215 
216         // Register new callbacks. Make-before-break; always register new callbacks before removal
217         // of old callbacks
218         if (!mIsQuitting) {
219             mRouteSelectionCallback = new UnderlyingNetworkListener();
220             mConnectivityManager.registerNetworkCallback(
221                     getRouteSelectionRequest(), mRouteSelectionCallback, mHandler);
222 
223             mWifiEntryRssiThresholdCallback = new NetworkBringupCallback();
224             mConnectivityManager.registerNetworkCallback(
225                     getWifiEntryRssiThresholdNetworkRequest(),
226                     mWifiEntryRssiThresholdCallback,
227                     mHandler);
228 
229             mWifiExitRssiThresholdCallback = new NetworkBringupCallback();
230             mConnectivityManager.registerNetworkCallback(
231                     getWifiExitRssiThresholdNetworkRequest(),
232                     mWifiExitRssiThresholdCallback,
233                     mHandler);
234 
235             mWifiBringupCallback = new NetworkBringupCallback();
236             mConnectivityManager.requestBackgroundNetwork(
237                     getWifiNetworkRequest(), mWifiBringupCallback, mHandler);
238 
239             for (final int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
240                 for (Set<CapabilityMatchCriteria> capsMatchCriteria :
241                         dedupAndGetCapRequirementsForCell(mConnectionConfig)) {
242                     final NetworkBringupCallback cb = new NetworkBringupCallback();
243                     mCellBringupCallbacks.add(cb);
244 
245                     mConnectivityManager.requestBackgroundNetwork(
246                             getCellNetworkRequestForSubId(subId, capsMatchCriteria), cb, mHandler);
247                 }
248             }
249         } else {
250             mRouteSelectionCallback = null;
251             mWifiBringupCallback = null;
252             mWifiEntryRssiThresholdCallback = null;
253             mWifiExitRssiThresholdCallback = null;
254             // mCellBringupCallbacks already cleared above.
255         }
256 
257         // Unregister old callbacks (as necessary)
258         if (oldRouteSelectionCallback != null) {
259             mConnectivityManager.unregisterNetworkCallback(oldRouteSelectionCallback);
260         }
261         if (oldWifiCallback != null) {
262             mConnectivityManager.unregisterNetworkCallback(oldWifiCallback);
263         }
264         if (oldWifiEntryRssiThresholdCallback != null) {
265             mConnectivityManager.unregisterNetworkCallback(oldWifiEntryRssiThresholdCallback);
266         }
267         if (oldWifiExitRssiThresholdCallback != null) {
268             mConnectivityManager.unregisterNetworkCallback(oldWifiExitRssiThresholdCallback);
269         }
270         for (NetworkCallback cellBringupCallback : oldCellCallbacks) {
271             mConnectivityManager.unregisterNetworkCallback(cellBringupCallback);
272         }
273     }
274 
275     /**
276      * Builds the Route selection request
277      *
278      * <p>This request is guaranteed to select carrier-owned, non-VCN underlying networks by virtue
279      * of a populated set of subIds as expressed in NetworkCapabilities#getSubscriptionIds(). Only
280      * carrier owned networks may be selected, as the request specifies only subIds in the VCN's
281      * subscription group, while the VCN networks are excluded by virtue of not having subIds set on
282      * the VCN-exposed networks.
283      *
284      * <p>If the VCN that this UnderlyingNetworkController belongs to is in test-mode, this will
285      * return a NetworkRequest that only matches Test Networks.
286      */
getRouteSelectionRequest()287     private NetworkRequest getRouteSelectionRequest() {
288         if (mVcnContext.isInTestMode()) {
289             return getTestNetworkRequest(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup));
290         }
291 
292         return getBaseNetworkRequestBuilder()
293                 .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
294                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
295                 .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
296                 .build();
297     }
298 
getBaseWifiNetworkRequestBuilder()299     private NetworkRequest.Builder getBaseWifiNetworkRequestBuilder() {
300         return getBaseNetworkRequestBuilder()
301                 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
302                 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
303                 .setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup));
304     }
305 
306     /**
307      * Builds the WiFi bringup request
308      *
309      * <p>This request is built specifically to match only carrier-owned WiFi networks, but is also
310      * built to ONLY keep Carrier WiFi Networks alive (but never bring them up). This is a result of
311      * the WifiNetworkFactory not advertising a list of subIds, and therefore not accepting this
312      * request. As such, it will bind to a Carrier WiFi Network that has already been brought up,
313      * but will NEVER bring up a Carrier WiFi network itself.
314      */
getWifiNetworkRequest()315     private NetworkRequest getWifiNetworkRequest() {
316         return getBaseWifiNetworkRequestBuilder().build();
317     }
318 
319     /**
320      * Builds the WiFi entry threshold signal strength request
321      *
322      * <p>This request ensures that WiFi reports the crossing of the wifi entry RSSI threshold.
323      * Without this request, WiFi rate-limits, and reports signal strength changes at too slow a
324      * pace to effectively select a short-lived WiFi offload network.
325      */
getWifiEntryRssiThresholdNetworkRequest()326     private NetworkRequest getWifiEntryRssiThresholdNetworkRequest() {
327         return getBaseWifiNetworkRequestBuilder()
328                 // Ensure wifi updates signal strengths when crossing this threshold.
329                 .setSignalStrength(getWifiEntryRssiThreshold(mCarrierConfig))
330                 .build();
331     }
332 
333     /**
334      * Builds the WiFi exit threshold signal strength request
335      *
336      * <p>This request ensures that WiFi reports the crossing of the wifi exit RSSI threshold.
337      * Without this request, WiFi rate-limits, and reports signal strength changes at too slow a
338      * pace to effectively select away from a failing WiFi network.
339      */
getWifiExitRssiThresholdNetworkRequest()340     private NetworkRequest getWifiExitRssiThresholdNetworkRequest() {
341         return getBaseWifiNetworkRequestBuilder()
342                 // Ensure wifi updates signal strengths when crossing this threshold.
343                 .setSignalStrength(getWifiExitRssiThreshold(mCarrierConfig))
344                 .build();
345     }
346 
347     /**
348      * Builds a Cellular bringup request for a given subId
349      *
350      * <p>This request is filed in order to ensure that the Telephony stack always has a
351      * NetworkRequest to bring up a VCN underlying cellular network. It is required in order to
352      * ensure that even when a VCN (appears as Cellular) satisfies the default request, Telephony
353      * will bring up additional underlying Cellular networks.
354      *
355      * <p>Since this request MUST make it to the TelephonyNetworkFactory, subIds are not specified
356      * in the NetworkCapabilities, but rather in the TelephonyNetworkSpecifier.
357      */
getCellNetworkRequestForSubId( int subId, Set<CapabilityMatchCriteria> capsMatchCriteria)358     private NetworkRequest getCellNetworkRequestForSubId(
359             int subId, Set<CapabilityMatchCriteria> capsMatchCriteria) {
360         final NetworkRequest.Builder nrBuilder =
361                 getBaseNetworkRequestBuilder()
362                         .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
363                         .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId));
364 
365         for (CapabilityMatchCriteria capMatchCriteria : capsMatchCriteria) {
366             final int cap = capMatchCriteria.capability;
367             final int matchCriteria = capMatchCriteria.matchCriteria;
368 
369             if (matchCriteria == MATCH_REQUIRED) {
370                 nrBuilder.addCapability(cap);
371             } else if (matchCriteria == MATCH_FORBIDDEN) {
372                 nrBuilder.addForbiddenCapability(cap);
373             }
374         }
375 
376         return nrBuilder.build();
377     }
378 
379     /**
380      * Builds and returns a NetworkRequest builder common to all Underlying Network requests
381      */
getBaseNetworkRequestBuilder()382     private NetworkRequest.Builder getBaseNetworkRequestBuilder() {
383         return new NetworkRequest.Builder()
384                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
385                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
386                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
387     }
388 
389     /** Builds and returns a NetworkRequest for the given subIds to match Test Networks. */
getTestNetworkRequest(@onNull Set<Integer> subIds)390     private NetworkRequest getTestNetworkRequest(@NonNull Set<Integer> subIds) {
391         return new NetworkRequest.Builder()
392                 .clearCapabilities()
393                 .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
394                 .setSubscriptionIds(subIds)
395                 .build();
396     }
397 
398     /**
399      * Update this UnderlyingNetworkController's TelephonySubscriptionSnapshot.
400      *
401      * <p>Updating the TelephonySubscriptionSnapshot will cause this UnderlyingNetworkController to
402      * reevaluate its NetworkBringupCallbacks. This may result in NetworkRequests being registered
403      * or unregistered if the subIds mapped to the this Tracker's SubscriptionGroup change.
404      */
updateSubscriptionSnapshot(@onNull TelephonySubscriptionSnapshot newSnapshot)405     public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot newSnapshot) {
406         Objects.requireNonNull(newSnapshot, "Missing newSnapshot");
407 
408         final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot;
409         mLastSnapshot = newSnapshot;
410 
411         // Update carrier config
412         mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup);
413 
414         // Make sure all evaluators use the same updated TelephonySubscriptionSnapshot and carrier
415         // config to calculate their cached priority classes. For simplicity, the
416         // UnderlyingNetworkController does not listen for changes in VCN-related carrier config
417         // keys, and changes are applied at restart of the VcnGatewayConnection
418         for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
419             evaluator.reevaluate(
420                     mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
421                     mSubscriptionGroup,
422                     mLastSnapshot,
423                     mCarrierConfig);
424         }
425 
426         // Only trigger re-registration if subIds in this group have changed
427         if (oldSnapshot
428                 .getAllSubIdsInGroup(mSubscriptionGroup)
429                 .equals(newSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))) {
430 
431             if (mVcnContext.isFlagNetworkMetricMonitorEnabled()
432                     && mVcnContext.isFlagIpSecTransformStateEnabled()) {
433                 reevaluateNetworks();
434             }
435             return;
436         }
437         registerOrUpdateNetworkRequests();
438     }
439 
440     /**
441      * Pass the IpSecTransform of the VCN to UnderlyingNetworkController for metric monitoring
442      *
443      * <p>Caller MUST call it when IpSecTransforms have been created for VCN creation or migration
444      */
updateInboundTransform( @onNull UnderlyingNetworkRecord currentNetwork, @NonNull IpSecTransform transform)445     public void updateInboundTransform(
446             @NonNull UnderlyingNetworkRecord currentNetwork, @NonNull IpSecTransform transform) {
447         if (!mVcnContext.isFlagNetworkMetricMonitorEnabled()
448                 || !mVcnContext.isFlagIpSecTransformStateEnabled()) {
449             logWtf("#updateInboundTransform: unexpected call; flags missing");
450             return;
451         }
452 
453         Objects.requireNonNull(currentNetwork, "currentNetwork is null");
454         Objects.requireNonNull(transform, "transform is null");
455 
456         if (mCurrentRecord == null
457                 || mRouteSelectionCallback == null
458                 || !Objects.equals(currentNetwork.network, mCurrentRecord.network)) {
459             // The caller (VcnGatewayConnection) is out-of-dated. Ignore this call.
460             return;
461         }
462 
463         mUnderlyingNetworkRecords.get(mCurrentRecord.network).setInboundTransform(transform);
464     }
465 
466     /** Tears down this Tracker, and releases all underlying network requests. */
teardown()467     public void teardown() {
468         mVcnContext.ensureRunningOnLooperThread();
469         mIsQuitting = true;
470 
471         // Will unregister all existing callbacks, but not register new ones due to quitting flag.
472         registerOrUpdateNetworkRequests();
473 
474         mVcnContext
475                 .getContext()
476                 .getSystemService(TelephonyManager.class)
477                 .unregisterTelephonyCallback(mActiveDataSubIdListener);
478     }
479 
getSortedUnderlyingNetworks()480     private TreeSet<UnderlyingNetworkEvaluator> getSortedUnderlyingNetworks() {
481         TreeSet<UnderlyingNetworkEvaluator> sorted =
482                 new TreeSet<>(UnderlyingNetworkEvaluator.getComparator(mVcnContext));
483 
484         for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
485             if (evaluator.getPriorityClass() != NetworkPriorityClassifier.PRIORITY_INVALID) {
486                 sorted.add(evaluator);
487             }
488         }
489 
490         return sorted;
491     }
492 
reevaluateNetworks()493     private void reevaluateNetworks() {
494         if (mIsQuitting || mRouteSelectionCallback == null) {
495             return; // UnderlyingNetworkController has quit.
496         }
497 
498         TreeSet<UnderlyingNetworkEvaluator> sorted = getSortedUnderlyingNetworks();
499 
500         UnderlyingNetworkEvaluator candidateEvaluator = sorted.isEmpty() ? null : sorted.first();
501         UnderlyingNetworkRecord candidate =
502                 candidateEvaluator == null ? null : candidateEvaluator.getNetworkRecord();
503         if (Objects.equals(mCurrentRecord, candidate)) {
504             return;
505         }
506 
507         String allNetworkPriorities = "";
508         for (UnderlyingNetworkEvaluator recordEvaluator : sorted) {
509             if (!allNetworkPriorities.isEmpty()) {
510                 allNetworkPriorities += ", ";
511             }
512             allNetworkPriorities +=
513                     recordEvaluator.getNetwork() + ": " + recordEvaluator.getPriorityClass();
514         }
515 
516         if (!UnderlyingNetworkRecord.isSameNetwork(mCurrentRecord, candidate)) {
517             logInfo(
518                     "Selected network changed to "
519                             + (candidate == null ? null : candidate.network)
520                             + ", selected from list: "
521                             + allNetworkPriorities);
522         }
523 
524         mCurrentRecord = candidate;
525         mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
526 
527         // Need to update all evaluators to ensure the previously selected one is unselected
528         for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
529             evaluator.setIsSelected(
530                     candidateEvaluator == evaluator,
531                     mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
532                     mSubscriptionGroup,
533                     mLastSnapshot,
534                     mCarrierConfig);
535         }
536     }
537 
538     /**
539      * NetworkBringupCallback is used to keep background, VCN-managed Networks from being reaped.
540      *
541      * <p>NetworkBringupCallback only exists to prevent matching (VCN-managed) Networks from being
542      * reaped, and no action is taken on any events firing.
543      */
544     @VisibleForTesting
545     class NetworkBringupCallback extends NetworkCallback {}
546 
547     /**
548      * RouteSelectionCallback is used to select the "best" underlying Network.
549      *
550      * <p>The "best" network is determined by ConnectivityService, which is treated as a source of
551      * truth.
552      */
553     @VisibleForTesting
554     class UnderlyingNetworkListener extends NetworkCallback {
UnderlyingNetworkListener()555         UnderlyingNetworkListener() {
556             super(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO);
557         }
558 
559         @Override
onAvailable(@onNull Network network)560         public void onAvailable(@NonNull Network network) {
561             mUnderlyingNetworkRecords.put(
562                     network,
563                     mDeps.newUnderlyingNetworkEvaluator(
564                             mVcnContext,
565                             network,
566                             mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
567                             mSubscriptionGroup,
568                             mLastSnapshot,
569                             mCarrierConfig,
570                             new NetworkEvaluatorCallbackImpl()));
571         }
572 
573         @Override
onLost(@onNull Network network)574         public void onLost(@NonNull Network network) {
575             if (mVcnContext.isFlagNetworkMetricMonitorEnabled()
576                     && mVcnContext.isFlagIpSecTransformStateEnabled()) {
577                 mUnderlyingNetworkRecords.get(network).close();
578             }
579 
580             mUnderlyingNetworkRecords.remove(network);
581 
582             reevaluateNetworks();
583         }
584 
585         @Override
onCapabilitiesChanged( @onNull Network network, @NonNull NetworkCapabilities networkCapabilities)586         public void onCapabilitiesChanged(
587                 @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
588             final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network);
589             if (evaluator == null) {
590                 logWtf("Got capabilities change for unknown key: " + network);
591                 return;
592             }
593 
594             evaluator.setNetworkCapabilities(
595                     networkCapabilities,
596                     mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
597                     mSubscriptionGroup,
598                     mLastSnapshot,
599                     mCarrierConfig);
600 
601             if (evaluator.isValid()) {
602                 reevaluateNetworks();
603             }
604         }
605 
606         @Override
onLinkPropertiesChanged( @onNull Network network, @NonNull LinkProperties linkProperties)607         public void onLinkPropertiesChanged(
608                 @NonNull Network network, @NonNull LinkProperties linkProperties) {
609             final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network);
610             if (evaluator == null) {
611                 logWtf("Got link properties change for unknown key: " + network);
612                 return;
613             }
614 
615             evaluator.setLinkProperties(
616                     linkProperties,
617                     mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
618                     mSubscriptionGroup,
619                     mLastSnapshot,
620                     mCarrierConfig);
621 
622             if (evaluator.isValid()) {
623                 reevaluateNetworks();
624             }
625         }
626 
627         @Override
onBlockedStatusChanged(@onNull Network network, boolean isBlocked)628         public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) {
629             final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network);
630             if (evaluator == null) {
631                 logWtf("Got blocked status change for unknown key: " + network);
632                 return;
633             }
634 
635             evaluator.setIsBlocked(
636                     isBlocked,
637                     mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
638                     mSubscriptionGroup,
639                     mLastSnapshot,
640                     mCarrierConfig);
641 
642             if (evaluator.isValid()) {
643                 reevaluateNetworks();
644             }
645         }
646     }
647 
648     @VisibleForTesting
649     class NetworkEvaluatorCallbackImpl implements NetworkEvaluatorCallback {
650         @Override
onEvaluationResultChanged()651         public void onEvaluationResultChanged() {
652             if (!mVcnContext.isFlagNetworkMetricMonitorEnabled()
653                     || !mVcnContext.isFlagIpSecTransformStateEnabled()) {
654                 logWtf("#onEvaluationResultChanged: unexpected call; flags missing");
655                 return;
656             }
657 
658             mVcnContext.ensureRunningOnLooperThread();
659             reevaluateNetworks();
660         }
661     }
662 
getLogPrefix()663     private String getLogPrefix() {
664         return "("
665                 + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
666                 + "-"
667                 + mConnectionConfig.getGatewayConnectionName()
668                 + "-"
669                 + System.identityHashCode(this)
670                 + ") ";
671     }
672 
getTagLogPrefix()673     private String getTagLogPrefix() {
674         return "[ " + TAG + " " + getLogPrefix() + "]";
675     }
676 
logInfo(String msg)677     private void logInfo(String msg) {
678         Slog.i(TAG, getLogPrefix() + msg);
679         LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg);
680     }
681 
logInfo(String msg, Throwable tr)682     private void logInfo(String msg, Throwable tr) {
683         Slog.i(TAG, getLogPrefix() + msg, tr);
684         LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr);
685     }
686 
logWtf(String msg)687     private void logWtf(String msg) {
688         Slog.wtf(TAG, msg);
689         LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg);
690     }
691 
logWtf(String msg, Throwable tr)692     private void logWtf(String msg, Throwable tr) {
693         Slog.wtf(TAG, msg, tr);
694         LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg + tr);
695     }
696 
697     /** Dumps the state of this record for logging and debugging purposes. */
dump(IndentingPrintWriter pw)698     public void dump(IndentingPrintWriter pw) {
699         pw.println("UnderlyingNetworkController:");
700         pw.increaseIndent();
701 
702         pw.println("Carrier WiFi Entry Threshold: " + getWifiEntryRssiThreshold(mCarrierConfig));
703         pw.println("Carrier WiFi Exit Threshold: " + getWifiExitRssiThreshold(mCarrierConfig));
704         pw.println(
705                 "Currently selected: " + (mCurrentRecord == null ? null : mCurrentRecord.network));
706 
707         pw.println("VcnUnderlyingNetworkTemplate list:");
708         pw.increaseIndent();
709         int index = 0;
710         for (VcnUnderlyingNetworkTemplate priority :
711                 mConnectionConfig.getVcnUnderlyingNetworkPriorities()) {
712             pw.println("Priority index: " + index);
713             priority.dump(pw);
714             index++;
715         }
716         pw.decreaseIndent();
717         pw.println();
718 
719         pw.println("Underlying networks:");
720         pw.increaseIndent();
721         if (mRouteSelectionCallback != null) {
722             for (UnderlyingNetworkEvaluator recordEvaluator : getSortedUnderlyingNetworks()) {
723                 recordEvaluator.dump(pw);
724             }
725         }
726         pw.decreaseIndent();
727         pw.println();
728 
729         pw.decreaseIndent();
730     }
731 
732     private class VcnActiveDataSubscriptionIdListener extends TelephonyCallback
733             implements ActiveDataSubscriptionIdListener {
734         @Override
onActiveDataSubscriptionIdChanged(int subId)735         public void onActiveDataSubscriptionIdChanged(int subId) {
736             reevaluateNetworks();
737         }
738     }
739 
740     /** Callbacks for being notified of the changes in, or to the selected underlying network. */
741     public interface UnderlyingNetworkControllerCallback {
742         /**
743          * Fired when a new underlying network is selected, or properties have changed.
744          *
745          * <p>This callback does NOT signal a mobility event.
746          *
747          * @param underlyingNetworkRecord The details of the new underlying network
748          */
onSelectedUnderlyingNetworkChanged( @ullable UnderlyingNetworkRecord underlyingNetworkRecord)749         void onSelectedUnderlyingNetworkChanged(
750                 @Nullable UnderlyingNetworkRecord underlyingNetworkRecord);
751     }
752 
753     @VisibleForTesting(visibility = Visibility.PRIVATE)
754     public static class Dependencies {
newUnderlyingNetworkEvaluator( @onNull VcnContext vcnContext, @NonNull Network network, @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot lastSnapshot, @Nullable PersistableBundleWrapper carrierConfig, @NonNull NetworkEvaluatorCallback evaluatorCallback)755         public UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator(
756                 @NonNull VcnContext vcnContext,
757                 @NonNull Network network,
758                 @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
759                 @NonNull ParcelUuid subscriptionGroup,
760                 @NonNull TelephonySubscriptionSnapshot lastSnapshot,
761                 @Nullable PersistableBundleWrapper carrierConfig,
762                 @NonNull NetworkEvaluatorCallback evaluatorCallback) {
763             return new UnderlyingNetworkEvaluator(
764                     vcnContext,
765                     network,
766                     underlyingNetworkTemplates,
767                     subscriptionGroup,
768                     lastSnapshot,
769                     carrierConfig,
770                     evaluatorCallback);
771         }
772     }
773 }
774