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