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.telephony.qns; 18 19 import android.annotation.NonNull; 20 import android.content.Context; 21 import android.location.Country; 22 import android.location.CountryDetector; 23 import android.net.ConnectivityManager; 24 import android.net.LinkAddress; 25 import android.net.LinkProperties; 26 import android.net.Network; 27 import android.net.NetworkCapabilities; 28 import android.net.NetworkSpecifier; 29 import android.net.TelephonyNetworkSpecifier; 30 import android.net.TransportInfo; 31 import android.net.vcn.VcnTransportInfo; 32 import android.net.vcn.VcnUtils; 33 import android.os.Handler; 34 import android.os.HandlerThread; 35 import android.os.Looper; 36 import android.os.Message; 37 import android.telephony.TelephonyManager; 38 import android.text.TextUtils; 39 import android.util.Log; 40 import android.util.SparseArray; 41 42 import com.android.internal.annotations.VisibleForTesting; 43 44 import java.io.PrintWriter; 45 import java.net.Inet4Address; 46 import java.net.Inet6Address; 47 import java.net.InetAddress; 48 import java.util.ArrayList; 49 import java.util.List; 50 import java.util.Map; 51 import java.util.concurrent.ConcurrentHashMap; 52 53 /** 54 * IwlanNetworkStatusTracker monitors if there is a network available for IWLAN and informs it to 55 * registrants. 56 */ 57 class IwlanNetworkStatusTracker { 58 private static final Boolean DBG = true; 59 private static final String sLogTag = IwlanNetworkStatusTracker.class.getSimpleName(); 60 private static final int EVENT_BASE = 1000; 61 private static final int EVENT_IWLAN_SERVICE_STATE_CHANGED = EVENT_BASE; 62 private final Map<Integer, QnsRegistrantList> mIwlanNetworkListenersArray = 63 new ConcurrentHashMap<>(); 64 private static final String LAST_KNOWN_COUNTRY_CODE_KEY = "last_known_country_code"; 65 private static final int INVALID_SUB_ID = -1; 66 private final SparseArray<QnsCarrierConfigManager> mQnsConfigManagers = new SparseArray<>(); 67 private final SparseArray<QnsEventDispatcher> mQnsEventDispatchers = new SparseArray<>(); 68 private final SparseArray<QnsImsManager> mQnsImsManagers = new SparseArray<>(); 69 private final SparseArray<QnsTelephonyListener> mQnsTelephonyListeners = new SparseArray<>(); 70 private final Context mContext; 71 private DefaultNetworkCallback mDefaultNetworkCallback; 72 private final HandlerThread mHandlerThread; 73 private final ConnectivityManager mConnectivityManager; 74 private final TelephonyManager mTelephonyManager; 75 private Handler mNetCbHandler; 76 private String mLastKnownCountryCode; 77 private boolean mWifiAvailable = false; 78 private boolean mWifiToggleOn = false; 79 private Map<Integer, Boolean> mIwlanRegistered = new ConcurrentHashMap<>(); 80 81 // The current active data subscription. May not be the default data subscription. 82 private int mConnectedDataSub = INVALID_SUB_ID; 83 @VisibleForTesting SparseArray<IwlanEventHandler> mHandlerSparseArray = new SparseArray<>(); 84 @VisibleForTesting SparseArray<IwlanAvailabilityInfo> mLastIwlanAvailabilityInfo = 85 new SparseArray<>(); 86 private CountryDetector mCountryDetector; 87 88 enum LinkProtocolType { 89 UNKNOWN, 90 IPV4, 91 IPV6, 92 IPV4V6; 93 } 94 95 private static LinkProtocolType sLinkProtocolType = LinkProtocolType.UNKNOWN; 96 97 class IwlanEventHandler extends Handler { 98 private final int mSlotIndex; 99 IwlanEventHandler(int slotId, Looper l)100 IwlanEventHandler(int slotId, Looper l) { 101 super(l); 102 mSlotIndex = slotId; 103 List<Integer> events = new ArrayList<>(); 104 events.add(QnsEventDispatcher.QNS_EVENT_CROSS_SIM_CALLING_ENABLED); 105 events.add(QnsEventDispatcher.QNS_EVENT_CROSS_SIM_CALLING_DISABLED); 106 events.add(QnsEventDispatcher.QNS_EVENT_WIFI_DISABLING); 107 events.add(QnsEventDispatcher.QNS_EVENT_WIFI_ENABLED); 108 mQnsEventDispatchers.get(mSlotIndex).registerEvent(events, this); 109 mQnsTelephonyListeners 110 .get(mSlotIndex) 111 .registerIwlanServiceStateListener( 112 this, EVENT_IWLAN_SERVICE_STATE_CHANGED, null); 113 } 114 115 @Override handleMessage(Message message)116 public void handleMessage(Message message) { 117 Log.d(sLogTag, "handleMessage msg=" + message.what); 118 switch (message.what) { 119 case QnsEventDispatcher.QNS_EVENT_CROSS_SIM_CALLING_ENABLED: 120 onCrossSimEnabledEvent(true, mSlotIndex); 121 break; 122 case QnsEventDispatcher.QNS_EVENT_CROSS_SIM_CALLING_DISABLED: 123 onCrossSimEnabledEvent(false, mSlotIndex); 124 break; 125 case QnsEventDispatcher.QNS_EVENT_WIFI_ENABLED: 126 onWifiEnabled(); 127 break; 128 case QnsEventDispatcher.QNS_EVENT_WIFI_DISABLING: 129 onWifiDisabling(); 130 break; 131 case EVENT_IWLAN_SERVICE_STATE_CHANGED: 132 QnsAsyncResult ar = (QnsAsyncResult) message.obj; 133 boolean isRegistered = (boolean) ar.mResult; 134 onIwlanServiceStateChanged(mSlotIndex, isRegistered); 135 break; 136 default: 137 Log.d(sLogTag, "Unknown message received!"); 138 break; 139 } 140 } 141 } 142 IwlanNetworkStatusTracker(@onNull Context context)143 IwlanNetworkStatusTracker(@NonNull Context context) { 144 mContext = context; 145 mHandlerThread = new HandlerThread(IwlanNetworkStatusTracker.class.getSimpleName()); 146 mHandlerThread.start(); 147 Looper looper = mHandlerThread.getLooper(); 148 mNetCbHandler = new Handler(looper); 149 mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); 150 mTelephonyManager = mContext.getSystemService(TelephonyManager.class); 151 mLastIwlanAvailabilityInfo.clear(); 152 registerDefaultNetworkCb(); 153 Log.d(sLogTag, "Registered with Connectivity Service"); 154 startCountryDetector(); 155 } 156 initBySlotIndex( @onNull QnsCarrierConfigManager configManager, @NonNull QnsEventDispatcher dispatcher, @NonNull QnsImsManager imsManager, @NonNull QnsTelephonyListener telephonyListener, int slotId)157 void initBySlotIndex( 158 @NonNull QnsCarrierConfigManager configManager, 159 @NonNull QnsEventDispatcher dispatcher, 160 @NonNull QnsImsManager imsManager, 161 @NonNull QnsTelephonyListener telephonyListener, 162 int slotId) { 163 mQnsConfigManagers.put(slotId, configManager); 164 mQnsEventDispatchers.put(slotId, dispatcher); 165 mQnsImsManagers.put(slotId, imsManager); 166 mQnsTelephonyListeners.put(slotId, telephonyListener); 167 mHandlerSparseArray.put(slotId, new IwlanEventHandler(slotId, mHandlerThread.getLooper())); 168 } 169 closeBySlotIndex(int slotId)170 void closeBySlotIndex(int slotId) { 171 IwlanEventHandler handler = mHandlerSparseArray.get(slotId); 172 mQnsEventDispatchers.get(slotId).unregisterEvent(handler); 173 mQnsTelephonyListeners.get(slotId).unregisterIwlanServiceStateChanged(handler); 174 mIwlanNetworkListenersArray.remove(slotId); 175 mQnsConfigManagers.remove(slotId); 176 mQnsEventDispatchers.remove(slotId); 177 mQnsImsManagers.remove(slotId); 178 mQnsTelephonyListeners.remove(slotId); 179 mHandlerSparseArray.remove(slotId); 180 } 181 182 @VisibleForTesting onCrossSimEnabledEvent(boolean enabled, int slotId)183 void onCrossSimEnabledEvent(boolean enabled, int slotId) { 184 Log.d(sLogTag, "onCrossSimEnabledEvent enabled:" + enabled + " slotIndex:" + slotId); 185 if (enabled) { 186 int activeDataSub = INVALID_SUB_ID; 187 NetworkSpecifier specifier; 188 final Network activeNetwork = mConnectivityManager.getActiveNetwork(); 189 if (activeNetwork != null) { 190 final NetworkCapabilities nc = 191 mConnectivityManager.getNetworkCapabilities(activeNetwork); 192 if (nc != null && nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { 193 specifier = nc.getNetworkSpecifier(); 194 TransportInfo transportInfo = nc.getTransportInfo(); 195 if (transportInfo instanceof VcnTransportInfo) { 196 activeDataSub = VcnUtils.getSubIdFromVcnCaps(mConnectivityManager, nc); 197 } else if (specifier instanceof TelephonyNetworkSpecifier) { 198 activeDataSub = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId(); 199 } 200 if (activeDataSub != INVALID_SUB_ID && activeDataSub != mConnectedDataSub) { 201 mConnectedDataSub = activeDataSub; 202 } 203 } 204 } 205 notifyIwlanNetworkStatus(); 206 } else { 207 notifyIwlanNetworkStatus(true); 208 } 209 } 210 211 @VisibleForTesting onWifiEnabled()212 void onWifiEnabled() { 213 mWifiToggleOn = true; 214 if (!mWifiAvailable) { 215 for (Integer slotId : mIwlanNetworkListenersArray.keySet()) { 216 if (!isCrossSimCallingCondition(slotId) 217 && mIwlanRegistered.containsKey(slotId) 218 && mIwlanRegistered.get(slotId)) { 219 mWifiAvailable = true; 220 notifyIwlanNetworkStatus(slotId, false); 221 } 222 } 223 } 224 } 225 226 @VisibleForTesting onWifiDisabling()227 void onWifiDisabling() { 228 mWifiToggleOn = false; 229 if (mWifiAvailable) { 230 mWifiAvailable = false; 231 notifyIwlanNetworkStatus(true); 232 } 233 } 234 235 @VisibleForTesting onIwlanServiceStateChanged(int slotId, boolean isRegistered)236 void onIwlanServiceStateChanged(int slotId, boolean isRegistered) { 237 mIwlanRegistered.put(slotId, isRegistered); 238 notifyIwlanNetworkStatus(slotId, false); 239 } 240 notifyIwlanNetworkStatusToRegister(int slotId, QnsRegistrant r)241 private void notifyIwlanNetworkStatusToRegister(int slotId, QnsRegistrant r) { 242 if (DBG) { 243 Log.d(sLogTag, "notifyIwlanNetworkStatusToRegister"); 244 } 245 IwlanAvailabilityInfo info = mLastIwlanAvailabilityInfo.get(slotId); 246 if (info == null) { 247 info = makeIwlanAvailabilityInfo(slotId); 248 mLastIwlanAvailabilityInfo.put(slotId, info); 249 } 250 r.notifyResult(info); 251 } 252 registerDefaultNetworkCb()253 private void registerDefaultNetworkCb() { 254 if (mDefaultNetworkCallback == null) { 255 mDefaultNetworkCallback = new DefaultNetworkCallback(); 256 mConnectivityManager.registerDefaultNetworkCallback( 257 mDefaultNetworkCallback, mNetCbHandler); 258 } 259 } 260 unregisterDefaultNetworkCb()261 private void unregisterDefaultNetworkCb() { 262 if (mDefaultNetworkCallback != null) { 263 mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback); 264 mDefaultNetworkCallback = null; 265 } 266 } 267 close()268 protected void close() { 269 mNetCbHandler.post(this::onClose); 270 mHandlerThread.quitSafely(); 271 } 272 onClose()273 private void onClose() { 274 unregisterDefaultNetworkCb(); 275 mLastIwlanAvailabilityInfo.clear(); 276 mIwlanNetworkListenersArray.clear(); 277 mIwlanRegistered.clear(); 278 mCountryDetector.unregisterCountryDetectorCallback(this::updateCountryCode); 279 Log.d(sLogTag, "closed IwlanNetworkStatusTracker"); 280 } 281 registerIwlanNetworksChanged(int slotId, Handler h, int what)282 public void registerIwlanNetworksChanged(int slotId, Handler h, int what) { 283 if (h != null && mHandlerThread.isAlive()) { 284 QnsRegistrant r = new QnsRegistrant(h, what, null); 285 if (mIwlanNetworkListenersArray.get(slotId) == null) { 286 mIwlanNetworkListenersArray.put(slotId, new QnsRegistrantList()); 287 } 288 mIwlanNetworkListenersArray.get(slotId).add(r); 289 IwlanEventHandler handler = mHandlerSparseArray.get(slotId); 290 if (handler != null) { 291 IwlanAvailabilityInfo lastInfo = mLastIwlanAvailabilityInfo.get(slotId); 292 IwlanAvailabilityInfo newInfo = makeIwlanAvailabilityInfo(slotId); 293 if (lastInfo == null || !lastInfo.equals(newInfo)) { 294 // if the LastIwlanAvailabilityInfo is no more valid, notify to all registrants. 295 handler.post(() -> notifyIwlanNetworkStatus()); 296 } else { 297 // if the LastIwlanAvailabilityInfo is valid, notify to only this registrant. 298 handler.post(() -> notifyIwlanNetworkStatusToRegister(slotId, r)); 299 } 300 } 301 } 302 } 303 unregisterIwlanNetworksChanged(int slotId, Handler h)304 void unregisterIwlanNetworksChanged(int slotId, Handler h) { 305 if (mIwlanNetworkListenersArray.get(slotId) != null) { 306 mIwlanNetworkListenersArray.get(slotId).remove(h); 307 } 308 } 309 makeIwlanAvailabilityInfo(int slotId)310 private IwlanAvailabilityInfo makeIwlanAvailabilityInfo(int slotId) { 311 boolean iwlanEnable = false; 312 boolean isCrossWfc = false; 313 boolean isRegistered = false; 314 boolean isBlockIpv6OnlyWifi = false; 315 if (mQnsConfigManagers.contains(slotId)) { 316 isBlockIpv6OnlyWifi = mQnsConfigManagers.get(slotId).blockIpv6OnlyWifi(); 317 } 318 LinkProtocolType linkProtocolType = sLinkProtocolType; 319 320 if (mIwlanRegistered.containsKey(slotId)) { 321 isRegistered = mIwlanRegistered.get(slotId); 322 } 323 324 if (mWifiAvailable) { 325 boolean blockWifi = 326 isBlockIpv6OnlyWifi 327 && ((linkProtocolType == LinkProtocolType.UNKNOWN) 328 || (linkProtocolType == LinkProtocolType.IPV6)); 329 iwlanEnable = !blockWifi && isRegistered; 330 } else if (isCrossSimCallingCondition(slotId) && isRegistered) { 331 iwlanEnable = true; 332 isCrossWfc = true; 333 } 334 if (DBG) { 335 if (QnsUtils.isCrossSimCallingEnabled(mQnsImsManagers.get(slotId))) { 336 Log.d( 337 sLogTag, 338 "makeIwlanAvailabilityInfo(slot:" 339 + slotId 340 + ") " 341 + "mWifiAvailable:" 342 + mWifiAvailable 343 + " mConnectedDataSub:" 344 + mConnectedDataSub 345 + " isRegistered:" 346 + isRegistered 347 + " subId:" 348 + QnsUtils.getSubId(mContext, slotId) 349 + " isDDS:" 350 + QnsUtils.isDefaultDataSubs(slotId) 351 + " iwlanEnable:" 352 + iwlanEnable 353 + " isCrossWfc:" 354 + isCrossWfc); 355 } else { 356 Log.d( 357 sLogTag, 358 "makeIwlanAvailabilityInfo(slot:" 359 + slotId 360 + ")" 361 + " mWifiAvailable:" 362 + mWifiAvailable 363 + " isRegistered:" 364 + isRegistered 365 + " iwlanEnable:" 366 + iwlanEnable 367 + " isCrossWfc:" 368 + isCrossWfc 369 + " isBlockIpv6OnlyWifi:" 370 + isBlockIpv6OnlyWifi 371 + " linkProtocolType:" 372 + linkProtocolType); 373 } 374 } 375 return new IwlanAvailabilityInfo(iwlanEnable, isCrossWfc); 376 } 377 isCrossSimCallingCondition(int slotId)378 private boolean isCrossSimCallingCondition(int slotId) { 379 return QnsUtils.isCrossSimCallingEnabled(mQnsImsManagers.get(slotId)) 380 && QnsUtils.getSubId(mContext, slotId) != mConnectedDataSub 381 && mConnectedDataSub != INVALID_SUB_ID; 382 } 383 notifyIwlanNetworkStatus()384 private void notifyIwlanNetworkStatus() { 385 notifyIwlanNetworkStatus(false); 386 } 387 notifyIwlanNetworkStatus(boolean notifyIwlanDisabled)388 private void notifyIwlanNetworkStatus(boolean notifyIwlanDisabled) { 389 for (Integer slotId : mIwlanNetworkListenersArray.keySet()) { 390 notifyIwlanNetworkStatus(slotId, notifyIwlanDisabled); 391 } 392 } 393 notifyIwlanNetworkStatus(int slotId, boolean notifyIwlanDisabled)394 private void notifyIwlanNetworkStatus(int slotId, boolean notifyIwlanDisabled) { 395 Log.d(sLogTag, "notifyIwlanNetworkStatus for slot: " + slotId); 396 IwlanAvailabilityInfo info = makeIwlanAvailabilityInfo(slotId); 397 if (!info.getIwlanAvailable() && notifyIwlanDisabled) { 398 Log.d(sLogTag, "setNotifyIwlanDisabled for slot: " + slotId); 399 info.setNotifyIwlanDisabled(); 400 } 401 if (!info.equals(mLastIwlanAvailabilityInfo.get(slotId))) { 402 Log.d(sLogTag, "notify updated info for slot: " + slotId); 403 if (mIwlanNetworkListenersArray.get(slotId) != null) { 404 mIwlanNetworkListenersArray.get(slotId).notifyResult(info); 405 } 406 mLastIwlanAvailabilityInfo.put(slotId, info); 407 } 408 } 409 410 class IwlanAvailabilityInfo { 411 private boolean mIwlanAvailable = false; 412 private boolean mIsCrossWfc = false; 413 private boolean mNotifyIwlanDisabled = false; 414 IwlanAvailabilityInfo(boolean iwlanAvailable, boolean crossWfc)415 IwlanAvailabilityInfo(boolean iwlanAvailable, boolean crossWfc) { 416 mIwlanAvailable = iwlanAvailable; 417 mIsCrossWfc = crossWfc; 418 } 419 420 @VisibleForTesting setNotifyIwlanDisabled()421 void setNotifyIwlanDisabled() { 422 mNotifyIwlanDisabled = true; 423 } 424 getIwlanAvailable()425 boolean getIwlanAvailable() { 426 return mIwlanAvailable; 427 } 428 isCrossWfc()429 boolean isCrossWfc() { 430 return mIsCrossWfc; 431 } 432 433 @VisibleForTesting getNotifyIwlanDisabled()434 boolean getNotifyIwlanDisabled() { 435 return mNotifyIwlanDisabled; 436 } 437 equals(IwlanAvailabilityInfo info)438 boolean equals(IwlanAvailabilityInfo info) { 439 if (info == null) { 440 Log.d(sLogTag, " equals info is null"); 441 return false; 442 } 443 Log.d( 444 sLogTag, 445 "equals() IwlanAvailable: " 446 + mIwlanAvailable 447 + "/" 448 + info.mIwlanAvailable 449 + " IsCrossWfc: " 450 + mIsCrossWfc 451 + "/" 452 + info.mIsCrossWfc 453 + " NotifyIwlanDisabled: " 454 + mNotifyIwlanDisabled 455 + "/" 456 + info.mNotifyIwlanDisabled); 457 return (mIwlanAvailable == info.mIwlanAvailable) 458 && (mIsCrossWfc == info.mIsCrossWfc) 459 && (mNotifyIwlanDisabled == info.mNotifyIwlanDisabled); 460 } 461 } 462 463 final class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback { 464 /** Called when the framework connects and has declared a new network ready for use. */ 465 @Override onAvailable(Network network)466 public void onAvailable(Network network) { 467 Log.d(sLogTag, "onAvailable: " + network); 468 if (mConnectivityManager != null) { 469 NetworkCapabilities nc = mConnectivityManager.getNetworkCapabilities(network); 470 if (nc != null) { 471 if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { 472 mWifiToggleOn = true; 473 mWifiAvailable = true; 474 mConnectedDataSub = INVALID_SUB_ID; 475 notifyIwlanNetworkStatus(); 476 } else if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { 477 NetworkSpecifier specifier = nc.getNetworkSpecifier(); 478 TransportInfo transportInfo = nc.getTransportInfo(); 479 if (transportInfo instanceof VcnTransportInfo) { 480 mConnectedDataSub = 481 VcnUtils.getSubIdFromVcnCaps(mConnectivityManager, nc); 482 } else if (specifier instanceof TelephonyNetworkSpecifier) { 483 mConnectedDataSub = 484 ((TelephonyNetworkSpecifier) specifier).getSubscriptionId(); 485 } 486 mWifiAvailable = false; 487 notifyIwlanNetworkStatus(); 488 } 489 } 490 } 491 } 492 493 /** 494 * Called when the network is about to be lost, typically because there are no outstanding 495 * requests left for it. This may be paired with a {@link 496 * android.net.ConnectivityManager.NetworkCallback#onAvailable} call with the new 497 * replacement network for graceful handover. This method is not guaranteed to be called 498 * before {@link android.net.ConnectivityManager.NetworkCallback#onLost} is called, for 499 * example in case a network is suddenly disconnected. 500 */ 501 @Override onLosing(Network network, int maxMsToLive)502 public void onLosing(Network network, int maxMsToLive) { 503 Log.d(sLogTag, "onLosing: maxMsToLive: " + maxMsToLive + " network: " + network); 504 } 505 506 /** 507 * Called when a network disconnects or otherwise no longer satisfies this request or * 508 * callback. 509 */ 510 @Override onLost(Network network)511 public void onLost(Network network) { 512 Log.d(sLogTag, "onLost: " + network); 513 if (mWifiAvailable) { 514 mWifiAvailable = false; 515 } 516 if (mConnectedDataSub != INVALID_SUB_ID) { 517 mConnectedDataSub = INVALID_SUB_ID; 518 } 519 sLinkProtocolType = LinkProtocolType.UNKNOWN; 520 notifyIwlanNetworkStatus(); 521 } 522 523 /** Called when the network corresponding to this request changes {@link LinkProperties}. */ 524 @Override onLinkPropertiesChanged(Network network, LinkProperties linkProperties)525 public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) { 526 Log.d(sLogTag, "onLinkPropertiesChanged: " + linkProperties); 527 if (mWifiAvailable) { 528 LinkProtocolType prevType = sLinkProtocolType; 529 530 checkWifiLinkProtocolType(linkProperties); 531 if (prevType != LinkProtocolType.IPV6 532 && sLinkProtocolType == LinkProtocolType.IPV6) { 533 notifyIwlanNetworkStatus(true); 534 } else if (prevType != sLinkProtocolType) { 535 notifyIwlanNetworkStatus(); 536 } 537 } 538 } 539 540 /** Called when access to the specified network is blocked or unblocked. */ 541 @Override onBlockedStatusChanged(Network network, boolean blocked)542 public void onBlockedStatusChanged(Network network, boolean blocked) { 543 Log.d(sLogTag, "onBlockedStatusChanged: " + " BLOCKED:" + blocked); 544 } 545 546 @Override onCapabilitiesChanged( Network network, NetworkCapabilities networkCapabilities)547 public void onCapabilitiesChanged( 548 Network network, NetworkCapabilities networkCapabilities) { 549 // onCapabilitiesChanged is guaranteed to be called immediately after onAvailable per 550 // API 551 Log.d(sLogTag, "onCapabilitiesChanged: " + network); 552 NetworkCapabilities nc = networkCapabilities; 553 if (nc != null) { 554 if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { 555 if (!mWifiAvailable && mWifiToggleOn) { 556 mWifiAvailable = true; 557 mConnectedDataSub = INVALID_SUB_ID; 558 notifyIwlanNetworkStatus(); 559 } else { 560 Log.d(sLogTag, "OnCapability : Wifi Available already true"); 561 } 562 } else if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { 563 int activeDataSub = INVALID_SUB_ID; 564 mWifiAvailable = false; 565 NetworkSpecifier specifier = nc.getNetworkSpecifier(); 566 TransportInfo transportInfo = nc.getTransportInfo(); 567 if (transportInfo instanceof VcnTransportInfo) { 568 activeDataSub = VcnUtils.getSubIdFromVcnCaps(mConnectivityManager, nc); 569 } else if (specifier instanceof TelephonyNetworkSpecifier) { 570 activeDataSub = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId(); 571 } 572 if (activeDataSub != INVALID_SUB_ID && activeDataSub != mConnectedDataSub) { 573 mConnectedDataSub = activeDataSub; 574 notifyIwlanNetworkStatus(); 575 } 576 } 577 } 578 } 579 } 580 checkWifiLinkProtocolType(@onNull LinkProperties linkProperties)581 private void checkWifiLinkProtocolType(@NonNull LinkProperties linkProperties) { 582 boolean hasIpv4 = false; 583 boolean hasIpv6 = false; 584 for (LinkAddress linkAddress : linkProperties.getLinkAddresses()) { 585 InetAddress inetAddress = linkAddress.getAddress(); 586 if (inetAddress instanceof Inet4Address) { 587 hasIpv4 = true; 588 } else if (inetAddress instanceof Inet6Address) { 589 hasIpv6 = true; 590 } 591 } 592 if (hasIpv4 && hasIpv6) { 593 sLinkProtocolType = LinkProtocolType.IPV4V6; 594 } else if (hasIpv4) { 595 sLinkProtocolType = LinkProtocolType.IPV4; 596 } else if (hasIpv6) { 597 sLinkProtocolType = LinkProtocolType.IPV6; 598 } 599 } 600 601 /** 602 * This method returns if current country code is outside the home country. 603 * 604 * @return True if it is international roaming, otherwise false. 605 */ isInternationalRoaming(int slotId)606 boolean isInternationalRoaming(int slotId) { 607 boolean isInternationalRoaming = false; 608 String simCountry = mTelephonyManager.createForSubscriptionId(slotId).getSimCountryIso(); 609 if (!TextUtils.isEmpty(simCountry) && !TextUtils.isEmpty(mLastKnownCountryCode)) { 610 Log.d( 611 sLogTag, 612 "SIM country = " + simCountry + ", current country = " + mLastKnownCountryCode); 613 isInternationalRoaming = !simCountry.equalsIgnoreCase(mLastKnownCountryCode); 614 } 615 return isInternationalRoaming; 616 } 617 618 /** 619 * This method is to add country listener in order to receive country code from the detector. 620 */ startCountryDetector()621 private void startCountryDetector() { 622 mCountryDetector = mContext.getSystemService(CountryDetector.class); 623 if (mCountryDetector != null) { 624 mCountryDetector.registerCountryDetectorCallback( 625 new QnsUtils.QnsExecutor(mNetCbHandler), this::updateCountryCode); 626 } 627 } 628 629 /** This method is to update the last known country code if it is changed. */ updateCountryCode(Country country)630 private void updateCountryCode(Country country) { 631 if (country == null) { 632 return; 633 } 634 if (country.getSource() == Country.COUNTRY_SOURCE_NETWORK 635 || country.getSource() == Country.COUNTRY_SOURCE_LOCATION) { 636 String newCountryCode = country.getCountryCode(); 637 if (!TextUtils.isEmpty(newCountryCode) 638 && (TextUtils.isEmpty(mLastKnownCountryCode) 639 || !mLastKnownCountryCode.equalsIgnoreCase(newCountryCode))) { 640 mLastKnownCountryCode = newCountryCode; 641 Log.d(sLogTag, "Update the last known country code = " + mLastKnownCountryCode); 642 } 643 } 644 } 645 646 /** 647 * Dumps the state of {@link QualityMonitor} 648 * 649 * @param pw {@link PrintWriter} to write the state of the object. 650 * @param prefix String to append at start of dumped log. 651 */ dump(PrintWriter pw, String prefix)652 void dump(PrintWriter pw, String prefix) { 653 pw.println(prefix + "------------------------------"); 654 pw.println(prefix + "IwlanNetworkStatusTracker:"); 655 pw.println( 656 prefix 657 + "mWifiAvailable=" 658 + mWifiAvailable 659 + ", mWifiToggleOn=" 660 + mWifiToggleOn 661 + ", mConnectedDataSub=" 662 + mConnectedDataSub 663 + ", mIwlanRegistered=" 664 + mIwlanRegistered); 665 pw.println(prefix + "sLinkProtocolType=" + sLinkProtocolType); 666 pw.println(prefix + "mLastKnownCountryCode=" + mLastKnownCountryCode); 667 } 668 } 669