1 /* 2 * Copyright (C) 2013 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.hardware.radio.V1_4.DataConnActiveStatus; 20 import android.net.LinkAddress; 21 import android.os.AsyncResult; 22 import android.os.Handler; 23 import android.os.Looper; 24 import android.os.Message; 25 import android.os.RegistrantList; 26 import android.telephony.AccessNetworkConstants; 27 import android.telephony.CarrierConfigManager; 28 import android.telephony.DataFailCause; 29 import android.telephony.data.ApnSetting; 30 import android.telephony.data.DataCallResponse; 31 import android.telephony.data.TrafficDescriptor; 32 33 import com.android.internal.annotations.VisibleForTesting; 34 import com.android.internal.telephony.DctConstants; 35 import com.android.internal.telephony.Phone; 36 import com.android.internal.telephony.dataconnection.DataConnection.UpdateLinkPropertyResult; 37 import com.android.internal.telephony.util.TelephonyUtils; 38 import com.android.net.module.util.LinkPropertiesUtils; 39 import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult; 40 import com.android.net.module.util.NetUtils; 41 import com.android.telephony.Rlog; 42 43 import java.io.FileDescriptor; 44 import java.io.PrintWriter; 45 import java.util.ArrayList; 46 import java.util.HashMap; 47 import java.util.List; 48 import java.util.Objects; 49 50 /** 51 * Data Connection Controller which is a package visible class and controls 52 * multiple data connections. For instance listening for unsolicited messages 53 * and then demultiplexing them to the appropriate DC. 54 */ 55 public class DcController extends Handler { 56 private static final boolean DBG = true; 57 private static final boolean VDBG = false; 58 59 private final Phone mPhone; 60 private final DcTracker mDct; 61 private final String mTag; 62 private final DataServiceManager mDataServiceManager; 63 private final DcTesterDeactivateAll mDcTesterDeactivateAll; 64 65 // package as its used by Testing code 66 // @GuardedBy("mDcListAll") 67 final ArrayList<DataConnection> mDcListAll = new ArrayList<>(); 68 // @GuardedBy("mDcListAll") 69 private final HashMap<Integer, DataConnection> mDcListActiveByCid = new HashMap<>(); 70 // @GuardedBy("mTrafficDescriptorsByCid") 71 private final HashMap<Integer, List<TrafficDescriptor>> mTrafficDescriptorsByCid = 72 new HashMap<>(); 73 74 /** 75 * Aggregated physical link status from all data connections. This reflects the device's RRC 76 * connection state. 77 * If {@link CarrierConfigManager#KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL} is true, 78 * then This reflects "internet data connection" instead of RRC state. 79 */ 80 private @DataCallResponse.LinkStatus int mPhysicalLinkStatus = 81 DataCallResponse.LINK_STATUS_UNKNOWN; 82 83 private RegistrantList mPhysicalLinkStatusChangedRegistrants = new RegistrantList(); 84 85 /** 86 * Constructor. 87 * 88 * @param name to be used for the Controller 89 * @param phone the phone associated with Dcc and Dct 90 * @param dct the DataConnectionTracker associated with Dcc 91 * @param dataServiceManager the data service manager that manages data services 92 * @param looper looper for this handler 93 */ DcController(String name, Phone phone, DcTracker dct, DataServiceManager dataServiceManager, Looper looper)94 private DcController(String name, Phone phone, DcTracker dct, 95 DataServiceManager dataServiceManager, Looper looper) { 96 super(looper); 97 mPhone = phone; 98 mDct = dct; 99 mTag = name; 100 mDataServiceManager = dataServiceManager; 101 102 mDcTesterDeactivateAll = (TelephonyUtils.IS_DEBUGGABLE) 103 ? new DcTesterDeactivateAll(mPhone, DcController.this, this) 104 : null; 105 mDataServiceManager.registerForDataCallListChanged(this, 106 DataConnection.EVENT_DATA_STATE_CHANGED); 107 } 108 makeDcc(Phone phone, DcTracker dct, DataServiceManager dataServiceManager, Looper looper, String tagSuffix)109 public static DcController makeDcc(Phone phone, DcTracker dct, 110 DataServiceManager dataServiceManager, Looper looper, 111 String tagSuffix) { 112 return new DcController("Dcc" + tagSuffix, phone, dct, dataServiceManager, looper); 113 } 114 addDc(DataConnection dc)115 void addDc(DataConnection dc) { 116 synchronized (mDcListAll) { 117 mDcListAll.add(dc); 118 } 119 } 120 removeDc(DataConnection dc)121 void removeDc(DataConnection dc) { 122 synchronized (mDcListAll) { 123 mDcListActiveByCid.remove(dc.mCid); 124 mDcListAll.remove(dc); 125 } 126 synchronized (mTrafficDescriptorsByCid) { 127 mTrafficDescriptorsByCid.remove(dc.mCid); 128 } 129 } 130 addActiveDcByCid(DataConnection dc)131 public void addActiveDcByCid(DataConnection dc) { 132 if (DBG && dc.mCid < 0) { 133 log("addActiveDcByCid dc.mCid < 0 dc=" + dc); 134 } 135 synchronized (mDcListAll) { 136 mDcListActiveByCid.put(dc.mCid, dc); 137 } 138 updateTrafficDescriptorsForCid(dc.mCid, dc.getTrafficDescriptors()); 139 } 140 getActiveDcByCid(int cid)141 DataConnection getActiveDcByCid(int cid) { 142 synchronized (mDcListAll) { 143 return mDcListActiveByCid.get(cid); 144 } 145 } 146 removeActiveDcByCid(DataConnection dc)147 void removeActiveDcByCid(DataConnection dc) { 148 synchronized (mDcListAll) { 149 DataConnection removedDc = mDcListActiveByCid.remove(dc.mCid); 150 if (DBG && removedDc == null) { 151 log("removeActiveDcByCid removedDc=null dc=" + dc); 152 } 153 } 154 synchronized (mTrafficDescriptorsByCid) { 155 mTrafficDescriptorsByCid.remove(dc.mCid); 156 } 157 } 158 isDefaultDataActive()159 boolean isDefaultDataActive() { 160 synchronized (mDcListAll) { 161 return mDcListActiveByCid.values().stream() 162 .anyMatch(dc -> dc.getApnContexts().stream() 163 .anyMatch(apn -> apn.getApnTypeBitmask() == ApnSetting.TYPE_DEFAULT)); 164 } 165 } 166 getTrafficDescriptorsForCid(int cid)167 List<TrafficDescriptor> getTrafficDescriptorsForCid(int cid) { 168 synchronized (mTrafficDescriptorsByCid) { 169 return mTrafficDescriptorsByCid.get(cid); 170 } 171 } 172 updateTrafficDescriptorsForCid(int cid, List<TrafficDescriptor> tds)173 void updateTrafficDescriptorsForCid(int cid, List<TrafficDescriptor> tds) { 174 synchronized (mTrafficDescriptorsByCid) { 175 mTrafficDescriptorsByCid.put(cid, tds); 176 } 177 } 178 179 @Override handleMessage(Message msg)180 public void handleMessage(Message msg) { 181 AsyncResult ar; 182 183 switch (msg.what) { 184 case DataConnection.EVENT_DATA_STATE_CHANGED: 185 ar = (AsyncResult) msg.obj; 186 if (ar.exception == null) { 187 onDataStateChanged((ArrayList<DataCallResponse>) ar.result); 188 } else { 189 log("EVENT_DATA_STATE_CHANGED: exception; likely radio not available, ignore"); 190 } 191 break; 192 default: 193 loge("Unexpected event " + msg); 194 break; 195 } 196 } 197 198 /** 199 * Process the new list of "known" Data Calls 200 * @param dcsList as sent by RIL_UNSOL_DATA_CALL_LIST_CHANGED 201 */ onDataStateChanged(ArrayList<DataCallResponse> dcsList)202 private void onDataStateChanged(ArrayList<DataCallResponse> dcsList) { 203 final HashMap<Integer, DataConnection> dcListActiveByCid; 204 synchronized (mDcListAll) { 205 dcListActiveByCid = new HashMap<>(mDcListActiveByCid); 206 } 207 208 if (DBG) { 209 log("onDataStateChanged: dcsList=" + dcsList 210 + " dcListActiveByCid=" + dcListActiveByCid); 211 } 212 213 // Create hashmap of cid to DataCallResponse 214 HashMap<Integer, DataCallResponse> dataCallResponseListByCid = new HashMap<>(); 215 for (DataCallResponse dcs : dcsList) { 216 dataCallResponseListByCid.put(dcs.getId(), dcs); 217 } 218 219 // Add a DC that is active but not in the dcsList to the list of DC's to retry 220 ArrayList<DataConnection> dcsToRetry = new ArrayList<>(); 221 for (DataConnection dc : dcListActiveByCid.values()) { 222 DataCallResponse response = dataCallResponseListByCid.get(dc.mCid); 223 if (response == null) { 224 if (DBG) log("onDataStateChanged: add to retry dc=" + dc); 225 dcsToRetry.add(dc); 226 } else { 227 List<TrafficDescriptor> oldTds = getTrafficDescriptorsForCid(dc.mCid); 228 List<TrafficDescriptor> newTds = response.getTrafficDescriptors(); 229 if (!oldTds.equals(newTds)) { 230 if (DBG) { 231 log("onDataStateChanged: add to retry due to TD changed dc=" + dc 232 + ", oldTds=" + oldTds + ", newTds=" + newTds); 233 } 234 updateTrafficDescriptorsForCid(dc.mCid, newTds); 235 dcsToRetry.add(dc); 236 } 237 } 238 } 239 if (DBG) log("onDataStateChanged: dcsToRetry=" + dcsToRetry); 240 241 // Find which connections have changed state and send a notification or cleanup 242 // and any that are in active need to be retried. 243 ArrayList<ApnContext> apnsToCleanup = new ArrayList<ApnContext>(); 244 245 boolean isAnyDataCallDormant = false; 246 boolean isAnyDataCallActive = false; 247 boolean isInternetDataCallActive = false; 248 249 for (DataCallResponse newState : dcsList) { 250 251 DataConnection dc = dcListActiveByCid.get(newState.getId()); 252 if (dc == null) { 253 // UNSOL_DATA_CALL_LIST_CHANGED arrived before SETUP_DATA_CALL completed. 254 loge("onDataStateChanged: no associated DC yet, ignore"); 255 continue; 256 } 257 258 List<ApnContext> apnContexts = dc.getApnContexts(); 259 if (apnContexts.size() == 0) { 260 if (DBG) loge("onDataStateChanged: no connected apns, ignore"); 261 } else { 262 // Determine if the connection/apnContext should be cleaned up 263 // or just a notification should be sent out. 264 if (DBG) { 265 log("onDataStateChanged: Found ConnId=" + newState.getId() 266 + " newState=" + newState.toString()); 267 } 268 if (apnContexts.stream().anyMatch( 269 i -> ApnSetting.TYPE_DEFAULT_STRING.equals(i.getApnType())) 270 && newState.getLinkStatus() == DataConnActiveStatus.ACTIVE) { 271 isInternetDataCallActive = true; 272 } 273 if (newState.getLinkStatus() == DataConnActiveStatus.INACTIVE) { 274 if (mDct.isCleanupRequired.get()) { 275 apnsToCleanup.addAll(apnContexts); 276 mDct.isCleanupRequired.set(false); 277 } else { 278 int failCause = DataFailCause.getFailCause(newState.getCause()); 279 if (DataFailCause.isRadioRestartFailure(mPhone.getContext(), failCause, 280 mPhone.getSubId())) { 281 if (DBG) { 282 log("onDataStateChanged: X restart radio, failCause=" 283 + failCause); 284 } 285 mDct.sendRestartRadio(); 286 } else if (mDct.isPermanentFailure(failCause)) { 287 if (DBG) { 288 log("onDataStateChanged: inactive, add to cleanup list. " 289 + "failCause=" + failCause); 290 } 291 apnsToCleanup.addAll(apnContexts); 292 } else { 293 if (DBG) { 294 log("onDataStateChanged: inactive, add to retry list. " 295 + "failCause=" + failCause); 296 } 297 dcsToRetry.add(dc); 298 } 299 } 300 } else { 301 // Update the pdu session id 302 dc.setPduSessionId(newState.getPduSessionId()); 303 304 dc.updatePcscfAddr(newState); 305 306 // Its active so update the DataConnections link properties 307 UpdateLinkPropertyResult result = dc.updateLinkProperty(newState); 308 dc.updateResponseFields(newState); 309 if (result.oldLp.equals(result.newLp)) { 310 if (DBG) log("onDataStateChanged: no change"); 311 } else { 312 if (LinkPropertiesUtils.isIdenticalInterfaceName( 313 result.oldLp, result.newLp)) { 314 if (!LinkPropertiesUtils.isIdenticalDnses( 315 result.oldLp, result.newLp) 316 || !LinkPropertiesUtils.isIdenticalRoutes( 317 result.oldLp, result.newLp) 318 || !LinkPropertiesUtils.isIdenticalHttpProxy( 319 result.oldLp, result.newLp) 320 || !LinkPropertiesUtils.isIdenticalAddresses( 321 result.oldLp, result.newLp)) { 322 // If the same address type was removed and 323 // added we need to cleanup 324 CompareOrUpdateResult<Integer, LinkAddress> car 325 = new CompareOrUpdateResult( 326 result.oldLp != null ? 327 result.oldLp.getLinkAddresses() : null, 328 result.newLp != null ? 329 result.newLp.getLinkAddresses() : null, 330 (la) -> Objects.hash(((LinkAddress)la).getAddress(), 331 ((LinkAddress)la).getPrefixLength(), 332 ((LinkAddress)la).getScope())); 333 if (DBG) { 334 log("onDataStateChanged: oldLp=" + result.oldLp 335 + " newLp=" + result.newLp + " car=" + car); 336 } 337 boolean needToClean = false; 338 for (LinkAddress added : car.added) { 339 for (LinkAddress removed : car.removed) { 340 if (NetUtils.addressTypeMatches( 341 removed.getAddress(), 342 added.getAddress())) { 343 needToClean = true; 344 break; 345 } 346 } 347 } 348 if (needToClean) { 349 if (DBG) { 350 log("onDataStateChanged: addr change," 351 + " cleanup apns=" + apnContexts 352 + " oldLp=" + result.oldLp 353 + " newLp=" + result.newLp); 354 } 355 apnsToCleanup.addAll(apnContexts); 356 } 357 } else { 358 if (DBG) { 359 log("onDataStateChanged: no changes"); 360 } 361 } 362 } else { 363 apnsToCleanup.addAll(apnContexts); 364 if (DBG) { 365 log("onDataStateChanged: interface change, cleanup apns=" 366 + apnContexts); 367 } 368 } 369 } 370 } 371 } 372 373 if (newState.getLinkStatus() == DataConnActiveStatus.ACTIVE) { 374 isAnyDataCallActive = true; 375 } 376 if (newState.getLinkStatus() == DataConnActiveStatus.DORMANT) { 377 isAnyDataCallDormant = true; 378 } 379 } 380 381 if (mDataServiceManager.getTransportType() 382 == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) { 383 boolean isPhysicalLinkStatusFocusingOnInternetData = 384 mDct.getLteEndcUsingUserDataForIdleDetection(); 385 int physicalLinkStatus = 386 (isPhysicalLinkStatusFocusingOnInternetData 387 ? isInternetDataCallActive : isAnyDataCallActive) 388 ? DataCallResponse.LINK_STATUS_ACTIVE 389 : DataCallResponse.LINK_STATUS_DORMANT; 390 if (mPhysicalLinkStatus != physicalLinkStatus) { 391 mPhysicalLinkStatus = physicalLinkStatus; 392 mPhysicalLinkStatusChangedRegistrants.notifyResult(mPhysicalLinkStatus); 393 } 394 if (isAnyDataCallDormant && !isAnyDataCallActive) { 395 // There is no way to indicate link activity per APN right now. So 396 // Link Activity will be considered dormant only when all data calls 397 // are dormant. 398 // If a single data call is in dormant state and none of the data 399 // calls are active broadcast overall link status as dormant. 400 if (DBG) { 401 log("onDataStateChanged: Data activity DORMANT. stopNetStatePoll"); 402 } 403 mDct.sendStopNetStatPoll(DctConstants.Activity.DORMANT); 404 } else { 405 if (DBG) { 406 log("onDataStateChanged: Data Activity updated to NONE. " 407 + "isAnyDataCallActive = " + isAnyDataCallActive 408 + " isAnyDataCallDormant = " + isAnyDataCallDormant); 409 } 410 if (isAnyDataCallActive) { 411 mDct.sendStartNetStatPoll(DctConstants.Activity.NONE); 412 } 413 } 414 } 415 416 if (DBG) { 417 log("onDataStateChanged: dcsToRetry=" + dcsToRetry 418 + " apnsToCleanup=" + apnsToCleanup); 419 } 420 421 // Cleanup connections that have changed 422 for (ApnContext apnContext : apnsToCleanup) { 423 mDct.cleanUpConnection(apnContext); 424 } 425 426 // Retry connections that have disappeared 427 for (DataConnection dc : dcsToRetry) { 428 if (DBG) log("onDataStateChanged: send EVENT_LOST_CONNECTION dc.mTag=" + dc.mTag); 429 dc.sendMessage(DataConnection.EVENT_LOST_CONNECTION, dc.mTag); 430 } 431 432 if (VDBG) log("onDataStateChanged: X"); 433 } 434 435 /** 436 * Register for physical link status (i.e. RRC state) changed event. 437 * if {@link CarrierConfigManager#KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL} is true, 438 * then physical link status is focusing on "internet data connection" instead of RRC state. 439 * @param h The handler 440 * @param what The event 441 */ 442 @VisibleForTesting registerForPhysicalLinkStatusChanged(Handler h, int what)443 public void registerForPhysicalLinkStatusChanged(Handler h, int what) { 444 mPhysicalLinkStatusChangedRegistrants.addUnique(h, what, null); 445 } 446 447 /** 448 * Unregister from physical link status (i.e. RRC state) changed event. 449 * 450 * @param h The previously registered handler 451 */ unregisterForPhysicalLinkStatusChanged(Handler h)452 void unregisterForPhysicalLinkStatusChanged(Handler h) { 453 mPhysicalLinkStatusChangedRegistrants.remove(h); 454 } 455 log(String s)456 private void log(String s) { 457 Rlog.d(mTag, s); 458 } 459 loge(String s)460 private void loge(String s) { 461 Rlog.e(mTag, s); 462 } 463 464 @Override toString()465 public String toString() { 466 StringBuilder sb = new StringBuilder(); 467 synchronized (mDcListAll) { 468 sb.append("mDcListAll=").append(mDcListAll) 469 .append(" mDcListActiveByCid=").append(mDcListActiveByCid); 470 } 471 synchronized (mTrafficDescriptorsByCid) { 472 sb.append("mTrafficDescriptorsByCid=").append(mTrafficDescriptorsByCid); 473 } 474 return sb.toString(); 475 } 476 dump(FileDescriptor fd, PrintWriter pw, String[] args)477 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 478 pw.println(" mPhone=" + mPhone); 479 synchronized (mDcListAll) { 480 pw.println(" mDcListAll=" + mDcListAll); 481 pw.println(" mDcListActiveByCid=" + mDcListActiveByCid); 482 } 483 synchronized (mTrafficDescriptorsByCid) { 484 pw.println(" mTrafficDescriptorsByCid=" + mTrafficDescriptorsByCid); 485 } 486 } 487 } 488