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