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