DcController.java revision 454b1dfd508844b42eb775e4ab2359be74d3672b
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.Handler; 24import android.os.Message; 25import android.os.SystemProperties; 26import android.telephony.Rlog; 27import com.android.internal.telephony.PhoneBase; 28import com.android.internal.telephony.PhoneConstants; 29import com.android.internal.telephony.dataconnection.DataConnection.UpdateLinkPropertyResult; 30import com.android.internal.util.State; 31import com.android.internal.util.StateMachine; 32 33import java.io.FileDescriptor; 34import java.io.PrintWriter; 35import java.util.ArrayList; 36import java.util.HashMap; 37 38/** 39 * Data Connection Controller which is a package visible class and controls 40 * multiple data connections. For instance listening for unsolicited messages 41 * and then demultiplexing them to the appropriate DC. 42 */ 43class DcController extends StateMachine { 44 private static final boolean DBG = true; 45 private static final boolean VDBG = false; 46 protected static final boolean DEBUGGABLE = SystemProperties.getInt("ro.debuggable", 0) == 1; 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 (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 for (DataCallResponse newState : dcsList) { 213 DataConnection dc = mDcListActiveByCid.get(newState.cid); 214 if (dc == null) { 215 // UNSOL_DATA_CALL_LIST_CHANGED arrived before SETUP_DATA_CALL completed. 216 loge("onDataStateChanged: no associated DC yet, ignore"); 217 continue; 218 } 219 220 if (dc.mApnContexts.size() == 0) { 221 if (DBG) loge("onDataStateChanged: no connected apns, ignore"); 222 } else { 223 // Determine if the connection/apnContext should be cleaned up 224 // or just a notification should be sent out. 225 if (DBG) log("onDataStateChanged: Found ConnId=" + newState.cid 226 + " newState=" + newState.toString()); 227 if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE) { 228 if (DBG) log("onDataStateChanged: inactive, add to retry list"); 229 dcsToRetry.add(dc); 230 } else { 231 // Its active so update the DataConnections link properties 232 UpdateLinkPropertyResult result = dc.updateLinkProperty(newState); 233 if (result.oldLp.equals(result.newLp)) { 234 if (DBG) log("onDataStateChanged: no change"); 235 } else { 236 if (result.oldLp.isIdenticalInterfaceName(result.newLp)) { 237 if (! result.oldLp.isIdenticalDnses(result.newLp) || 238 ! result.oldLp.isIdenticalRoutes(result.newLp) || 239 ! result.oldLp.isIdenticalHttpProxy(result.newLp) || 240 ! result.oldLp.isIdenticalAddresses(result.newLp)) { 241 // If the same address type was removed and 242 // added we need to cleanup 243 CompareResult<LinkAddress> car = 244 result.oldLp.compareAddresses(result.newLp); 245 if (DBG) { 246 log("onDataStateChanged: oldLp=" + result.oldLp + 247 " newLp=" + result.newLp + " car=" + car); 248 } 249 boolean needToClean = false; 250 for (LinkAddress added : car.added) { 251 for (LinkAddress removed : car.removed) { 252 if (NetworkUtils.addressTypeMatches( 253 removed.getAddress(), 254 added.getAddress())) { 255 needToClean = true; 256 break; 257 } 258 } 259 } 260 if (needToClean) { 261 if (DBG) { 262 log("onDataStateChanged: addr change," + 263 " cleanup apns=" + dc.mApnContexts + 264 " oldLp=" + result.oldLp + 265 " newLp=" + result.newLp); 266 } 267 apnsToCleanup.addAll(dc.mApnContexts); 268 } else { 269 if (DBG) log("onDataStateChanged: simple change"); 270 for (ApnContext apnContext : dc.mApnContexts) { 271 mPhone.notifyDataConnection( 272 PhoneConstants.REASON_LINK_PROPERTIES_CHANGED, 273 apnContext.getApnType()); 274 } 275 } 276 } else { 277 if (DBG) { 278 log("onDataStateChanged: no changes"); 279 } 280 } 281 } else { 282 apnsToCleanup.addAll(dc.mApnContexts); 283 if (DBG) { 284 log("onDataStateChanged: interface change, cleanup apns=" 285 + dc.mApnContexts); 286 } 287 } 288 } 289 } 290 } 291 } 292 mPhone.notifyDataActivity(); 293 294 if (DBG) { 295 lr("onDataStateChanged: dcsToRetry=" + dcsToRetry 296 + " apnsToCleanup=" + apnsToCleanup); 297 } 298 299 // Cleanup connections that have changed 300 for (ApnContext apnContext : apnsToCleanup) { 301 mDct.sendCleanUpConnection(true, apnContext); 302 } 303 304 // Retry connections that have disappeared 305 for (DataConnection dc : dcsToRetry) { 306 if (DBG) log("onDataStateChanged: send EVENT_LOST_CONNECTION dc.mTag=" + dc.mTag); 307 dc.sendMessage(DataConnection.EVENT_LOST_CONNECTION, dc.mTag); 308 } 309 310 if (DBG) log("onDataStateChanged: X"); 311 } 312 } 313 314 /** 315 * lr is short name for logAndAddLogRec 316 * @param s 317 */ 318 private void lr(String s) { 319 logAndAddLogRec(s); 320 } 321 322 @Override 323 protected void log(String s) { 324 Rlog.d(getName(), s); 325 } 326 327 @Override 328 protected void loge(String s) { 329 Rlog.e(getName(), s); 330 } 331 332 /** 333 * @return the string for msg.what as our info. 334 */ 335 @Override 336 protected String getWhatToString(int what) { 337 String info = null; 338 info = DataConnection.cmdToString(what); 339 if (info == null) { 340 info = DcAsyncChannel.cmdToString(what); 341 } 342 return info; 343 } 344 345 @Override 346 public String toString() { 347 return "mDcListAll=" + mDcListAll + " mDcListActiveByCid=" + mDcListActiveByCid; 348 } 349 350 @Override 351 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 352 super.dump(fd, pw, args); 353 pw.println(" mPhone=" + mPhone); 354 pw.println(" mDcListAll=" + mDcListAll); 355 pw.println(" mDcListActiveByCid=" + mDcListActiveByCid); 356 } 357}