1 /* 2 * Copyright (C) 2006 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; 18 19 20 import com.android.internal.telephony.DataCallState.SetupResult; 21 import com.android.internal.util.AsyncChannel; 22 import com.android.internal.util.Protocol; 23 import com.android.internal.util.State; 24 import com.android.internal.util.StateMachine; 25 26 import android.app.PendingIntent; 27 import android.net.LinkCapabilities; 28 import android.net.LinkProperties; 29 import android.net.ProxyProperties; 30 import android.os.AsyncResult; 31 import android.os.Message; 32 import android.os.SystemProperties; 33 import android.text.TextUtils; 34 35 import java.util.ArrayList; 36 import java.util.HashMap; 37 import java.util.List; 38 39 /** 40 * {@hide} 41 * 42 * DataConnection StateMachine. 43 * 44 * This is an abstract base class for representing a single data connection. 45 * Instances of this class such as <code>CdmaDataConnection</code> and 46 * <code>GsmDataConnection</code>, * represent a connection via the cellular network. 47 * There may be multiple data connections and all of them are managed by the 48 * <code>DataConnectionTracker</code>. 49 * 50 * Instances are asynchronous state machines and have two primary entry points 51 * <code>connect()</code> and <code>disconnect</code>. The message a parameter will be returned 52 * hen the operation completes. The <code>msg.obj</code> will contain an AsyncResult 53 * object and <code>AsyncResult.userObj</code> is the original <code>msg.obj</code>. if successful 54 * with the <code>AsyncResult.result == null</code> and <code>AsyncResult.exception == null</code>. 55 * If an error <code>AsyncResult.result = FailCause</code> and 56 * <code>AsyncResult.exception = new Exception()</code>. 57 * 58 * The other public methods are provided for debugging. 59 */ 60 public abstract class DataConnection extends StateMachine { 61 protected static final boolean DBG = true; 62 protected static final boolean VDBG = false; 63 64 protected static Object mCountLock = new Object(); 65 protected static int mCount; 66 protected AsyncChannel mAc; 67 68 private List<ApnContext> mApnList = null; 69 PendingIntent mReconnectIntent = null; 70 71 /** 72 * Used internally for saving connecting parameters. 73 */ 74 protected static class ConnectionParams { 75 public ConnectionParams(ApnSetting apn, Message onCompletedMsg) { 76 this.apn = apn; 77 this.onCompletedMsg = onCompletedMsg; 78 } 79 80 public int tag; 81 public ApnSetting apn; 82 public Message onCompletedMsg; 83 } 84 85 /** 86 * Used internally for saving disconnecting parameters. 87 */ 88 protected static class DisconnectParams { 89 public DisconnectParams(String reason, Message onCompletedMsg) { 90 this.reason = reason; 91 this.onCompletedMsg = onCompletedMsg; 92 } 93 public int tag; 94 public String reason; 95 public Message onCompletedMsg; 96 } 97 98 /** 99 * Returned as the reason for a connection failure as defined 100 * by RIL_DataCallFailCause in ril.h and some local errors. 101 */ 102 public enum FailCause { 103 NONE(0), 104 105 // This series of errors as specified by the standards 106 // specified in ril.h 107 OPERATOR_BARRED(0x08), 108 INSUFFICIENT_RESOURCES(0x1A), 109 MISSING_UNKNOWN_APN(0x1B), 110 UNKNOWN_PDP_ADDRESS_TYPE(0x1C), 111 USER_AUTHENTICATION(0x1D), 112 ACTIVATION_REJECT_GGSN(0x1E), 113 ACTIVATION_REJECT_UNSPECIFIED(0x1F), 114 SERVICE_OPTION_NOT_SUPPORTED(0x20), 115 SERVICE_OPTION_NOT_SUBSCRIBED(0x21), 116 SERVICE_OPTION_OUT_OF_ORDER(0x22), 117 NSAPI_IN_USE(0x23), 118 ONLY_IPV4_ALLOWED(0x32), 119 ONLY_IPV6_ALLOWED(0x33), 120 ONLY_SINGLE_BEARER_ALLOWED(0x34), 121 PROTOCOL_ERRORS(0x6F), 122 123 // Local errors generated by Vendor RIL 124 // specified in ril.h 125 REGISTRATION_FAIL(-1), 126 GPRS_REGISTRATION_FAIL(-2), 127 SIGNAL_LOST(-3), 128 PREF_RADIO_TECH_CHANGED(-4), 129 RADIO_POWER_OFF(-5), 130 TETHERED_CALL_ACTIVE(-6), 131 ERROR_UNSPECIFIED(0xFFFF), 132 133 // Errors generated by the Framework 134 // specified here 135 UNKNOWN(0x10000), 136 RADIO_NOT_AVAILABLE(0x10001), 137 UNACCEPTABLE_NETWORK_PARAMETER(0x10002); 138 139 private final int mErrorCode; 140 private static final HashMap<Integer, FailCause> sErrorCodeToFailCauseMap; 141 static { 142 sErrorCodeToFailCauseMap = new HashMap<Integer, FailCause>(); 143 for (FailCause fc : values()) { 144 sErrorCodeToFailCauseMap.put(fc.getErrorCode(), fc); 145 } 146 } 147 148 FailCause(int errorCode) { 149 mErrorCode = errorCode; 150 } 151 152 int getErrorCode() { 153 return mErrorCode; 154 } 155 156 public boolean isPermanentFail() { 157 return (this == OPERATOR_BARRED) || (this == MISSING_UNKNOWN_APN) || 158 (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) || 159 (this == SERVICE_OPTION_NOT_SUPPORTED) || 160 (this == SERVICE_OPTION_NOT_SUBSCRIBED) || (this == NSAPI_IN_USE) || 161 (this == PROTOCOL_ERRORS); 162 } 163 164 public boolean isEventLoggable() { 165 return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) || 166 (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) || 167 (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) || 168 (this == SERVICE_OPTION_NOT_SUBSCRIBED) || 169 (this == SERVICE_OPTION_NOT_SUPPORTED) || 170 (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) || 171 (this == PROTOCOL_ERRORS) || 172 (this == UNACCEPTABLE_NETWORK_PARAMETER); 173 } 174 175 public static FailCause fromInt(int errorCode) { 176 FailCause fc = sErrorCodeToFailCauseMap.get(errorCode); 177 if (fc == null) { 178 fc = UNKNOWN; 179 } 180 return fc; 181 } 182 } 183 184 public static class CallSetupException extends Exception { 185 private int mRetryOverride = -1; 186 187 CallSetupException (int retryOverride) { 188 mRetryOverride = retryOverride; 189 } 190 191 public int getRetryOverride() { 192 return mRetryOverride; 193 } 194 } 195 196 // ***** Event codes for driving the state machine 197 protected static final int BASE = Protocol.BASE_DATA_CONNECTION; 198 protected static final int EVENT_CONNECT = BASE + 0; 199 protected static final int EVENT_SETUP_DATA_CONNECTION_DONE = BASE + 1; 200 protected static final int EVENT_GET_LAST_FAIL_DONE = BASE + 2; 201 protected static final int EVENT_DEACTIVATE_DONE = BASE + 3; 202 protected static final int EVENT_DISCONNECT = BASE + 4; 203 protected static final int EVENT_RIL_CONNECTED = BASE + 5; 204 205 //***** Tag IDs for EventLog 206 protected static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100; 207 208 //***** Member Variables 209 protected ApnSetting mApn; 210 protected int mTag; 211 protected PhoneBase phone; 212 protected int mRilVersion = -1; 213 protected int cid; 214 protected LinkProperties mLinkProperties = new LinkProperties(); 215 protected LinkCapabilities mCapabilities = new LinkCapabilities(); 216 protected long createTime; 217 protected long lastFailTime; 218 protected FailCause lastFailCause; 219 protected int mRetryOverride = -1; 220 protected static final String NULL_IP = "0.0.0.0"; 221 private int mRefCount; 222 Object userData; 223 224 //***** Abstract methods 225 @Override 226 public abstract String toString(); 227 228 protected abstract void onConnect(ConnectionParams cp); 229 230 protected abstract boolean isDnsOk(String[] domainNameServers); 231 232 protected abstract void log(String s); 233 234 235 //***** Constructor 236 protected DataConnection(PhoneBase phone, String name, int id, RetryManager rm) { 237 super(name); 238 if (DBG) log("DataConnection constructor E"); 239 this.phone = phone; 240 mId = id; 241 mRetryMgr = rm; 242 this.cid = -1; 243 244 setDbg(false); 245 addState(mDefaultState); 246 addState(mInactiveState, mDefaultState); 247 addState(mActivatingState, mDefaultState); 248 addState(mActiveState, mDefaultState); 249 addState(mDisconnectingState, mDefaultState); 250 addState(mDisconnectingErrorCreatingConnection, mDefaultState); 251 setInitialState(mInactiveState); 252 253 mApnList = new ArrayList<ApnContext>(); 254 if (DBG) log("DataConnection constructor X"); 255 } 256 257 /** 258 * TearDown the data connection. 259 * 260 * @param o will be returned in AsyncResult.userObj 261 * and is either a DisconnectParams or ConnectionParams. 262 */ 263 private void tearDownData(Object o) { 264 int discReason = RILConstants.DEACTIVATE_REASON_NONE; 265 if ((o != null) && (o instanceof DisconnectParams)) { 266 DisconnectParams dp = (DisconnectParams)o; 267 Message m = dp.onCompletedMsg; 268 if (TextUtils.equals(dp.reason, Phone.REASON_RADIO_TURNED_OFF)) { 269 discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF; 270 } else if (TextUtils.equals(dp.reason, Phone.REASON_PDP_RESET)) { 271 discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET; 272 } 273 } 274 if (phone.mCM.getRadioState().isOn()) { 275 if (DBG) log("tearDownData radio is on, call deactivateDataCall"); 276 phone.mCM.deactivateDataCall(cid, discReason, obtainMessage(EVENT_DEACTIVATE_DONE, o)); 277 } else { 278 if (DBG) log("tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately"); 279 AsyncResult ar = new AsyncResult(o, null, null); 280 sendMessage(obtainMessage(EVENT_DEACTIVATE_DONE, ar)); 281 } 282 } 283 284 /** 285 * Send the connectionCompletedMsg. 286 * 287 * @param cp is the ConnectionParams 288 * @param cause 289 */ 290 private void notifyConnectCompleted(ConnectionParams cp, FailCause cause) { 291 Message connectionCompletedMsg = cp.onCompletedMsg; 292 if (connectionCompletedMsg == null) { 293 return; 294 } 295 296 long timeStamp = System.currentTimeMillis(); 297 connectionCompletedMsg.arg1 = cid; 298 299 if (cause == FailCause.NONE) { 300 createTime = timeStamp; 301 AsyncResult.forMessage(connectionCompletedMsg); 302 } else { 303 lastFailCause = cause; 304 lastFailTime = timeStamp; 305 AsyncResult.forMessage(connectionCompletedMsg, cause, 306 new CallSetupException(mRetryOverride)); 307 } 308 if (DBG) log("notifyConnectionCompleted at " + timeStamp + " cause=" + cause); 309 310 connectionCompletedMsg.sendToTarget(); 311 } 312 313 /** 314 * Send ar.userObj if its a message, which is should be back to originator. 315 * 316 * @param dp is the DisconnectParams. 317 */ 318 private void notifyDisconnectCompleted(DisconnectParams dp) { 319 if (VDBG) log("NotifyDisconnectCompleted"); 320 321 if (dp.onCompletedMsg != null) { 322 Message msg = dp.onCompletedMsg; 323 if (VDBG) { 324 log(String.format("msg=%s msg.obj=%s", msg.toString(), 325 ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>"))); 326 } 327 AsyncResult.forMessage(msg); 328 msg.sendToTarget(); 329 } 330 if (DBG) log("NotifyDisconnectCompleted DisconnectParams=" + dp); 331 } 332 333 protected int getRadioTechnology(int defaultRadioTechnology) { 334 int radioTechnology; 335 if (mRilVersion < 6) { 336 radioTechnology = defaultRadioTechnology; 337 } else { 338 radioTechnology = phone.getServiceState().getRadioTechnology() + 2; 339 } 340 return radioTechnology; 341 } 342 343 /* 344 * ************************************************************************** 345 * Begin Members and methods owned by DataConnectionTracker but stored 346 * in a DataConnection because there is one per connection. 347 * ************************************************************************** 348 */ 349 350 /* 351 * The id is owned by DataConnectionTracker. 352 */ 353 private int mId; 354 355 /** 356 * Get the DataConnection ID 357 */ 358 public int getDataConnectionId() { 359 return mId; 360 } 361 362 /* 363 * The retry manager is currently owned by the DataConnectionTracker but is stored 364 * in the DataConnection because there is one per connection. These methods 365 * should only be used by the DataConnectionTracker although someday the retrying 366 * maybe managed by the DataConnection itself and these methods could disappear. 367 */ 368 private RetryManager mRetryMgr; 369 370 /** 371 * @return retry manager retryCount 372 */ 373 public int getRetryCount() { 374 return mRetryMgr.getRetryCount(); 375 } 376 377 /** 378 * @return retry manager retryTimer 379 */ 380 public int getRetryTimer() { 381 return mRetryMgr.getRetryTimer(); 382 } 383 384 /** 385 * increaseRetryCount of retry manager 386 */ 387 public void increaseRetryCount() { 388 mRetryMgr.increaseRetryCount(); 389 } 390 391 /** 392 * @return retry manager isRetryNeeded 393 */ 394 public boolean isRetryNeeded() { 395 return mRetryMgr.isRetryNeeded(); 396 } 397 398 /** 399 * resetRetryCount of retry manager 400 */ 401 public void resetRetryCount() { 402 mRetryMgr.resetRetryCount(); 403 } 404 405 /** 406 * set retryForeverUsingLasttimeout of retry manager 407 */ 408 public void retryForeverUsingLastTimeout() { 409 mRetryMgr.retryForeverUsingLastTimeout(); 410 } 411 412 /** 413 * @return retry manager isRetryForever 414 */ 415 public boolean isRetryForever() { 416 return mRetryMgr.isRetryForever(); 417 } 418 419 /** 420 * @return whether the retry config is set successfully or not 421 */ 422 public boolean configureRetry(int maxRetryCount, int retryTime, int randomizationTime) { 423 return mRetryMgr.configure(maxRetryCount, retryTime, randomizationTime); 424 } 425 426 /** 427 * @return whether the retry config is set successfully or not 428 */ 429 public boolean configureRetry(String configStr) { 430 return mRetryMgr.configure(configStr); 431 } 432 433 /* 434 * ************************************************************************** 435 * End members owned by DataConnectionTracker 436 * ************************************************************************** 437 */ 438 439 /** 440 * Clear all settings called when entering mInactiveState. 441 */ 442 protected void clearSettings() { 443 if (DBG) log("clearSettings"); 444 445 createTime = -1; 446 lastFailTime = -1; 447 lastFailCause = FailCause.NONE; 448 mRetryOverride = -1; 449 mRefCount = 0; 450 cid = -1; 451 452 mLinkProperties = new LinkProperties(); 453 mApn = null; 454 } 455 456 /** 457 * Process setup completion. 458 * 459 * @param ar is the result 460 * @return SetupResult. 461 */ 462 private DataCallState.SetupResult onSetupConnectionCompleted(AsyncResult ar) { 463 DataCallState response = (DataCallState) ar.result; 464 ConnectionParams cp = (ConnectionParams) ar.userObj; 465 DataCallState.SetupResult result; 466 467 if (ar.exception != null) { 468 if (DBG) { 469 log("onSetupConnectionCompleted failed, ar.exception=" + ar.exception + 470 " response=" + response); 471 } 472 473 if (ar.exception instanceof CommandException 474 && ((CommandException) (ar.exception)).getCommandError() 475 == CommandException.Error.RADIO_NOT_AVAILABLE) { 476 result = DataCallState.SetupResult.ERR_BadCommand; 477 result.mFailCause = FailCause.RADIO_NOT_AVAILABLE; 478 } else if ((response == null) || (response.version < 4)) { 479 result = DataCallState.SetupResult.ERR_GetLastErrorFromRil; 480 } else { 481 result = DataCallState.SetupResult.ERR_RilError; 482 result.mFailCause = FailCause.fromInt(response.status); 483 } 484 } else if (cp.tag != mTag) { 485 if (DBG) { 486 log("BUG: onSetupConnectionCompleted is stale cp.tag=" + cp.tag + ", mtag=" + mTag); 487 } 488 result = DataCallState.SetupResult.ERR_Stale; 489 } else if (response.status != 0) { 490 result = DataCallState.SetupResult.ERR_RilError; 491 result.mFailCause = FailCause.fromInt(response.status); 492 } else { 493 if (DBG) log("onSetupConnectionCompleted received DataCallState: " + response); 494 cid = response.cid; 495 result = updateLinkProperty(response).setupResult; 496 } 497 498 return result; 499 } 500 501 private int getSuggestedRetryTime(AsyncResult ar) { 502 int retry = -1; 503 if (ar.exception == null) { 504 DataCallState response = (DataCallState) ar.result; 505 retry = response.suggestedRetryTime; 506 } 507 return retry; 508 } 509 510 private DataCallState.SetupResult setLinkProperties(DataCallState response, 511 LinkProperties lp) { 512 // Check if system property dns usable 513 boolean okToUseSystemPropertyDns = false; 514 String propertyPrefix = "net." + response.ifname + "."; 515 String dnsServers[] = new String[2]; 516 dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1"); 517 dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2"); 518 okToUseSystemPropertyDns = isDnsOk(dnsServers); 519 520 // set link properties based on data call response 521 return response.setLinkProperties(lp, okToUseSystemPropertyDns); 522 } 523 524 public static class UpdateLinkPropertyResult { 525 public DataCallState.SetupResult setupResult = DataCallState.SetupResult.SUCCESS; 526 public LinkProperties oldLp; 527 public LinkProperties newLp; 528 public UpdateLinkPropertyResult(LinkProperties curLp) { 529 oldLp = curLp; 530 newLp = curLp; 531 } 532 } 533 534 private UpdateLinkPropertyResult updateLinkProperty(DataCallState newState) { 535 UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties); 536 537 if (newState == null) return result; 538 539 DataCallState.SetupResult setupResult; 540 result.newLp = new LinkProperties(); 541 542 // set link properties based on data call response 543 result.setupResult = setLinkProperties(newState, result.newLp); 544 if (result.setupResult != DataCallState.SetupResult.SUCCESS) { 545 if (DBG) log("updateLinkProperty failed : " + result.setupResult); 546 return result; 547 } 548 // copy HTTP proxy as it is not part DataCallState. 549 result.newLp.setHttpProxy(mLinkProperties.getHttpProxy()); 550 551 if (DBG && (! result.oldLp.equals(result.newLp))) { 552 if (DBG) log("updateLinkProperty old != new"); 553 if (VDBG) log("updateLinkProperty old LP=" + result.oldLp); 554 if (VDBG) log("updateLinkProperty new LP=" + result.newLp); 555 } 556 mLinkProperties = result.newLp; 557 558 return result; 559 } 560 561 /** 562 * The parent state for all other states. 563 */ 564 private class DcDefaultState extends State { 565 @Override 566 public void enter() { 567 phone.mCM.registerForRilConnected(getHandler(), EVENT_RIL_CONNECTED, null); 568 } 569 @Override 570 public void exit() { 571 phone.mCM.unregisterForRilConnected(getHandler()); 572 } 573 @Override 574 public boolean processMessage(Message msg) { 575 AsyncResult ar; 576 577 switch (msg.what) { 578 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: { 579 if (mAc != null) { 580 if (VDBG) log("Disconnecting to previous connection mAc=" + mAc); 581 mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, 582 AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED); 583 } else { 584 mAc = new AsyncChannel(); 585 mAc.connected(null, getHandler(), msg.replyTo); 586 if (VDBG) log("DcDefaultState: FULL_CONNECTION reply connected"); 587 mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, 588 AsyncChannel.STATUS_SUCCESSFUL, mId, "hi"); 589 } 590 break; 591 } 592 case AsyncChannel.CMD_CHANNEL_DISCONNECT: { 593 if (VDBG) log("CMD_CHANNEL_DISCONNECT"); 594 mAc.disconnect(); 595 break; 596 } 597 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { 598 if (VDBG) log("CMD_CHANNEL_DISCONNECTED"); 599 mAc = null; 600 break; 601 } 602 case DataConnectionAc.REQ_IS_INACTIVE: { 603 boolean val = getCurrentState() == mInactiveState; 604 if (VDBG) log("REQ_IS_INACTIVE isInactive=" + val); 605 mAc.replyToMessage(msg, DataConnectionAc.RSP_IS_INACTIVE, val ? 1 : 0); 606 break; 607 } 608 case DataConnectionAc.REQ_GET_CID: { 609 if (VDBG) log("REQ_GET_CID cid=" + cid); 610 mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_CID, cid); 611 break; 612 } 613 case DataConnectionAc.REQ_GET_APNSETTING: { 614 if (VDBG) log("REQ_GET_APNSETTING apnSetting=" + mApn); 615 mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNSETTING, mApn); 616 break; 617 } 618 case DataConnectionAc.REQ_GET_LINK_PROPERTIES: { 619 LinkProperties lp = new LinkProperties(mLinkProperties); 620 if (VDBG) log("REQ_GET_LINK_PROPERTIES linkProperties" + lp); 621 mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_PROPERTIES, lp); 622 break; 623 } 624 case DataConnectionAc.REQ_SET_LINK_PROPERTIES_HTTP_PROXY: { 625 ProxyProperties proxy = (ProxyProperties) msg.obj; 626 if (VDBG) log("REQ_SET_LINK_PROPERTIES_HTTP_PROXY proxy=" + proxy); 627 mLinkProperties.setHttpProxy(proxy); 628 mAc.replyToMessage(msg, DataConnectionAc.RSP_SET_LINK_PROPERTIES_HTTP_PROXY); 629 break; 630 } 631 case DataConnectionAc.REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE: { 632 DataCallState newState = (DataCallState) msg.obj; 633 UpdateLinkPropertyResult result = 634 updateLinkProperty(newState); 635 if (VDBG) { 636 log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE result=" 637 + result + " newState=" + newState); 638 } 639 mAc.replyToMessage(msg, 640 DataConnectionAc.RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE, 641 result); 642 break; 643 } 644 case DataConnectionAc.REQ_GET_LINK_CAPABILITIES: { 645 LinkCapabilities lc = new LinkCapabilities(mCapabilities); 646 if (VDBG) log("REQ_GET_LINK_CAPABILITIES linkCapabilities" + lc); 647 mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_CAPABILITIES, lc); 648 break; 649 } 650 case DataConnectionAc.REQ_RESET: 651 if (VDBG) log("DcDefaultState: msg.what=REQ_RESET"); 652 mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET); 653 transitionTo(mInactiveState); 654 break; 655 case DataConnectionAc.REQ_GET_REFCOUNT: { 656 if (VDBG) log("REQ_GET_REFCOUNT refCount=" + mRefCount); 657 mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_REFCOUNT, mRefCount); 658 break; 659 } 660 case DataConnectionAc.REQ_ADD_APNCONTEXT: { 661 ApnContext apnContext = (ApnContext) msg.obj; 662 if (VDBG) log("REQ_ADD_APNCONTEXT apn=" + apnContext.getApnType()); 663 if (!mApnList.contains(apnContext)) { 664 mApnList.add(apnContext); 665 } 666 mAc.replyToMessage(msg, DataConnectionAc.RSP_ADD_APNCONTEXT); 667 break; 668 } 669 case DataConnectionAc.REQ_REMOVE_APNCONTEXT: { 670 ApnContext apnContext = (ApnContext) msg.obj; 671 if (VDBG) log("REQ_REMOVE_APNCONTEXT apn=" + apnContext.getApnType()); 672 mApnList.remove(apnContext); 673 mAc.replyToMessage(msg, DataConnectionAc.RSP_REMOVE_APNCONTEXT); 674 break; 675 } 676 case DataConnectionAc.REQ_GET_APNCONTEXT_LIST: { 677 if (VDBG) log("REQ_GET_APNCONTEXT_LIST num in list=" + mApnList.size()); 678 mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNCONTEXT_LIST, 679 new ArrayList<ApnContext>(mApnList)); 680 break; 681 } 682 case DataConnectionAc.REQ_SET_RECONNECT_INTENT: { 683 PendingIntent intent = (PendingIntent) msg.obj; 684 if (VDBG) log("REQ_SET_RECONNECT_INTENT"); 685 mReconnectIntent = intent; 686 mAc.replyToMessage(msg, DataConnectionAc.RSP_SET_RECONNECT_INTENT); 687 break; 688 } 689 case DataConnectionAc.REQ_GET_RECONNECT_INTENT: { 690 if (VDBG) log("REQ_GET_RECONNECT_INTENT"); 691 mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_RECONNECT_INTENT, 692 mReconnectIntent); 693 break; 694 } 695 case EVENT_CONNECT: 696 if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected"); 697 ConnectionParams cp = (ConnectionParams) msg.obj; 698 notifyConnectCompleted(cp, FailCause.UNKNOWN); 699 break; 700 701 case EVENT_DISCONNECT: 702 if (DBG) log("DcDefaultState: msg.what=EVENT_DISCONNECT"); 703 notifyDisconnectCompleted((DisconnectParams) msg.obj); 704 break; 705 706 case EVENT_RIL_CONNECTED: 707 ar = (AsyncResult)msg.obj; 708 if (ar.exception == null) { 709 mRilVersion = (Integer)ar.result; 710 if (DBG) { 711 log("DcDefaultState: msg.what=EVENT_RIL_CONNECTED mRilVersion=" + 712 mRilVersion); 713 } 714 } else { 715 log("Unexpected exception on EVENT_RIL_CONNECTED"); 716 mRilVersion = -1; 717 } 718 break; 719 720 default: 721 if (DBG) { 722 log("DcDefaultState: shouldn't happen but ignore msg.what=0x" + 723 Integer.toHexString(msg.what)); 724 } 725 break; 726 } 727 728 return HANDLED; 729 } 730 } 731 private DcDefaultState mDefaultState = new DcDefaultState(); 732 733 /** 734 * The state machine is inactive and expects a EVENT_CONNECT. 735 */ 736 private class DcInactiveState extends State { 737 private ConnectionParams mConnectionParams = null; 738 private FailCause mFailCause = null; 739 private DisconnectParams mDisconnectParams = null; 740 741 public void setEnterNotificationParams(ConnectionParams cp, FailCause cause, 742 int retryOverride) { 743 if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause"); 744 mConnectionParams = cp; 745 mFailCause = cause; 746 mRetryOverride = retryOverride; 747 } 748 749 public void setEnterNotificationParams(DisconnectParams dp) { 750 if (VDBG) log("DcInactiveState: setEnterNoticationParams dp"); 751 mDisconnectParams = dp; 752 } 753 754 @Override 755 public void enter() { 756 mTag += 1; 757 758 /** 759 * Now that we've transitioned to Inactive state we 760 * can send notifications. Previously we sent the 761 * notifications in the processMessage handler but 762 * that caused a race condition because the synchronous 763 * call to isInactive. 764 */ 765 if ((mConnectionParams != null) && (mFailCause != null)) { 766 if (VDBG) log("DcInactiveState: enter notifyConnectCompleted"); 767 notifyConnectCompleted(mConnectionParams, mFailCause); 768 } 769 if (mDisconnectParams != null) { 770 if (VDBG) log("DcInactiveState: enter notifyDisconnectCompleted"); 771 notifyDisconnectCompleted(mDisconnectParams); 772 } 773 clearSettings(); 774 } 775 776 @Override 777 public void exit() { 778 // clear notifications 779 mConnectionParams = null; 780 mFailCause = null; 781 mDisconnectParams = null; 782 } 783 784 @Override 785 public boolean processMessage(Message msg) { 786 boolean retVal; 787 788 switch (msg.what) { 789 case DataConnectionAc.REQ_RESET: 790 if (DBG) { 791 log("DcInactiveState: msg.what=RSP_RESET, ignore we're already reset"); 792 } 793 mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET); 794 retVal = HANDLED; 795 break; 796 797 case EVENT_CONNECT: 798 ConnectionParams cp = (ConnectionParams) msg.obj; 799 cp.tag = mTag; 800 if (DBG) { 801 log("DcInactiveState msg.what=EVENT_CONNECT." + "RefCount = " 802 + mRefCount); 803 } 804 mRefCount = 1; 805 onConnect(cp); 806 transitionTo(mActivatingState); 807 retVal = HANDLED; 808 break; 809 810 default: 811 if (VDBG) { 812 log("DcInactiveState nothandled msg.what=0x" + 813 Integer.toHexString(msg.what)); 814 } 815 retVal = NOT_HANDLED; 816 break; 817 } 818 return retVal; 819 } 820 } 821 private DcInactiveState mInactiveState = new DcInactiveState(); 822 823 /** 824 * The state machine is activating a connection. 825 */ 826 private class DcActivatingState extends State { 827 @Override 828 public boolean processMessage(Message msg) { 829 boolean retVal; 830 AsyncResult ar; 831 ConnectionParams cp; 832 833 switch (msg.what) { 834 case EVENT_DISCONNECT: 835 if (DBG) log("DcActivatingState deferring msg.what=EVENT_DISCONNECT" 836 + mRefCount); 837 deferMessage(msg); 838 retVal = HANDLED; 839 break; 840 841 case EVENT_CONNECT: 842 if (DBG) log("DcActivatingState deferring msg.what=EVENT_CONNECT refCount = " 843 + mRefCount); 844 deferMessage(msg); 845 retVal = HANDLED; 846 break; 847 848 case EVENT_SETUP_DATA_CONNECTION_DONE: 849 if (DBG) log("DcActivatingState msg.what=EVENT_SETUP_DATA_CONNECTION_DONE"); 850 851 ar = (AsyncResult) msg.obj; 852 cp = (ConnectionParams) ar.userObj; 853 854 DataCallState.SetupResult result = onSetupConnectionCompleted(ar); 855 if (DBG) log("DcActivatingState onSetupConnectionCompleted result=" + result); 856 switch (result) { 857 case SUCCESS: 858 // All is well 859 mActiveState.setEnterNotificationParams(cp, FailCause.NONE); 860 transitionTo(mActiveState); 861 break; 862 case ERR_BadCommand: 863 // Vendor ril rejected the command and didn't connect. 864 // Transition to inactive but send notifications after 865 // we've entered the mInactive state. 866 mInactiveState.setEnterNotificationParams(cp, result.mFailCause, -1); 867 transitionTo(mInactiveState); 868 break; 869 case ERR_UnacceptableParameter: 870 // The addresses given from the RIL are bad 871 tearDownData(cp); 872 transitionTo(mDisconnectingErrorCreatingConnection); 873 break; 874 case ERR_GetLastErrorFromRil: 875 // Request failed and this is an old RIL 876 phone.mCM.getLastDataCallFailCause( 877 obtainMessage(EVENT_GET_LAST_FAIL_DONE, cp)); 878 break; 879 case ERR_RilError: 880 // Request failed and mFailCause has the reason 881 mInactiveState.setEnterNotificationParams(cp, result.mFailCause, 882 getSuggestedRetryTime(ar)); 883 transitionTo(mInactiveState); 884 break; 885 case ERR_Stale: 886 // Request is stale, ignore. 887 break; 888 default: 889 throw new RuntimeException("Unknown SetupResult, should not happen"); 890 } 891 retVal = HANDLED; 892 break; 893 894 case EVENT_GET_LAST_FAIL_DONE: 895 ar = (AsyncResult) msg.obj; 896 cp = (ConnectionParams) ar.userObj; 897 FailCause cause = FailCause.UNKNOWN; 898 899 if (cp.tag == mTag) { 900 if (DBG) log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE"); 901 if (ar.exception == null) { 902 int rilFailCause = ((int[]) (ar.result))[0]; 903 cause = FailCause.fromInt(rilFailCause); 904 } 905 // Transition to inactive but send notifications after 906 // we've entered the mInactive state. 907 mInactiveState.setEnterNotificationParams(cp, cause, -1); 908 transitionTo(mInactiveState); 909 } else { 910 if (DBG) { 911 log("DcActivatingState EVENT_GET_LAST_FAIL_DONE is stale cp.tag=" 912 + cp.tag + ", mTag=" + mTag); 913 } 914 } 915 916 retVal = HANDLED; 917 break; 918 919 default: 920 if (VDBG) { 921 log("DcActivatingState not handled msg.what=0x" + 922 Integer.toHexString(msg.what)); 923 } 924 retVal = NOT_HANDLED; 925 break; 926 } 927 return retVal; 928 } 929 } 930 private DcActivatingState mActivatingState = new DcActivatingState(); 931 932 /** 933 * The state machine is connected, expecting an EVENT_DISCONNECT. 934 */ 935 private class DcActiveState extends State { 936 private ConnectionParams mConnectionParams = null; 937 private FailCause mFailCause = null; 938 939 public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) { 940 if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause"); 941 mConnectionParams = cp; 942 mFailCause = cause; 943 } 944 945 @Override public void enter() { 946 /** 947 * Now that we've transitioned to Active state we 948 * can send notifications. Previously we sent the 949 * notifications in the processMessage handler but 950 * that caused a race condition because the synchronous 951 * call to isActive. 952 */ 953 if ((mConnectionParams != null) && (mFailCause != null)) { 954 if (VDBG) log("DcActiveState: enter notifyConnectCompleted"); 955 notifyConnectCompleted(mConnectionParams, mFailCause); 956 } 957 } 958 959 @Override 960 public void exit() { 961 // clear notifications 962 mConnectionParams = null; 963 mFailCause = null; 964 } 965 966 @Override 967 public boolean processMessage(Message msg) { 968 boolean retVal; 969 970 switch (msg.what) { 971 case EVENT_CONNECT: 972 mRefCount++; 973 if (DBG) log("DcActiveState msg.what=EVENT_CONNECT RefCount=" + mRefCount); 974 if (msg.obj != null) { 975 notifyConnectCompleted((ConnectionParams) msg.obj, FailCause.NONE); 976 } 977 retVal = HANDLED; 978 break; 979 case EVENT_DISCONNECT: 980 mRefCount--; 981 if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT RefCount=" + mRefCount); 982 if (mRefCount == 0) 983 { 984 DisconnectParams dp = (DisconnectParams) msg.obj; 985 dp.tag = mTag; 986 tearDownData(dp); 987 transitionTo(mDisconnectingState); 988 } else { 989 if (msg.obj != null) { 990 notifyDisconnectCompleted((DisconnectParams) msg.obj); 991 } 992 } 993 retVal = HANDLED; 994 break; 995 996 default: 997 if (VDBG) { 998 log("DcActiveState not handled msg.what=0x" + 999 Integer.toHexString(msg.what)); 1000 } 1001 retVal = NOT_HANDLED; 1002 break; 1003 } 1004 return retVal; 1005 } 1006 } 1007 private DcActiveState mActiveState = new DcActiveState(); 1008 1009 /** 1010 * The state machine is disconnecting. 1011 */ 1012 private class DcDisconnectingState extends State { 1013 @Override 1014 public boolean processMessage(Message msg) { 1015 boolean retVal; 1016 1017 switch (msg.what) { 1018 case EVENT_CONNECT: 1019 if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = " 1020 + mRefCount); 1021 deferMessage(msg); 1022 retVal = HANDLED; 1023 break; 1024 1025 case EVENT_DEACTIVATE_DONE: 1026 if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE"); 1027 AsyncResult ar = (AsyncResult) msg.obj; 1028 DisconnectParams dp = (DisconnectParams) ar.userObj; 1029 if (dp.tag == mTag) { 1030 // Transition to inactive but send notifications after 1031 // we've entered the mInactive state. 1032 mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj); 1033 transitionTo(mInactiveState); 1034 } else { 1035 if (DBG) log("DcDisconnectState EVENT_DEACTIVATE_DONE stale dp.tag=" 1036 + dp.tag + " mTag=" + mTag); 1037 } 1038 retVal = HANDLED; 1039 break; 1040 1041 default: 1042 if (VDBG) { 1043 log("DcDisconnectingState not handled msg.what=0x" + 1044 Integer.toHexString(msg.what)); 1045 } 1046 retVal = NOT_HANDLED; 1047 break; 1048 } 1049 return retVal; 1050 } 1051 } 1052 private DcDisconnectingState mDisconnectingState = new DcDisconnectingState(); 1053 1054 /** 1055 * The state machine is disconnecting after an creating a connection. 1056 */ 1057 private class DcDisconnectionErrorCreatingConnection extends State { 1058 @Override 1059 public boolean processMessage(Message msg) { 1060 boolean retVal; 1061 1062 switch (msg.what) { 1063 case EVENT_DEACTIVATE_DONE: 1064 AsyncResult ar = (AsyncResult) msg.obj; 1065 ConnectionParams cp = (ConnectionParams) ar.userObj; 1066 if (cp.tag == mTag) { 1067 if (DBG) { 1068 log("DcDisconnectionErrorCreatingConnection" + 1069 " msg.what=EVENT_DEACTIVATE_DONE"); 1070 } 1071 1072 // Transition to inactive but send notifications after 1073 // we've entered the mInactive state. 1074 mInactiveState.setEnterNotificationParams(cp, 1075 FailCause.UNACCEPTABLE_NETWORK_PARAMETER, -1); 1076 transitionTo(mInactiveState); 1077 } else { 1078 if (DBG) { 1079 log("DcDisconnectionErrorCreatingConnection EVENT_DEACTIVATE_DONE" + 1080 " stale dp.tag=" + cp.tag + ", mTag=" + mTag); 1081 } 1082 } 1083 retVal = HANDLED; 1084 break; 1085 1086 default: 1087 if (VDBG) { 1088 log("DcDisconnectionErrorCreatingConnection not handled msg.what=0x" 1089 + Integer.toHexString(msg.what)); 1090 } 1091 retVal = NOT_HANDLED; 1092 break; 1093 } 1094 return retVal; 1095 } 1096 } 1097 private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection = 1098 new DcDisconnectionErrorCreatingConnection(); 1099 1100 // ******* public interface 1101 1102 /** 1103 * Bring up a connection to the apn and return an AsyncResult in onCompletedMsg. 1104 * Used for cellular networks that use Acesss Point Names (APN) such 1105 * as GSM networks. 1106 * 1107 * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. 1108 * With AsyncResult.userObj set to the original msg.obj, 1109 * AsyncResult.result = FailCause and AsyncResult.exception = Exception(). 1110 * @param apn is the Access Point Name to bring up a connection to 1111 */ 1112 public void bringUp(Message onCompletedMsg, ApnSetting apn) { 1113 sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(apn, onCompletedMsg))); 1114 } 1115 1116 /** 1117 * Tear down the connection through the apn on the network. 1118 * 1119 * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. 1120 * With AsyncResult.userObj set to the original msg.obj. 1121 */ 1122 public void tearDown(String reason, Message onCompletedMsg) { 1123 sendMessage(obtainMessage(EVENT_DISCONNECT, new DisconnectParams(reason, onCompletedMsg))); 1124 } 1125 } 1126