• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.wifi;
18 
19 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY;
20 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_LONG_LIVED;
21 
22 import android.annotation.IntDef;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.content.Context;
26 import android.net.wifi.ScanResult;
27 import android.net.wifi.WifiInfo;
28 import android.net.wifi.WifiManager;
29 import android.os.Handler;
30 import android.os.WorkSource;
31 import android.util.Log;
32 import android.util.SparseArray;
33 
34 import com.android.internal.annotations.VisibleForTesting;
35 import com.android.modules.utils.build.SdkLevel;
36 
37 import java.io.FileDescriptor;
38 import java.io.PrintWriter;
39 import java.lang.annotation.Retention;
40 import java.lang.annotation.RetentionPolicy;
41 
42 /**
43  * Manages STA + STA for multi internet networks.
44  */
45 public class MultiInternetManager {
46     private static final String TAG = "WifiMultiInternet";
47 
48     private final ActiveModeWarden mActiveModeWarden;
49     private final FrameworkFacade mFrameworkFacade;
50     private final Context mContext;
51     private final ClientModeImplMonitor mCmiMonitor;
52     private final WifiSettingsStore mSettingsStore;
53     private final Handler mEventHandler;
54     private final Clock mClock;
55     private int mStaConcurrencyMultiInternetMode = WifiManager.WIFI_MULTI_INTERNET_MODE_DISABLED;
56     @MultiInternetState
57     private int mMultiInternetConnectionState = MULTI_INTERNET_STATE_NONE;
58     private ConnectionStatusListener mConnectionStatusListener;
59 
60     private SparseArray<NetworkConnectionState> mNetworkConnectionStates = new SparseArray<>();
61     private boolean mVerboseLoggingEnabled = false;
62 
63     /** No multi internet connection needed. */
64     public static final int MULTI_INTERNET_STATE_NONE = 0;
65     /** Multi internet connection is connecting. */
66     public static final int MULTI_INTERNET_STATE_CONNECTION_REQUESTED = 1;
67     /** No multi internet connection is connected. */
68     public static final int MULTI_INTERNET_STATE_CONNECTED = 2;
69     /** @hide */
70     @Retention(RetentionPolicy.SOURCE)
71     @IntDef(prefix = {"MULTI_INTERNET_STATE_"}, value = {
72             MULTI_INTERNET_STATE_NONE,
73             MULTI_INTERNET_STATE_CONNECTION_REQUESTED,
74             MULTI_INTERNET_STATE_CONNECTED})
75     public @interface MultiInternetState {}
76 
77     /** The internal network connection state per each Wi-Fi band. */
78     class NetworkConnectionState {
79         // If the supplicant connection is completed.
80         private boolean mConnected;
81         // If the internet has been validated.
82         private boolean mValidated;
83         // The connection start time in millisecond
84         public long connectionStartTimeMillis;
85         // The WorkSource of the connection requestor.
86         public WorkSource requestorWorkSource;
87 
NetworkConnectionState(WorkSource workSource)88         NetworkConnectionState(WorkSource workSource) {
89             this(workSource, -1L);
90         }
91 
NetworkConnectionState(WorkSource workSource, long connectionStartTime)92         NetworkConnectionState(WorkSource workSource, long connectionStartTime) {
93             requestorWorkSource = workSource;
94             connectionStartTimeMillis = connectionStartTime;
95 
96             mConnected = false;
97             mValidated = false;
98         }
99 
setConnected(boolean connected)100         public NetworkConnectionState setConnected(boolean connected) {
101             mConnected = connected;
102             return this;
103         }
104 
105         @VisibleForTesting
isConnected()106         public boolean isConnected() {
107             return mConnected;
108         }
109 
setValidated(boolean validated)110         public NetworkConnectionState setValidated(boolean validated) {
111             mValidated = validated;
112             return this;
113         }
114 
115         @VisibleForTesting
isValidated()116         public boolean isValidated() {
117             return mValidated;
118         }
119     }
120 
121     @VisibleForTesting
getNetworkConnectionState()122     SparseArray<NetworkConnectionState> getNetworkConnectionState() {
123         return mNetworkConnectionStates;
124     }
125 
126     /** The Multi Internet Connection Status Listener. The registered listener will be notified
127      * for the connection status change and scan needed. */
128     public interface ConnectionStatusListener {
129         /** Called when connection status changed */
onStatusChange( @ultiInternetManager.MultiInternetState int state, WorkSource requestorWs)130         void onStatusChange(
131                 @MultiInternetManager.MultiInternetState int state,
132                 WorkSource requestorWs);
133         /** Called when a scan is needed */
onStartScan(WorkSource requestorWs)134         void onStartScan(WorkSource requestorWs);
135     }
136 
getRequestorWorkSource(int band)137     @Nullable private WorkSource getRequestorWorkSource(int band) {
138         if (!mNetworkConnectionStates.contains(band)) {
139             return null;
140         }
141         return mNetworkConnectionStates.get(band).requestorWorkSource;
142     }
143 
144     private class ModeChangeCallback implements ActiveModeWarden.ModeChangeCallback {
145         @Override
onActiveModeManagerAdded(@onNull ActiveModeManager activeModeManager)146         public void onActiveModeManagerAdded(@NonNull ActiveModeManager activeModeManager) {
147             if (!mActiveModeWarden.isStaStaConcurrencySupportedForMultiInternet()
148                     || !isStaConcurrencyForMultiInternetEnabled()) {
149                 return;
150             }
151             if (!(activeModeManager instanceof ConcreteClientModeManager)) {
152                 return;
153             }
154             final ConcreteClientModeManager ccm = (ConcreteClientModeManager) activeModeManager;
155             // TODO: b/197670907 : Add client role ROLE_CLIENT_SECONDARY_INTERNET
156             if (ccm.getRole() != ROLE_CLIENT_SECONDARY_LONG_LIVED || !ccm.isSecondaryInternet()) {
157                 return;
158             }
159             if (mVerboseLoggingEnabled) {
160                 Log.v(TAG, "Secondary ClientModeManager created for internet, connecting!");
161             }
162             updateNetworkConnectionStates();
163         }
164 
165         @Override
onActiveModeManagerRemoved(@onNull ActiveModeManager activeModeManager)166         public void onActiveModeManagerRemoved(@NonNull ActiveModeManager activeModeManager) {
167             if (!mActiveModeWarden.isStaStaConcurrencySupportedForMultiInternet()
168                     || !isStaConcurrencyForMultiInternetEnabled()) {
169                 return;
170             }
171             if (!(activeModeManager instanceof ConcreteClientModeManager)) {
172                 return;
173             }
174             final ConcreteClientModeManager ccm = (ConcreteClientModeManager) activeModeManager;
175             // TODO: b/197670907 : Add client role ROLE_CLIENT_SECONDARY_INTERNET
176             if (ccm.getRole() != ROLE_CLIENT_SECONDARY_LONG_LIVED || !ccm.isSecondaryInternet()) {
177                 return;
178             }
179             if (mVerboseLoggingEnabled) {
180                 Log.v(TAG, "ClientModeManager for internet removed");
181             }
182             // A secondary cmm was removed because of the connection was lost, start scan
183             // to find the new network connection.
184             updateNetworkConnectionStates();
185             if (hasPendingConnectionRequests()) {
186                 startConnectivityScan();
187             }
188         }
189 
190         @Override
onActiveModeManagerRoleChanged(@onNull ActiveModeManager activeModeManager)191         public void onActiveModeManagerRoleChanged(@NonNull ActiveModeManager activeModeManager) {
192             if (!mActiveModeWarden.isStaStaConcurrencySupportedForMultiInternet()
193                     || !isStaConcurrencyForMultiInternetEnabled()) {
194                 return;
195             }
196             if (!(activeModeManager instanceof ConcreteClientModeManager)) {
197                 return;
198             }
199             final ConcreteClientModeManager ccm = (ConcreteClientModeManager) activeModeManager;
200             if (ccm.getPreviousRole() == ROLE_CLIENT_SECONDARY_LONG_LIVED
201                     && ccm.isSecondaryInternet()) {
202                 Log.w(TAG, "Secondary client mode manager changed role to "
203                         + ccm.getRole());
204             }
205             updateNetworkConnectionStates();
206         }
207     }
208 
209     private class ClientModeListenerInternal implements ClientModeImplListener {
210         @Override
onInternetValidated(@onNull ConcreteClientModeManager clientModeManager)211         public void onInternetValidated(@NonNull ConcreteClientModeManager clientModeManager) {
212             if (!mActiveModeWarden.isStaStaConcurrencySupportedForMultiInternet()
213                     || !isStaConcurrencyForMultiInternetEnabled()) {
214                 return;
215             }
216             final WifiInfo info = clientModeManager.syncRequestConnectionInfo();
217             if (info != null) {
218                 final int band = ScanResult.toBand(info.getFrequency());
219                 if (mNetworkConnectionStates.contains(band)) {
220                     mNetworkConnectionStates.get(band).setValidated(true);
221                 }
222             }
223             if (mVerboseLoggingEnabled) {
224                 Log.v(TAG, "ClientModeManager role " + clientModeManager.getRole()
225                         + " internet validated for connection");
226             }
227             // If the primary role was connected and internet validated, update the connection state
228             // immediately and issue scan for secondary network connection if needed.
229             // If the secondary role was connected and internet validated, update the connection
230             // state and notify connectivity manager.
231             // TODO: b/197670907 : Add client role ROLE_CLIENT_SECONDARY_INTERNET
232             if (clientModeManager.getRole() == ROLE_CLIENT_PRIMARY) {
233                 updateNetworkConnectionStates();
234                 final int band = findUnconnectedRequestBand();
235                 if (band != ScanResult.UNSPECIFIED) {
236                     // Trigger the connectivity scan
237                     mConnectionStatusListener.onStartScan(getRequestorWorkSource(band));
238                 }
239             } else if (clientModeManager.getRole() == ROLE_CLIENT_SECONDARY_LONG_LIVED
240                     && clientModeManager.isSecondaryInternet()) {
241                 updateNetworkConnectionStates();
242             }
243         }
244 
245         // TODO(b/175896748): not yet triggered by ClientModeImpl
246         @Override
onL3Connected(@onNull ConcreteClientModeManager clientModeManager)247         public void onL3Connected(@NonNull ConcreteClientModeManager clientModeManager) {
248             if (!mActiveModeWarden.isStaStaConcurrencySupportedForMultiInternet()
249                     || !isStaConcurrencyForMultiInternetEnabled()) {
250                 return;
251             }
252             // TODO: b/197670907 : Add client role ROLE_CLIENT_SECONDARY_INTERNET
253             if (clientModeManager.getRole() != ROLE_CLIENT_SECONDARY_LONG_LIVED
254                     || !clientModeManager.isSecondaryInternet()) {
255                 return;
256             }
257             updateNetworkConnectionStates();
258             // If no pending connection requests, update connection listener.
259             if (!hasPendingConnectionRequests()) {
260                 final int band = getSecondaryConnectedNetworkBand();
261                 if (band == ScanResult.UNSPECIFIED) return;
262                 final long connectionTime = mClock.getElapsedSinceBootMillis()
263                         - mNetworkConnectionStates.get(band).connectionStartTimeMillis;
264                 if (mVerboseLoggingEnabled) {
265                     Log.v(TAG, "ClientModeManager for internet L3 connected for "
266                             + connectionTime + " ms.");
267                 }
268                 handleConnectionStateChange(MULTI_INTERNET_STATE_CONNECTED,
269                         getRequestorWorkSource(band));
270             }
271         }
272 
273         @Override
onConnectionEnd(@onNull ConcreteClientModeManager clientModeManager)274         public void onConnectionEnd(@NonNull ConcreteClientModeManager clientModeManager) {
275             if (!mActiveModeWarden.isStaStaConcurrencySupportedForMultiInternet()
276                     || !isStaConcurrencyForMultiInternetEnabled()) {
277                 return;
278             }
279             if (clientModeManager.getRole() == ROLE_CLIENT_PRIMARY) {
280                 if (mVerboseLoggingEnabled) {
281                     Log.v(TAG, "Connection end on primary client mode manager");
282                 }
283                 // When the primary network connection is ended, disconnect the secondary network,
284                 // as the secondary network is opportunistic.
285                 // TODO: b/197670907 : Add client role ROLE_CLIENT_SECONDARY_INTERNET
286                 for (ConcreteClientModeManager cmm : mActiveModeWarden.getClientModeManagersInRoles(
287                         ROLE_CLIENT_SECONDARY_LONG_LIVED)) {
288                     if (cmm.isSecondaryInternet()) {
289                         if (mVerboseLoggingEnabled) {
290                             Log.v(TAG, "Disconnect secondary client mode manager");
291                         }
292                         cmm.disconnect();
293                     }
294                 }
295                 // As the secondary network is disconnected, mark all bands as disconnected.
296                 for (int i = 0; i < mNetworkConnectionStates.size(); i++) {
297                     mNetworkConnectionStates.valueAt(i).setConnected(false);
298                 }
299             }
300             updateNetworkConnectionStates();
301         }
302     }
303 
MultiInternetManager( @onNull ActiveModeWarden activeModeWarden, @NonNull FrameworkFacade frameworkFacade, @NonNull Context context, @NonNull ClientModeImplMonitor cmiMonitor, @NonNull WifiSettingsStore settingsStore, @NonNull Handler handler, @NonNull Clock clock)304     public MultiInternetManager(
305             @NonNull ActiveModeWarden activeModeWarden,
306             @NonNull FrameworkFacade frameworkFacade,
307             @NonNull Context context,
308             @NonNull ClientModeImplMonitor cmiMonitor,
309             @NonNull WifiSettingsStore settingsStore,
310             @NonNull Handler handler,
311             @NonNull Clock clock) {
312         mActiveModeWarden = activeModeWarden;
313         mFrameworkFacade = frameworkFacade;
314         mContext = context;
315         mCmiMonitor = cmiMonitor;
316         mSettingsStore = settingsStore;
317         mEventHandler = handler;
318         mClock = clock;
319         mActiveModeWarden.registerModeChangeCallback(new ModeChangeCallback());
320         cmiMonitor.registerListener(new ClientModeListenerInternal());
321         mStaConcurrencyMultiInternetMode = mSettingsStore.getWifiMultiInternetMode();
322     }
323 
324     /**
325      * Check if Wi-Fi multi internet use case is enabled.
326      *
327      * @return true if Wi-Fi multi internet use case is enabled.
328      */
isStaConcurrencyForMultiInternetEnabled()329     public boolean isStaConcurrencyForMultiInternetEnabled() {
330         return mStaConcurrencyMultiInternetMode
331             != WifiManager.WIFI_MULTI_INTERNET_MODE_DISABLED;
332     }
333 
334     /**
335      * Check if Wi-Fi multi internet use case allows multi AP.
336      *
337      * @return true if Wi-Fi multi internet use case allows multi AP.
338      */
isStaConcurrencyForMultiInternetMultiApAllowed()339     public boolean isStaConcurrencyForMultiInternetMultiApAllowed() {
340         return mStaConcurrencyMultiInternetMode
341                 == WifiManager.WIFI_MULTI_INTERNET_MODE_MULTI_AP;
342     }
343 
344     /**
345      * Return Wi-Fi multi internet use case mode.
346      *
347      * @return Current mode of Wi-Fi multi internet use case.
348      */
getStaConcurrencyForMultiInternetMode()349     public @WifiManager.WifiMultiInternetMode int getStaConcurrencyForMultiInternetMode() {
350         if (mActiveModeWarden.isStaStaConcurrencySupportedForMultiInternet()) {
351             return mStaConcurrencyMultiInternetMode;
352         }
353         return WifiManager.WIFI_MULTI_INTERNET_MODE_DISABLED;
354     }
355 
356     /**
357      * Set the multi internet use case mode.
358      * @return true if the mode set successfully, false if failed.
359      */
setStaConcurrencyForMultiInternetMode( @ifiManager.WifiMultiInternetMode int mode)360     public boolean setStaConcurrencyForMultiInternetMode(
361             @WifiManager.WifiMultiInternetMode int mode) {
362         final boolean enabled = (mode != WifiManager.WIFI_MULTI_INTERNET_MODE_DISABLED);
363         if (!mActiveModeWarden.isStaStaConcurrencySupportedForMultiInternet()) {
364             return false;
365         }
366 
367         if (mode == mStaConcurrencyMultiInternetMode) {
368             return true;
369         }
370         // If the STA+STA multi internet feature was disabled, disconnect the secondary cmm.
371         // TODO: b/197670907 : Add client role ROLE_CLIENT_SECONDARY_INTERNET
372         if (!enabled) {
373             for (ConcreteClientModeManager cmm : mActiveModeWarden.getClientModeManagersInRoles(
374                     ROLE_CLIENT_SECONDARY_LONG_LIVED)) {
375                 if (cmm.isSecondaryInternet()) {
376                     cmm.disconnect();
377                 }
378             }
379             for (int i = 0; i < mNetworkConnectionStates.size(); i++) {
380                 // Clear the connection state for all bands.
381                 mNetworkConnectionStates.setValueAt(i, new NetworkConnectionState(null));
382             }
383             handleConnectionStateChange(MULTI_INTERNET_STATE_NONE, null);
384         } else {
385             updateNetworkConnectionStates();
386             final int band = findUnconnectedRequestBand();
387             if (band != ScanResult.UNSPECIFIED) {
388                 handleConnectionStateChange(MULTI_INTERNET_STATE_CONNECTION_REQUESTED,
389                         getRequestorWorkSource(band));
390             }
391         }
392         mStaConcurrencyMultiInternetMode = mode;
393         mSettingsStore.handleWifiMultiInternetMode(mode);
394         // Check if there is already multi internet request then start scan for connection.
395         if (hasPendingConnectionRequests()) {
396             startConnectivityScan();
397         }
398         return true;
399     }
400 
setVerboseLoggingEnabled(boolean enabled)401     public void setVerboseLoggingEnabled(boolean enabled) {
402         mVerboseLoggingEnabled = enabled;
403     }
404 
405     /** Set the Multi Internet Connection Status listener.
406      *
407      * @param listener The Multi Internet Connection Status listener.
408      */
setConnectionStatusListener(ConnectionStatusListener listener)409     public void setConnectionStatusListener(ConnectionStatusListener listener) {
410         mConnectionStatusListener = listener;
411     }
412 
413     /** Notify the BSSID associated event from ClientModeImpl. Triggered by
414      *  WifiMonitor.ASSOCIATED_BSSID_EVENT.
415      *  @param clientModeManager the client mode manager with BSSID associated event.
416      */
notifyBssidAssociatedEvent(ConcreteClientModeManager clientModeManager)417     public void notifyBssidAssociatedEvent(ConcreteClientModeManager clientModeManager) {
418         if (clientModeManager.getRole() != ROLE_CLIENT_PRIMARY
419                 || !mActiveModeWarden.isStaStaConcurrencySupportedForMultiInternet()
420                 || !isStaConcurrencyForMultiInternetEnabled()) {
421             return;
422         }
423         // If primary CMM has associated to a new BSSID, need to check if it is in a different band
424         // of secondary CMM.
425         final WifiInfo info = clientModeManager.syncRequestConnectionInfo();
426         final ConcreteClientModeManager secondaryCcmm =
427                 mActiveModeWarden.getClientModeManagerInRole(ROLE_CLIENT_SECONDARY_LONG_LIVED);
428         // If no secondary client mode manager then it's ok
429         if (secondaryCcmm == null) return;
430         // If secondary client mode manager is not connected or not for secondary internet
431         if (!secondaryCcmm.isConnected() || !secondaryCcmm.isSecondaryInternet()) return;
432         final WifiInfo info2 = secondaryCcmm.syncRequestConnectionInfo();
433         // If secondary network is in same band as primary now
434         if (ScanResult.toBand(info.getFrequency()) == ScanResult.toBand(info2.getFrequency())) {
435             // Need to disconnect secondary network
436             secondaryCcmm.disconnect();
437             // As the secondary network is disconnected, mark all bands as disconnected.
438             for (int i = 0; i < mNetworkConnectionStates.size(); i++) {
439                 mNetworkConnectionStates.valueAt(i).setConnected(false);
440             }
441             updateNetworkConnectionStates();
442         }
443     }
444 
445     /** Check if there is a connection request for multi internet
446      * @return true if there is one or more connection request
447      */
hasPendingConnectionRequests()448     public boolean hasPendingConnectionRequests() {
449         return findUnconnectedRequestBand() != ScanResult.UNSPECIFIED;
450     }
451 
452     /**
453      * Check if there is connection request on a specific band.
454      * @param band The band for the connection request.
455      * @return true if there is connection request on specific band.
456      */
hasConnectionRequest(int band)457     public boolean hasConnectionRequest(int band) {
458         return mNetworkConnectionStates.contains(band)
459                 ? (getRequestorWorkSource(band) != null) : false;
460     }
461 
462     /**
463      * Check if there is unconnected network connection request.
464      * @return the band of the connection request that is still not connected.
465      */
findUnconnectedRequestBand()466     public int findUnconnectedRequestBand() {
467         for (int i = 0; i < mNetworkConnectionStates.size(); i++) {
468             if (!mNetworkConnectionStates.valueAt(i).isConnected()) {
469                 return mNetworkConnectionStates.keyAt(i);
470             }
471         }
472         return ScanResult.UNSPECIFIED;
473     }
474 
475     /**
476      * Traverse the client mode managers and update the internal connection states.
477      */
updateNetworkConnectionStates()478     private void updateNetworkConnectionStates() {
479         for (int i = 0; i < mNetworkConnectionStates.size(); i++) {
480             mNetworkConnectionStates.valueAt(i).setConnected(false);
481         }
482 
483         for (ClientModeManager clientModeManager :
484                 mActiveModeWarden.getInternetConnectivityClientModeManagers()) {
485             // TODO: b/197670907 : Add client role ROLE_CLIENT_SECONDARY_INTERNET
486             if (clientModeManager instanceof ConcreteClientModeManager
487                     && (clientModeManager.getRole() == ROLE_CLIENT_PRIMARY
488                     || clientModeManager.getRole() == ROLE_CLIENT_SECONDARY_LONG_LIVED)) {
489                 ConcreteClientModeManager ccmm = (ConcreteClientModeManager) clientModeManager;
490                 // Exclude the secondary client mode manager not for secondary internet.
491                 if (ccmm.getRole() == ROLE_CLIENT_SECONDARY_LONG_LIVED
492                         && !ccmm.isSecondaryInternet()) {
493                     continue;
494                 }
495                 WifiInfo info = clientModeManager.syncRequestConnectionInfo();
496                 // Exclude the network that is not connected or restricted.
497                 if (info == null || !clientModeManager.isConnected()
498                         ||  info.isRestricted()) continue;
499                 // Exclude the network that is oem paid/private.
500                 if (SdkLevel.isAtLeastT() && (info.isOemPaid() || info.isOemPrivate())) continue;
501                 final int band = ScanResult.toBand(info.getFrequency());
502                 if (mNetworkConnectionStates.contains(band)) {
503                     // Update the connected state
504                     mNetworkConnectionStates.get(band).setConnected(true);
505                 }
506                 if (mVerboseLoggingEnabled) {
507                     Log.v(TAG, "network band " + band + " role "
508                             + clientModeManager.getRole().toString());
509                 }
510             }
511         }
512         // Handle the state change and notify listener
513         if (!hasPendingConnectionRequests()) {
514             if (mNetworkConnectionStates.size() == 0) {
515                 handleConnectionStateChange(MULTI_INTERNET_STATE_NONE, null);
516             } else {
517                 final int band = getSecondaryConnectedNetworkBand();
518                 if (band == ScanResult.UNSPECIFIED) return;
519                 handleConnectionStateChange(MULTI_INTERNET_STATE_CONNECTED,
520                         getRequestorWorkSource(band));
521             }
522         } else {
523             final int band = findUnconnectedRequestBand();
524             handleConnectionStateChange(MULTI_INTERNET_STATE_CONNECTION_REQUESTED,
525                     getRequestorWorkSource(band));
526         }
527     }
528 
529     /**
530      * Set a network connection request from a requestor WorkSource for a specific band, or clear
531      * the connection request if the WorkSource is null.
532      * Triggered when {@link MultiInternetWifiNetworkFactory} has a pending network request.
533      * @param band The band of the Wi-Fi network requested.
534      * @param requestorWs The requestor's WorkSource. Null to clear a network request for a
535      * a band.
536      */
setMultiInternetConnectionWorksource(int band, WorkSource requestorWs)537     public void setMultiInternetConnectionWorksource(int band, WorkSource requestorWs) {
538         if (!isStaConcurrencyForMultiInternetEnabled()) {
539             Log.w(TAG, "MultInternet is not enabled.");
540             return;
541         }
542         if (mVerboseLoggingEnabled) {
543             Log.v(TAG, "setMultiInternetConnectionWorksource: band=" + band + ", requestorWs="
544                     + requestorWs);
545         }
546         if (requestorWs == null) {
547             // Disconnect secondary network if the request is removed.
548             if (band == getSecondaryConnectedNetworkBand()) {
549                 for (ConcreteClientModeManager cmm : mActiveModeWarden.getClientModeManagersInRoles(
550                         ROLE_CLIENT_SECONDARY_LONG_LIVED)) {
551                     if (cmm.isSecondaryInternet()) {
552                         cmm.disconnect();
553                     }
554                 }
555             }
556             mNetworkConnectionStates.remove(band);
557             updateNetworkConnectionStates();
558             return;
559         }
560         if (mNetworkConnectionStates.contains(band)) {
561             Log.w(TAG, "band " + band + " already requested.");
562         }
563         mNetworkConnectionStates.put(band, new NetworkConnectionState(requestorWs,
564                     mClock.getElapsedSinceBootMillis()));
565         startConnectivityScan();
566     }
567 
568     /** Returns the band of the secondary network connected. */
getSecondaryConnectedNetworkBand()569     private int getSecondaryConnectedNetworkBand() {
570         final ConcreteClientModeManager secondaryCcmm =
571                 mActiveModeWarden.getClientModeManagerInRole(ROLE_CLIENT_SECONDARY_LONG_LIVED);
572         if (secondaryCcmm == null) {
573             return ScanResult.UNSPECIFIED;
574         }
575         final WifiInfo info = secondaryCcmm.syncRequestConnectionInfo();
576         // Make sure secondary network is connected.
577         if (info == null || !secondaryCcmm.isConnected() || !secondaryCcmm.isSecondaryInternet()) {
578             return ScanResult.UNSPECIFIED;
579         }
580         return ScanResult.toBand(info.getFrequency());
581     }
582 
583     /**
584      * Handles the connection state change and notifies the status listener.
585      * The listener will only be notified when the state changes. If the state remains the same
586      * but with a different requestor WorkSource then the listener is not notified.
587      *
588      * @param state
589      * @param workSource
590      */
handleConnectionStateChange(int state, WorkSource workSource)591     private void handleConnectionStateChange(int state, WorkSource workSource) {
592         if (mMultiInternetConnectionState == state) {
593             return;
594         }
595         mMultiInternetConnectionState = state;
596         mConnectionStatusListener.onStatusChange(state, workSource);
597     }
598 
599     /**
600      * Start a connectivity scan to trigger the network selection process and connect to
601      * the requested multi internet networks.
602      */
startConnectivityScan()603     private void startConnectivityScan() {
604         if (!isStaConcurrencyForMultiInternetEnabled()) {
605             return;
606         }
607         updateNetworkConnectionStates();
608 
609         final int band = findUnconnectedRequestBand();
610         if (band == ScanResult.UNSPECIFIED) return;
611         NetworkConnectionState state = mNetworkConnectionStates.get(band);
612         if (mVerboseLoggingEnabled) {
613             Log.v(TAG, "Schedule connectivity scan for network request with band " + band
614                     + " start time " + state.connectionStartTimeMillis + " now "
615                     + mClock.getElapsedSinceBootMillis());
616         }
617         // Trigger the connectivity scan
618         mConnectionStatusListener.onStartScan(getRequestorWorkSource(band));
619     }
620 
621     /** Dump the internal states of MultiInternetManager */
dump(FileDescriptor fd, PrintWriter pw, String[] args)622     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
623         pw.println("Dump of MultiInternetManager");
624         pw.println(TAG + ": mStaConcurrencyMultiInternetMode "
625                 + mStaConcurrencyMultiInternetMode);
626         for (int i = 0; i < mNetworkConnectionStates.size(); i++) {
627             pw.println("band " + mNetworkConnectionStates.keyAt(i) + " connected "
628                     + mNetworkConnectionStates.valueAt(i).isConnected()
629                     + " validated " + mNetworkConnectionStates.valueAt(i).isValidated());
630         }
631     }
632 
633 }
634