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