1 /* 2 * Copyright 2018 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.internal.telephony.dataconnection; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.StringDef; 22 import android.os.AsyncResult; 23 import android.os.Handler; 24 import android.os.Message; 25 import android.os.RegistrantList; 26 import android.os.SystemProperties; 27 import android.telephony.AccessNetworkConstants; 28 import android.telephony.AccessNetworkConstants.AccessNetworkType; 29 import android.telephony.Annotation.ApnType; 30 import android.telephony.CarrierConfigManager; 31 import android.telephony.data.ApnSetting; 32 import android.util.LocalLog; 33 import android.util.SparseArray; 34 import android.util.SparseIntArray; 35 36 import com.android.internal.annotations.VisibleForTesting; 37 import com.android.internal.telephony.Phone; 38 import com.android.internal.telephony.RIL; 39 import com.android.internal.telephony.dataconnection.AccessNetworksManager.QualifiedNetworks; 40 import com.android.internal.telephony.util.ArrayUtils; 41 import com.android.internal.util.IndentingPrintWriter; 42 import com.android.telephony.Rlog; 43 44 import java.io.FileDescriptor; 45 import java.io.PrintWriter; 46 import java.lang.annotation.Retention; 47 import java.lang.annotation.RetentionPolicy; 48 import java.util.Arrays; 49 import java.util.HashMap; 50 import java.util.LinkedList; 51 import java.util.List; 52 import java.util.Map; 53 import java.util.concurrent.ConcurrentHashMap; 54 import java.util.stream.Collectors; 55 56 /** 57 * This class represents the transport manager which manages available transports (i.e. WWAN or 58 * WLAN) and determine the correct transport for {@link TelephonyNetworkFactory} to handle the data 59 * requests. 60 * 61 * The device can operate in the following modes, which is stored in the system properties 62 * ro.telephony.iwlan_operation_mode. If the system properties is missing, then it's tied to 63 * IRadio version. For 1.4 or above, it's AP-assisted mdoe. For 1.3 or below, it's legacy mode. 64 * 65 * Legacy mode: 66 * Frameworks send all data requests to the default data service, which is the cellular data 67 * service. IWLAN should be still reported as a RAT on cellular network service. 68 * 69 * AP-assisted mode: 70 * IWLAN is handled by IWLAN data service extending {@link android.telephony.data.DataService}, 71 * IWLAN network service extending {@link android.telephony.NetworkService}, and qualified 72 * network service extending {@link android.telephony.data.QualifiedNetworksService}. 73 * 74 * The following settings for service package name need to be configured properly for 75 * frameworks to bind. 76 * 77 * Package name of data service: 78 * The resource overlay 'config_wlan_data_service_package' or, 79 * the carrier config 80 * {@link CarrierConfigManager#KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING}. 81 * The carrier config takes precedence over the resource overlay if both exist. 82 * 83 * Package name of network service 84 * The resource overlay 'config_wlan_network_service_package' or 85 * the carrier config 86 * {@link CarrierConfigManager#KEY_CARRIER_NETWORK_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING}. 87 * The carrier config takes precedence over the resource overlay if both exist. 88 * 89 * Package name of qualified network service 90 * The resource overlay 'config_qualified_networks_service_package' or 91 * the carrier config 92 * {@link CarrierConfigManager# 93 * KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING}. 94 * The carrier config takes precedence over the resource overlay if both exist. 95 */ 96 public class TransportManager extends Handler { 97 private static final String TAG = TransportManager.class.getSimpleName(); 98 99 // Key is the access network, value is the transport. 100 private static final Map<Integer, Integer> ACCESS_NETWORK_TRANSPORT_TYPE_MAP; 101 102 static { 103 ACCESS_NETWORK_TRANSPORT_TYPE_MAP = new HashMap<>(); ACCESS_NETWORK_TRANSPORT_TYPE_MAP.put(AccessNetworkType.GERAN, AccessNetworkConstants.TRANSPORT_TYPE_WWAN)104 ACCESS_NETWORK_TRANSPORT_TYPE_MAP.put(AccessNetworkType.GERAN, 105 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); ACCESS_NETWORK_TRANSPORT_TYPE_MAP.put(AccessNetworkType.UTRAN, AccessNetworkConstants.TRANSPORT_TYPE_WWAN)106 ACCESS_NETWORK_TRANSPORT_TYPE_MAP.put(AccessNetworkType.UTRAN, 107 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); ACCESS_NETWORK_TRANSPORT_TYPE_MAP.put(AccessNetworkType.EUTRAN, AccessNetworkConstants.TRANSPORT_TYPE_WWAN)108 ACCESS_NETWORK_TRANSPORT_TYPE_MAP.put(AccessNetworkType.EUTRAN, 109 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); ACCESS_NETWORK_TRANSPORT_TYPE_MAP.put(AccessNetworkType.CDMA2000, AccessNetworkConstants.TRANSPORT_TYPE_WWAN)110 ACCESS_NETWORK_TRANSPORT_TYPE_MAP.put(AccessNetworkType.CDMA2000, 111 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); ACCESS_NETWORK_TRANSPORT_TYPE_MAP.put(AccessNetworkType.NGRAN, AccessNetworkConstants.TRANSPORT_TYPE_WWAN)112 ACCESS_NETWORK_TRANSPORT_TYPE_MAP.put(AccessNetworkType.NGRAN, 113 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); ACCESS_NETWORK_TRANSPORT_TYPE_MAP.put(AccessNetworkType.IWLAN, AccessNetworkConstants.TRANSPORT_TYPE_WLAN)114 ACCESS_NETWORK_TRANSPORT_TYPE_MAP.put(AccessNetworkType.IWLAN, 115 AccessNetworkConstants.TRANSPORT_TYPE_WLAN); 116 } 117 118 private static final int EVENT_QUALIFIED_NETWORKS_CHANGED = 1; 119 120 private static final int EVENT_UPDATE_AVAILABLE_NETWORKS = 2; 121 122 public static final String SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE = 123 "ro.telephony.iwlan_operation_mode"; 124 125 @Retention(RetentionPolicy.SOURCE) 126 @StringDef(prefix = {"IWLAN_OPERATION_MODE_"}, 127 value = { 128 IWLAN_OPERATION_MODE_DEFAULT, 129 IWLAN_OPERATION_MODE_LEGACY, 130 IWLAN_OPERATION_MODE_AP_ASSISTED}) 131 public @interface IwlanOperationMode {} 132 133 /** 134 * IWLAN default mode. On device that has IRadio 1.4 or above, it means 135 * {@link #IWLAN_OPERATION_MODE_AP_ASSISTED}. On device that has IRadio 1.3 or below, it means 136 * {@link #IWLAN_OPERATION_MODE_LEGACY}. 137 */ 138 public static final String IWLAN_OPERATION_MODE_DEFAULT = "default"; 139 140 /** 141 * IWLAN legacy mode. IWLAN is completely handled by the modem, and when the device is on 142 * IWLAN, modem reports IWLAN as a RAT. 143 */ 144 public static final String IWLAN_OPERATION_MODE_LEGACY = "legacy"; 145 146 /** 147 * IWLAN application processor assisted mode. IWLAN is handled by the bound IWLAN data service 148 * and network service separately. 149 */ 150 public static final String IWLAN_OPERATION_MODE_AP_ASSISTED = "AP-assisted"; 151 152 private final Phone mPhone; 153 154 private final LocalLog mLocalLog = new LocalLog(100); 155 156 /** The available transports. Must be one or more of AccessNetworkConstants.TransportType.XXX */ 157 private final int[] mAvailableTransports; 158 159 @Nullable 160 private AccessNetworksManager mAccessNetworksManager; 161 162 /** 163 * Current available networks. The key is the APN type, and the value is the available network 164 * list in the preferred order. 165 */ 166 private final SparseArray<int[]> mCurrentAvailableNetworks; 167 168 /** 169 * The queued available networks list. 170 */ 171 private final LinkedList<List<QualifiedNetworks>> mAvailableNetworksList; 172 173 /** 174 * The current transport of the APN type. The key is the APN type, and the value is the 175 * transport. 176 */ 177 private final Map<Integer, Integer> mCurrentTransports; 178 179 /** 180 * The pending handover list. This is a list of APNs that are being handover to the new 181 * transport. The entry will be removed once handover is completed. The key 182 * is the APN type, and the value is the target transport that the APN is handovered to. 183 */ 184 private final SparseIntArray mPendingHandoverApns; 185 186 /** 187 * The registrants for listening data handover needed events. 188 */ 189 private final RegistrantList mHandoverNeededEventRegistrants; 190 191 /** 192 * Handover parameters 193 */ 194 @VisibleForTesting 195 public static final class HandoverParams { 196 /** 197 * The callback for handover complete. 198 */ 199 public interface HandoverCallback { 200 /** 201 * Called when handover is completed. 202 * 203 * @param success {@true} if handover succeeded, otherwise failed. 204 * @param fallback {@true} if handover failed, the data connection fallback to the 205 * original transport 206 */ onCompleted(boolean success, boolean fallback)207 void onCompleted(boolean success, boolean fallback); 208 } 209 210 public final @ApnType int apnType; 211 public final int targetTransport; 212 public final HandoverCallback callback; 213 214 @VisibleForTesting HandoverParams(int apnType, int targetTransport, HandoverCallback callback)215 public HandoverParams(int apnType, int targetTransport, HandoverCallback callback) { 216 this.apnType = apnType; 217 this.targetTransport = targetTransport; 218 this.callback = callback; 219 } 220 } 221 TransportManager(Phone phone)222 public TransportManager(Phone phone) { 223 mPhone = phone; 224 mCurrentAvailableNetworks = new SparseArray<>(); 225 mCurrentTransports = new ConcurrentHashMap<>(); 226 mPendingHandoverApns = new SparseIntArray(); 227 mHandoverNeededEventRegistrants = new RegistrantList(); 228 mAvailableNetworksList = new LinkedList<>(); 229 230 if (isInLegacyMode()) { 231 log("operates in legacy mode."); 232 // For legacy mode, WWAN is the only transport to handle all data connections, even 233 // the IWLAN ones. 234 mAvailableTransports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN}; 235 } else { 236 log("operates in AP-assisted mode."); 237 mAccessNetworksManager = new AccessNetworksManager(phone); 238 mAccessNetworksManager.registerForQualifiedNetworksChanged(this, 239 EVENT_QUALIFIED_NETWORKS_CHANGED); 240 mAvailableTransports = new int[]{AccessNetworkConstants.TRANSPORT_TYPE_WWAN, 241 AccessNetworkConstants.TRANSPORT_TYPE_WLAN}; 242 } 243 } 244 245 @Override handleMessage(Message msg)246 public void handleMessage(Message msg) { 247 switch (msg.what) { 248 case EVENT_QUALIFIED_NETWORKS_CHANGED: 249 AsyncResult ar = (AsyncResult) msg.obj; 250 List<QualifiedNetworks> networks = (List<QualifiedNetworks>) ar.result; 251 mAvailableNetworksList.add(networks); 252 sendEmptyMessage(EVENT_UPDATE_AVAILABLE_NETWORKS); 253 break; 254 case EVENT_UPDATE_AVAILABLE_NETWORKS: 255 updateAvailableNetworks(); 256 break; 257 default: 258 loge("Unexpected event " + msg.what); 259 break; 260 } 261 } 262 isHandoverNeeded(QualifiedNetworks newNetworks)263 private boolean isHandoverNeeded(QualifiedNetworks newNetworks) { 264 int apnType = newNetworks.apnType; 265 int[] newNetworkList = newNetworks.qualifiedNetworks; 266 int[] currentNetworkList = mCurrentAvailableNetworks.get(apnType); 267 268 if (ArrayUtils.isEmpty(currentNetworkList) 269 && ACCESS_NETWORK_TRANSPORT_TYPE_MAP.get(newNetworkList[0]) 270 == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { 271 // This is a special case that when first time boot up in airplane mode with wifi on, 272 // qualified network service reports IWLAN as the preferred network. Although there 273 // is no live data connection on cellular, there might be network requests which were 274 // already sent to cellular DCT. In this case, we still need to handover the network 275 // request to the new transport. 276 return true; 277 } 278 279 // If the current network list is empty, but the new network list is not, then we can 280 // directly setup data on the new network. If the current network list is not empty, but 281 // the new network is, then we can tear down the data directly. Therefore if one of the 282 // list is empty, then we don't need to do handover. 283 if (ArrayUtils.isEmpty(newNetworkList) || ArrayUtils.isEmpty(currentNetworkList)) { 284 return false; 285 } 286 287 288 if (mPendingHandoverApns.get(newNetworks.apnType) 289 == ACCESS_NETWORK_TRANSPORT_TYPE_MAP.get(newNetworkList[0])) { 290 log("Handover not needed. There is already an ongoing handover."); 291 return false; 292 } 293 294 // The list is networks in the preferred order. For now we only pick the first element 295 // because it's the most preferred. In the future we should also consider the rest in the 296 // list, for example, the first one violates carrier/user policy. 297 return !ACCESS_NETWORK_TRANSPORT_TYPE_MAP.get(newNetworkList[0]) 298 .equals(getCurrentTransport(newNetworks.apnType)); 299 } 300 areNetworksValid(QualifiedNetworks networks)301 private static boolean areNetworksValid(QualifiedNetworks networks) { 302 if (networks.qualifiedNetworks == null || networks.qualifiedNetworks.length == 0) { 303 return false; 304 } 305 for (int network : networks.qualifiedNetworks) { 306 if (!ACCESS_NETWORK_TRANSPORT_TYPE_MAP.containsKey(network)) { 307 return false; 308 } 309 } 310 return true; 311 } 312 313 /** 314 * Set the current transport of apn type. 315 * 316 * @param apnType The APN type 317 * @param transport The transport. Must be WWAN or WLAN. 318 */ setCurrentTransport(@pnType int apnType, int transport)319 private synchronized void setCurrentTransport(@ApnType int apnType, int transport) { 320 mCurrentTransports.put(apnType, transport); 321 logl("setCurrentTransport: apnType=" + ApnSetting.getApnTypeString(apnType) 322 + ", transport=" + AccessNetworkConstants.transportTypeToString(transport)); 323 } 324 isHandoverPending()325 private boolean isHandoverPending() { 326 return mPendingHandoverApns.size() > 0; 327 } 328 updateAvailableNetworks()329 private void updateAvailableNetworks() { 330 if (isHandoverPending()) { 331 log("There's ongoing handover. Will update networks once handover completed."); 332 return; 333 } 334 335 if (mAvailableNetworksList.size() == 0) { 336 log("Nothing in the available network list queue."); 337 return; 338 } 339 340 List<QualifiedNetworks> networksList = mAvailableNetworksList.remove(); 341 logl("updateAvailableNetworks: " + networksList); 342 for (QualifiedNetworks networks : networksList) { 343 if (areNetworksValid(networks)) { 344 if (isHandoverNeeded(networks)) { 345 // If handover is needed, perform the handover works. For now we only pick the 346 // first element because it's the most preferred. In the future we should also 347 // consider the rest in the list, for example, the first one violates 348 // carrier/user policy. 349 int targetTransport = ACCESS_NETWORK_TRANSPORT_TYPE_MAP.get( 350 networks.qualifiedNetworks[0]); 351 logl("Handover needed for APN type: " 352 + ApnSetting.getApnTypeString(networks.apnType) + ", target transport: " 353 + AccessNetworkConstants.transportTypeToString(targetTransport)); 354 mPendingHandoverApns.put(networks.apnType, targetTransport); 355 mHandoverNeededEventRegistrants.notifyResult( 356 new HandoverParams(networks.apnType, targetTransport, 357 (success, fallback) -> { 358 // The callback for handover completed. 359 if (success) { 360 logl("Handover succeeded."); 361 } else { 362 logl("APN type " 363 + ApnSetting.getApnTypeString(networks.apnType) 364 + " handover to " 365 + AccessNetworkConstants.transportTypeToString( 366 targetTransport) + " failed." 367 + ", fallback=" + fallback); 368 } 369 if (success || !fallback) { 370 // If handover succeeds or failed without falling back 371 // to the original transport, we should move to the new 372 // transport (even if it is failed). 373 setCurrentTransport(networks.apnType, targetTransport); 374 } 375 mPendingHandoverApns.delete(networks.apnType); 376 377 // If there are still pending available network changes, we 378 // need to process the rest. 379 if (mAvailableNetworksList.size() > 0) { 380 sendEmptyMessage(EVENT_UPDATE_AVAILABLE_NETWORKS); 381 } 382 })); 383 } 384 mCurrentAvailableNetworks.put(networks.apnType, networks.qualifiedNetworks); 385 } else { 386 loge("Invalid networks received: " + networks); 387 } 388 } 389 390 // If there are still pending available network changes, we need to process the rest. 391 if (mAvailableNetworksList.size() > 0) { 392 sendEmptyMessage(EVENT_UPDATE_AVAILABLE_NETWORKS); 393 } 394 } 395 396 /** 397 * @return The available transports. Note that on legacy devices, the only available transport 398 * would be WWAN only. If the device is configured as AP-assisted mode, the available transport 399 * will always be WWAN and WLAN (even if the device is not camped on IWLAN). 400 * See {@link #isInLegacyMode()} for mode details. 401 */ getAvailableTransports()402 public synchronized @NonNull int[] getAvailableTransports() { 403 return mAvailableTransports; 404 } 405 406 /** 407 * @return {@code true} if the device operates in legacy mode, otherwise {@code false}. 408 */ isInLegacyMode()409 public boolean isInLegacyMode() { 410 // Get IWLAN operation mode from the system property. If the system property is missing or 411 // mis-configured the default behavior is tied to the IRadio version. For 1.4 or above, it's 412 // AP-assisted mode, for 1.3 or below, it's legacy mode. For IRadio 1.3 or below, no matter 413 // what the configuration is, it will always be legacy mode. 414 String mode = SystemProperties.get(SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE); 415 416 return mode.equals(IWLAN_OPERATION_MODE_LEGACY) 417 || mPhone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_4); 418 } 419 420 /** 421 * Get the transport based on the APN type. 422 * 423 * @param apnType APN type 424 * @return The transport type 425 */ getCurrentTransport(@pnType int apnType)426 public int getCurrentTransport(@ApnType int apnType) { 427 // In legacy mode, always route to cellular. 428 if (isInLegacyMode()) { 429 return AccessNetworkConstants.TRANSPORT_TYPE_WWAN; 430 } 431 432 // If we can't find the corresponding transport, always route to cellular. 433 return mCurrentTransports.get(apnType) == null 434 ? AccessNetworkConstants.TRANSPORT_TYPE_WWAN : mCurrentTransports.get(apnType); 435 } 436 437 /** 438 * Check if there is any APN type of network preferred on IWLAN. 439 * 440 * @return {@code true} if there is any APN preferred on IWLAN, otherwise {@code false}. 441 */ isAnyApnPreferredOnIwlan()442 public boolean isAnyApnPreferredOnIwlan() { 443 for (int i = 0; i < mCurrentAvailableNetworks.size(); i++) { 444 int[] networkList = mCurrentAvailableNetworks.valueAt(i); 445 if (networkList.length > 0 && networkList[0] == AccessNetworkType.IWLAN) { 446 return true; 447 } 448 } 449 return false; 450 } 451 452 /** 453 * Register for data handover needed event 454 * 455 * @param h The handler of the event 456 * @param what The id of the event 457 */ registerForHandoverNeededEvent(Handler h, int what)458 public void registerForHandoverNeededEvent(Handler h, int what) { 459 if (h != null) { 460 mHandoverNeededEventRegistrants.addUnique(h, what, null); 461 } 462 } 463 464 /** 465 * Unregister for data handover needed event 466 * 467 * @param h The handler 468 */ unregisterForHandoverNeededEvent(Handler h)469 public void unregisterForHandoverNeededEvent(Handler h) { 470 mHandoverNeededEventRegistrants.remove(h); 471 } 472 473 /** 474 * Dump the state of transport manager 475 * 476 * @param fd File descriptor 477 * @param printwriter Print writer 478 * @param args Arguments 479 */ dump(FileDescriptor fd, PrintWriter printwriter, String[] args)480 public void dump(FileDescriptor fd, PrintWriter printwriter, String[] args) { 481 IndentingPrintWriter pw = new IndentingPrintWriter(printwriter, " "); 482 pw.println("TransportManager:"); 483 pw.increaseIndent(); 484 pw.println("mAvailableTransports=[" + Arrays.stream(mAvailableTransports) 485 .mapToObj(type -> AccessNetworkConstants.transportTypeToString(type)) 486 .collect(Collectors.joining(",")) + "]"); 487 pw.println("mCurrentAvailableNetworks=" + mCurrentAvailableNetworks); 488 pw.println("mAvailableNetworksList=" + mAvailableNetworksList); 489 pw.println("mPendingHandoverApns=" + mPendingHandoverApns); 490 pw.println("mCurrentTransports=" + mCurrentTransports); 491 pw.println("isInLegacy=" + isInLegacyMode()); 492 pw.println("IWLAN operation mode=" 493 + SystemProperties.get(SYSTEM_PROPERTIES_IWLAN_OPERATION_MODE)); 494 if (mAccessNetworksManager != null) { 495 mAccessNetworksManager.dump(fd, pw, args); 496 } 497 pw.println("Local logs="); 498 pw.increaseIndent(); 499 mLocalLog.dump(fd, pw, args); 500 pw.decreaseIndent(); 501 pw.decreaseIndent(); 502 pw.flush(); 503 } 504 logl(String s)505 private void logl(String s) { 506 log(s); 507 mLocalLog.log(s); 508 } 509 log(String s)510 private void log(String s) { 511 Rlog.d(TAG, s); 512 } 513 loge(String s)514 private void loge(String s) { 515 Rlog.e(TAG, s); 516 } 517 } 518