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