• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
23 import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
24 
25 import static com.android.server.VcnManagementService.LOCAL_LOG;
26 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiEntryRssiThreshold;
27 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiExitRssiThreshold;
28 
29 import android.annotation.NonNull;
30 import android.annotation.Nullable;
31 import android.annotation.TargetApi;
32 import android.net.ConnectivityManager;
33 import android.net.ConnectivityManager.NetworkCallback;
34 import android.net.IpSecTransform;
35 import android.net.LinkProperties;
36 import android.net.Network;
37 import android.net.NetworkCapabilities;
38 import android.net.NetworkRequest;
39 import android.net.TelephonyNetworkSpecifier;
40 import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
41 import android.net.vcn.VcnGatewayConnectionConfig;
42 import android.net.vcn.VcnUnderlyingNetworkTemplate;
43 import android.net.vcn.util.LogUtils;
44 import android.os.Build;
45 import android.os.Handler;
46 import android.os.ParcelUuid;
47 import android.telephony.TelephonyCallback;
48 import android.telephony.TelephonyManager;
49 import android.util.ArrayMap;
50 import android.util.ArraySet;
51 import android.util.IndentingPrintWriter;
52 import android.util.Slog;
53 
54 import com.android.internal.annotations.VisibleForTesting;
55 import com.android.internal.annotations.VisibleForTesting.Visibility;
56 import com.android.modules.utils.HandlerExecutor;
57 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
58 import com.android.server.vcn.VcnContext;
59 import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback;
60 
61 import java.util.ArrayList;
62 import java.util.Collections;
63 import java.util.List;
64 import java.util.Map;
65 import java.util.Objects;
66 import java.util.Set;
67 import java.util.TreeSet;
68 
69 /**
70  * Tracks a set of Networks underpinning a VcnGatewayConnection.
71  *
72  * <p>A single UnderlyingNetworkController is built to serve a SINGLE VCN Gateway Connection, and
73  * MUST be torn down with the VcnGatewayConnection in order to ensure underlying networks are
74  * allowed to be reaped.
75  *
76  * @hide
77  */
78 @TargetApi(Build.VERSION_CODES.BAKLAVA)
79 public class UnderlyingNetworkController {
80     @NonNull private static final String TAG = UnderlyingNetworkController.class.getSimpleName();
81 
82     @NonNull private final VcnContext mVcnContext;
83     @NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
84     @NonNull private final ParcelUuid mSubscriptionGroup;
85     @NonNull private final UnderlyingNetworkControllerCallback mCb;
86     @NonNull private final Dependencies mDeps;
87     @NonNull private final Handler mHandler;
88     @NonNull private final ConnectivityManager mConnectivityManager;
89     @NonNull private final TelephonyCallback mActiveDataSubIdListener =
90             new VcnActiveDataSubscriptionIdListener();
91 
92     private final Map<Network, UnderlyingNetworkEvaluator> mUnderlyingNetworkRecords =
93             new ArrayMap<>();
94 
95     @NonNull private final List<NetworkCallback> mCellBringupCallbacks = new ArrayList<>();
96     @Nullable private NetworkCallback mWifiBringupCallback;
97     @Nullable private NetworkCallback mWifiEntryRssiThresholdCallback;
98     @Nullable private NetworkCallback mWifiExitRssiThresholdCallback;
99     @Nullable private UnderlyingNetworkListener mRouteSelectionCallback;
100 
101     @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
102     @Nullable private PersistableBundleWrapper mCarrierConfig;
103     private boolean mIsQuitting = false;
104 
105     @Nullable private UnderlyingNetworkRecord mCurrentRecord;
106     @Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress;
107 
UnderlyingNetworkController( @onNull VcnContext vcnContext, @NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull UnderlyingNetworkControllerCallback cb)108     public UnderlyingNetworkController(
109             @NonNull VcnContext vcnContext,
110             @NonNull VcnGatewayConnectionConfig connectionConfig,
111             @NonNull ParcelUuid subscriptionGroup,
112             @NonNull TelephonySubscriptionSnapshot snapshot,
113             @NonNull UnderlyingNetworkControllerCallback cb) {
114         this(vcnContext, connectionConfig, subscriptionGroup, snapshot, cb, new Dependencies());
115     }
116 
117     @VisibleForTesting(visibility = Visibility.PRIVATE)
UnderlyingNetworkController( @onNull VcnContext vcnContext, @NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull UnderlyingNetworkControllerCallback cb, @NonNull Dependencies deps)118     UnderlyingNetworkController(
119             @NonNull VcnContext vcnContext,
120             @NonNull VcnGatewayConnectionConfig connectionConfig,
121             @NonNull ParcelUuid subscriptionGroup,
122             @NonNull TelephonySubscriptionSnapshot snapshot,
123             @NonNull UnderlyingNetworkControllerCallback cb,
124             @NonNull Dependencies deps) {
125         mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
126         mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
127         mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
128         mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
129         mCb = Objects.requireNonNull(cb, "Missing cb");
130         mDeps = Objects.requireNonNull(deps, "Missing deps");
131 
132         mHandler = new Handler(mVcnContext.getLooper());
133 
134         mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class);
135         mVcnContext
136                 .getContext()
137                 .getSystemService(TelephonyManager.class)
138                 .registerTelephonyCallback(new HandlerExecutor(mHandler), mActiveDataSubIdListener);
139 
140         mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup);
141 
142         registerOrUpdateNetworkRequests();
143     }
144 
145     private static class CapabilityMatchCriteria {
146         public final int capability;
147         public final int matchCriteria;
148 
CapabilityMatchCriteria(int capability, int matchCriteria)149         CapabilityMatchCriteria(int capability, int matchCriteria) {
150             this.capability = capability;
151             this.matchCriteria = matchCriteria;
152         }
153 
154         @Override
hashCode()155         public int hashCode() {
156             return Objects.hash(capability, matchCriteria);
157         }
158 
159         @Override
equals(@ullable Object other)160         public boolean equals(@Nullable Object other) {
161             if (!(other instanceof CapabilityMatchCriteria)) {
162                 return false;
163             }
164 
165             final CapabilityMatchCriteria rhs = (CapabilityMatchCriteria) other;
166             return capability == rhs.capability && matchCriteria == rhs.matchCriteria;
167         }
168     }
169 
dedupAndGetCapRequirementsForCell( VcnGatewayConnectionConfig connectionConfig)170     private static Set<Set<CapabilityMatchCriteria>> dedupAndGetCapRequirementsForCell(
171             VcnGatewayConnectionConfig connectionConfig) {
172         final Set<Set<CapabilityMatchCriteria>> dedupedCapsMatchSets = new ArraySet<>();
173 
174         for (VcnUnderlyingNetworkTemplate template :
175                 connectionConfig.getVcnUnderlyingNetworkPriorities()) {
176             if (template instanceof VcnCellUnderlyingNetworkTemplate) {
177                 final Set<CapabilityMatchCriteria> capsMatchSet = new ArraySet<>();
178 
179                 for (Map.Entry<Integer, Integer> entry :
180                         ((VcnCellUnderlyingNetworkTemplate) template)
181                                 .getCapabilitiesMatchCriteria()
182                                 .entrySet()) {
183 
184                     final int capability = entry.getKey();
185                     final int matchCriteria = entry.getValue();
186                     if (matchCriteria != MATCH_ANY) {
187                         capsMatchSet.add(new CapabilityMatchCriteria(capability, matchCriteria));
188                     }
189                 }
190 
191                 dedupedCapsMatchSets.add(capsMatchSet);
192             }
193         }
194 
195         dedupedCapsMatchSets.add(
196                 Collections.singleton(
197                         new CapabilityMatchCriteria(
198                                 NetworkCapabilities.NET_CAPABILITY_INTERNET, MATCH_REQUIRED)));
199         return dedupedCapsMatchSets;
200     }
201 
registerOrUpdateNetworkRequests()202     private void registerOrUpdateNetworkRequests() {
203         NetworkCallback oldRouteSelectionCallback = mRouteSelectionCallback;
204         NetworkCallback oldWifiCallback = mWifiBringupCallback;
205         NetworkCallback oldWifiEntryRssiThresholdCallback = mWifiEntryRssiThresholdCallback;
206         NetworkCallback oldWifiExitRssiThresholdCallback = mWifiExitRssiThresholdCallback;
207         List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks);
208         mCellBringupCallbacks.clear();
209 
210         for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
211             evaluator.close();
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(
364                                 new TelephonyNetworkSpecifier.Builder()
365                                         .setSubscriptionId(subId)
366                                         .build());
367 
368         for (CapabilityMatchCriteria capMatchCriteria : capsMatchCriteria) {
369             final int cap = capMatchCriteria.capability;
370             final int matchCriteria = capMatchCriteria.matchCriteria;
371 
372             if (matchCriteria == MATCH_REQUIRED) {
373                 nrBuilder.addCapability(cap);
374             } else if (matchCriteria == MATCH_FORBIDDEN) {
375                 nrBuilder.addForbiddenCapability(cap);
376             }
377         }
378 
379         return nrBuilder.build();
380     }
381 
382     /**
383      * Builds and returns a NetworkRequest builder common to all Underlying Network requests
384      */
getBaseNetworkRequestBuilder()385     private NetworkRequest.Builder getBaseNetworkRequestBuilder() {
386         return new NetworkRequest.Builder()
387                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
388                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
389                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
390     }
391 
392     /** Builds and returns a NetworkRequest for the given subIds to match Test Networks. */
getTestNetworkRequest(@onNull Set<Integer> subIds)393     private NetworkRequest getTestNetworkRequest(@NonNull Set<Integer> subIds) {
394         return new NetworkRequest.Builder()
395                 .clearCapabilities()
396                 .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
397                 .setSubscriptionIds(subIds)
398                 .build();
399     }
400 
401     /**
402      * Update this UnderlyingNetworkController's TelephonySubscriptionSnapshot.
403      *
404      * <p>Updating the TelephonySubscriptionSnapshot will cause this UnderlyingNetworkController to
405      * reevaluate its NetworkBringupCallbacks. This may result in NetworkRequests being registered
406      * or unregistered if the subIds mapped to the this Tracker's SubscriptionGroup change.
407      */
updateSubscriptionSnapshot(@onNull TelephonySubscriptionSnapshot newSnapshot)408     public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot newSnapshot) {
409         Objects.requireNonNull(newSnapshot, "Missing newSnapshot");
410 
411         final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot;
412         mLastSnapshot = newSnapshot;
413 
414         // Update carrier config
415         mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup);
416 
417         // Make sure all evaluators use the same updated TelephonySubscriptionSnapshot and carrier
418         // config to calculate their cached priority classes. For simplicity, the
419         // UnderlyingNetworkController does not listen for changes in VCN-related carrier config
420         // keys, and changes are applied at restart of the VcnGatewayConnection
421         for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
422             evaluator.reevaluate(
423                     mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
424                     mSubscriptionGroup,
425                     mLastSnapshot,
426                     mCarrierConfig);
427         }
428 
429         // Only trigger re-registration if subIds in this group have changed
430         if (oldSnapshot
431                 .getAllSubIdsInGroup(mSubscriptionGroup)
432                 .equals(newSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))) {
433             reevaluateNetworks();
434             return;
435         }
436         registerOrUpdateNetworkRequests();
437     }
438 
439     /**
440      * Pass the IpSecTransform of the VCN to UnderlyingNetworkController for metric monitoring
441      *
442      * <p>Caller MUST call it when IpSecTransforms have been created for VCN creation or migration
443      */
updateInboundTransform( @onNull UnderlyingNetworkRecord currentNetwork, @NonNull IpSecTransform transform)444     public void updateInboundTransform(
445             @NonNull UnderlyingNetworkRecord currentNetwork, @NonNull IpSecTransform transform) {
446         Objects.requireNonNull(currentNetwork, "currentNetwork is null");
447         Objects.requireNonNull(transform, "transform is null");
448 
449         if (mCurrentRecord == null
450                 || mRouteSelectionCallback == null
451                 || !Objects.equals(currentNetwork.network, mCurrentRecord.network)) {
452             // The caller (VcnGatewayConnection) is out-of-dated. Ignore this call.
453             return;
454         }
455 
456         mUnderlyingNetworkRecords.get(mCurrentRecord.network).setInboundTransform(transform);
457     }
458 
459     /** Tears down this Tracker, and releases all underlying network requests. */
teardown()460     public void teardown() {
461         mVcnContext.ensureRunningOnLooperThread();
462         mIsQuitting = true;
463 
464         // Will unregister all existing callbacks, but not register new ones due to quitting flag.
465         registerOrUpdateNetworkRequests();
466 
467         mVcnContext
468                 .getContext()
469                 .getSystemService(TelephonyManager.class)
470                 .unregisterTelephonyCallback(mActiveDataSubIdListener);
471     }
472 
getSortedUnderlyingNetworks()473     private TreeSet<UnderlyingNetworkEvaluator> getSortedUnderlyingNetworks() {
474         TreeSet<UnderlyingNetworkEvaluator> sorted =
475                 new TreeSet<>(UnderlyingNetworkEvaluator.getComparator(mVcnContext));
476 
477         for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
478             if (evaluator.getPriorityClass() != NetworkPriorityClassifier.PRIORITY_INVALID) {
479                 sorted.add(evaluator);
480             }
481         }
482 
483         return sorted;
484     }
485 
reevaluateNetworks()486     private void reevaluateNetworks() {
487         if (mIsQuitting || mRouteSelectionCallback == null) {
488             return; // UnderlyingNetworkController has quit.
489         }
490 
491         TreeSet<UnderlyingNetworkEvaluator> sorted = getSortedUnderlyingNetworks();
492 
493         UnderlyingNetworkEvaluator candidateEvaluator = sorted.isEmpty() ? null : sorted.first();
494         UnderlyingNetworkRecord candidate =
495                 candidateEvaluator == null ? null : candidateEvaluator.getNetworkRecord();
496         if (Objects.equals(mCurrentRecord, candidate)) {
497             return;
498         }
499 
500         String allNetworkPriorities = "";
501         for (UnderlyingNetworkEvaluator recordEvaluator : sorted) {
502             if (!allNetworkPriorities.isEmpty()) {
503                 allNetworkPriorities += ", ";
504             }
505             allNetworkPriorities +=
506                     recordEvaluator.getNetwork() + ": " + recordEvaluator.getPriorityClass();
507         }
508 
509         if (!UnderlyingNetworkRecord.isSameNetwork(mCurrentRecord, candidate)) {
510             logInfo(
511                     "Selected network changed to "
512                             + (candidate == null ? null : candidate.network)
513                             + ", selected from list: "
514                             + allNetworkPriorities);
515         }
516 
517         mCurrentRecord = candidate;
518         mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
519 
520         // Need to update all evaluators to ensure the previously selected one is unselected
521         for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
522             evaluator.setIsSelected(
523                     candidateEvaluator == evaluator,
524                     mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
525                     mSubscriptionGroup,
526                     mLastSnapshot,
527                     mCarrierConfig);
528         }
529     }
530 
531     /**
532      * NetworkBringupCallback is used to keep background, VCN-managed Networks from being reaped.
533      *
534      * <p>NetworkBringupCallback only exists to prevent matching (VCN-managed) Networks from being
535      * reaped, and no action is taken on any events firing.
536      */
537     @VisibleForTesting
538     class NetworkBringupCallback extends NetworkCallback {}
539 
540     /**
541      * RouteSelectionCallback is used to select the "best" underlying Network.
542      *
543      * <p>The "best" network is determined by ConnectivityService, which is treated as a source of
544      * truth.
545      */
546     @VisibleForTesting
547     class UnderlyingNetworkListener extends NetworkCallback {
UnderlyingNetworkListener()548         UnderlyingNetworkListener() {
549             super(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO);
550         }
551 
552         @Override
onAvailable(@onNull Network network)553         public void onAvailable(@NonNull Network network) {
554             mUnderlyingNetworkRecords.put(
555                     network,
556                     mDeps.newUnderlyingNetworkEvaluator(
557                             mVcnContext,
558                             network,
559                             mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
560                             mSubscriptionGroup,
561                             mLastSnapshot,
562                             mCarrierConfig,
563                             new NetworkEvaluatorCallbackImpl()));
564         }
565 
566         @Override
onLost(@onNull Network network)567         public void onLost(@NonNull Network network) {
568             mUnderlyingNetworkRecords.get(network).close();
569             mUnderlyingNetworkRecords.remove(network);
570 
571             reevaluateNetworks();
572         }
573 
574         @Override
onCapabilitiesChanged( @onNull Network network, @NonNull NetworkCapabilities networkCapabilities)575         public void onCapabilitiesChanged(
576                 @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
577             final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network);
578             if (evaluator == null) {
579                 logWtf("Got capabilities change for unknown key: " + network);
580                 return;
581             }
582 
583             evaluator.setNetworkCapabilities(
584                     networkCapabilities,
585                     mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
586                     mSubscriptionGroup,
587                     mLastSnapshot,
588                     mCarrierConfig);
589 
590             if (evaluator.isValid()) {
591                 reevaluateNetworks();
592             }
593         }
594 
595         @Override
onLinkPropertiesChanged( @onNull Network network, @NonNull LinkProperties linkProperties)596         public void onLinkPropertiesChanged(
597                 @NonNull Network network, @NonNull LinkProperties linkProperties) {
598             final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network);
599             if (evaluator == null) {
600                 logWtf("Got link properties change for unknown key: " + network);
601                 return;
602             }
603 
604             evaluator.setLinkProperties(
605                     linkProperties,
606                     mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
607                     mSubscriptionGroup,
608                     mLastSnapshot,
609                     mCarrierConfig);
610 
611             if (evaluator.isValid()) {
612                 reevaluateNetworks();
613             }
614         }
615 
616         @Override
onBlockedStatusChanged(@onNull Network network, boolean isBlocked)617         public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) {
618             final UnderlyingNetworkEvaluator evaluator = mUnderlyingNetworkRecords.get(network);
619             if (evaluator == null) {
620                 logWtf("Got blocked status change for unknown key: " + network);
621                 return;
622             }
623 
624             evaluator.setIsBlocked(
625                     isBlocked,
626                     mConnectionConfig.getVcnUnderlyingNetworkPriorities(),
627                     mSubscriptionGroup,
628                     mLastSnapshot,
629                     mCarrierConfig);
630 
631             if (evaluator.isValid()) {
632                 reevaluateNetworks();
633             }
634         }
635     }
636 
637     @VisibleForTesting
638     class NetworkEvaluatorCallbackImpl implements NetworkEvaluatorCallback {
639         @Override
onEvaluationResultChanged()640         public void onEvaluationResultChanged() {
641             mVcnContext.ensureRunningOnLooperThread();
642             reevaluateNetworks();
643         }
644     }
645 
getLogPrefix()646     private String getLogPrefix() {
647         return "("
648                 + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
649                 + "-"
650                 + mConnectionConfig.getGatewayConnectionName()
651                 + "-"
652                 + System.identityHashCode(this)
653                 + ") ";
654     }
655 
getTagLogPrefix()656     private String getTagLogPrefix() {
657         return "[ " + TAG + " " + getLogPrefix() + "]";
658     }
659 
logInfo(String msg)660     private void logInfo(String msg) {
661         Slog.i(TAG, getLogPrefix() + msg);
662         LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg);
663     }
664 
logInfo(String msg, Throwable tr)665     private void logInfo(String msg, Throwable tr) {
666         Slog.i(TAG, getLogPrefix() + msg, tr);
667         LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr);
668     }
669 
logWtf(String msg)670     private void logWtf(String msg) {
671         Slog.wtf(TAG, msg);
672         LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg);
673     }
674 
logWtf(String msg, Throwable tr)675     private void logWtf(String msg, Throwable tr) {
676         Slog.wtf(TAG, msg, tr);
677         LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg + tr);
678     }
679 
680     /** Dumps the state of this record for logging and debugging purposes. */
dump(IndentingPrintWriter pw)681     public void dump(IndentingPrintWriter pw) {
682         pw.println("UnderlyingNetworkController:");
683         pw.increaseIndent();
684 
685         pw.println("Carrier WiFi Entry Threshold: " + getWifiEntryRssiThreshold(mCarrierConfig));
686         pw.println("Carrier WiFi Exit Threshold: " + getWifiExitRssiThreshold(mCarrierConfig));
687         pw.println(
688                 "Currently selected: " + (mCurrentRecord == null ? null : mCurrentRecord.network));
689 
690         pw.println("VcnUnderlyingNetworkTemplate list:");
691         pw.increaseIndent();
692         int index = 0;
693         for (VcnUnderlyingNetworkTemplate priority :
694                 mConnectionConfig.getVcnUnderlyingNetworkPriorities()) {
695             pw.println("Priority index: " + index);
696             priority.dump(pw);
697             index++;
698         }
699         pw.decreaseIndent();
700         pw.println();
701 
702         pw.println("Underlying networks:");
703         pw.increaseIndent();
704         if (mRouteSelectionCallback != null) {
705             for (UnderlyingNetworkEvaluator recordEvaluator : getSortedUnderlyingNetworks()) {
706                 recordEvaluator.dump(pw);
707             }
708         }
709         pw.decreaseIndent();
710         pw.println();
711 
712         pw.decreaseIndent();
713     }
714 
715     private class VcnActiveDataSubscriptionIdListener extends TelephonyCallback
716             implements ActiveDataSubscriptionIdListener {
717         @Override
onActiveDataSubscriptionIdChanged(int subId)718         public void onActiveDataSubscriptionIdChanged(int subId) {
719             reevaluateNetworks();
720         }
721     }
722 
723     /** Callbacks for being notified of the changes in, or to the selected underlying network. */
724     public interface UnderlyingNetworkControllerCallback {
725         /**
726          * Fired when a new underlying network is selected, or properties have changed.
727          *
728          * <p>This callback does NOT signal a mobility event.
729          *
730          * @param underlyingNetworkRecord The details of the new underlying network
731          */
onSelectedUnderlyingNetworkChanged( @ullable UnderlyingNetworkRecord underlyingNetworkRecord)732         void onSelectedUnderlyingNetworkChanged(
733                 @Nullable UnderlyingNetworkRecord underlyingNetworkRecord);
734     }
735 
736     @VisibleForTesting(visibility = Visibility.PRIVATE)
737     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)738         public UnderlyingNetworkEvaluator newUnderlyingNetworkEvaluator(
739                 @NonNull VcnContext vcnContext,
740                 @NonNull Network network,
741                 @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
742                 @NonNull ParcelUuid subscriptionGroup,
743                 @NonNull TelephonySubscriptionSnapshot lastSnapshot,
744                 @Nullable PersistableBundleWrapper carrierConfig,
745                 @NonNull NetworkEvaluatorCallback evaluatorCallback) {
746             return new UnderlyingNetworkEvaluator(
747                     vcnContext,
748                     network,
749                     underlyingNetworkTemplates,
750                     subscriptionGroup,
751                     lastSnapshot,
752                     carrierConfig,
753                     evaluatorCallback);
754         }
755     }
756 }
757