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