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 import com.android.internal.telephony.gsm.ApnSetting; 20 21 import com.android.internal.util.HierarchicalState; 22 import com.android.internal.util.HierarchicalStateMachine; 23 24 import android.os.AsyncResult; 25 import android.os.Message; 26 import android.os.SystemProperties; 27 import android.util.EventLog; 28 29 /** 30 * {@hide} 31 * 32 * DataConnection HierarchicalStateMachine. 33 * 34 * This is an abstract base class for representing a single data connection. 35 * Instances of this class such as <code>CdmaDataConnection</code> and 36 * <code>GsmDataConnection</code>, * represent a connection via the cellular network. 37 * There may be multiple data connections and all of them are managed by the 38 * <code>DataConnectionTracker</code>. 39 * 40 * Instances are asynchronous state machines and have two primary entry points 41 * <code>connect()</code> and <code>disconnect</code>. The message a parameter will be returned 42 * hen the operation completes. The <code>msg.obj</code> will contain an AsyncResult 43 * object and <code>AsyncResult.userObj</code> is the original <code>msg.obj</code>. if successful 44 * with the <code>AsyncResult.result == null</code> and <code>AsyncResult.exception == null</code>. 45 * If an error <code>AsyncResult.result = FailCause</code> and 46 * <code>AsyncResult.exception = new Exception()</code>. 47 * 48 * The other public methods are provided for debugging. 49 * 50 * Below is the state machine description for this class. 51 * 52 * DataConnection { 53 * + mDefaultState { 54 * EVENT_RESET { clearSettings, notifiyDisconnectCompleted, >mInactiveState }. 55 * EVENT_CONNECT { notifyConnectCompleted(FailCause.UNKNOWN) }. 56 * EVENT_DISCONNECT { notifyDisconnectCompleted }. 57 * 58 * // Ignored messages 59 * EVENT_SETUP_DATA_CONNECTION_DONE, 60 * EVENT_GET_LAST_FAIL_DONE, 61 * EVENT_DEACTIVATE_DONE. 62 * } 63 * ++ # mInactiveState 64 * e(doNotifications) 65 * x(clearNotifications) { 66 * EVENT_RESET { notifiyDisconnectCompleted }. 67 * EVENT_CONNECT {startConnecting, >mActivatingState }. 68 * } 69 * ++ mActivatingState { 70 * EVENT_DISCONNECT { %EVENT_DISCONNECT }. 71 * EVENT_SETUP_DATA_CONNECTION_DONE { 72 * if (SUCCESS) { notifyConnectCompleted(FailCause.NONE), >mActiveState }. 73 * if (ERR_BadCommand) { 74 * notifyConnectCompleted(FailCause.UNKNOWN), >mInactiveState }. 75 * if (ERR_BadDns) { tearDownData($DEACTIVATE_DONE), >mDisconnectingBadDnsState }. 76 * if (ERR_Other) { getLastDataCallFailCause($EVENT_GET_LAST_FAIL_DONE) }. 77 * if (ERR_Stale) {}. 78 * } 79 * EVENT_GET_LAST_FAIL_DONE { notifyConnectCompleted(result), >mInactive }. 80 * } 81 * ++ mActiveState { 82 * EVENT_DISCONNECT { tearDownData($EVENT_DEACTIVATE_DONE), >mDisconnecting }. 83 * } 84 * ++ mDisconnectingState { 85 * EVENT_DEACTIVATE_DONE { notifyDisconnectCompleted, >mInactiveState }. 86 * } 87 * ++ mDisconnectingBadDnsState { 88 * EVENT_DEACTIVATE_DONE { notifyConnectComplete(FailCause.UNKNOWN), >mInactiveState }. 89 * } 90 * } 91 */ 92 public abstract class DataConnection extends HierarchicalStateMachine { 93 protected static final boolean DBG = true; 94 95 protected static Object mCountLock = new Object(); 96 protected static int mCount; 97 98 /** 99 * Class returned by onSetupConnectionCompleted. 100 */ 101 protected enum SetupResult { 102 ERR_BadCommand, 103 ERR_BadDns, 104 ERR_Other, 105 ERR_Stale, 106 SUCCESS; 107 108 public FailCause mFailCause; 109 110 @Override 111 public String toString() { 112 switch (this) { 113 case ERR_BadCommand: return "Bad Command"; 114 case ERR_BadDns: return "Bad DNS"; 115 case ERR_Other: return "Other error"; 116 case ERR_Stale: return "Stale command"; 117 case SUCCESS: return "SUCCESS"; 118 default: return "unknown"; 119 } 120 } 121 } 122 123 /** 124 * Used internally for saving connecting parameters. 125 */ 126 protected static class ConnectionParams { 127 public ConnectionParams(ApnSetting apn, Message onCompletedMsg) { 128 this.apn = apn; 129 this.onCompletedMsg = onCompletedMsg; 130 } 131 132 public int tag; 133 public ApnSetting apn; 134 public Message onCompletedMsg; 135 } 136 137 /** 138 * An instance used for notification of blockingReset. 139 * TODO: Remove when blockingReset is removed. 140 */ 141 class ResetSynchronouslyLock { 142 } 143 144 /** 145 * Used internally for saving disconnecting parameters. 146 */ 147 protected static class DisconnectParams { 148 public DisconnectParams(Message onCompletedMsg) { 149 this.onCompletedMsg = onCompletedMsg; 150 } 151 public DisconnectParams(ResetSynchronouslyLock lockObj) { 152 this.lockObj = lockObj; 153 } 154 155 public int tag; 156 public Message onCompletedMsg; 157 public ResetSynchronouslyLock lockObj; 158 } 159 160 /** 161 * Returned as the reason for a connection failure. 162 */ 163 public enum FailCause { 164 NONE, 165 OPERATOR_BARRED, 166 INSUFFICIENT_RESOURCES, 167 MISSING_UNKNOWN_APN, 168 UNKNOWN_PDP_ADDRESS, 169 USER_AUTHENTICATION, 170 ACTIVATION_REJECT_GGSN, 171 ACTIVATION_REJECT_UNSPECIFIED, 172 SERVICE_OPTION_NOT_SUPPORTED, 173 SERVICE_OPTION_NOT_SUBSCRIBED, 174 SERVICE_OPTION_OUT_OF_ORDER, 175 NSAPI_IN_USE, 176 PROTOCOL_ERRORS, 177 REGISTRATION_FAIL, 178 GPRS_REGISTRATION_FAIL, 179 UNKNOWN, 180 181 RADIO_NOT_AVAILABLE; 182 183 public boolean isPermanentFail() { 184 return (this == OPERATOR_BARRED) || (this == MISSING_UNKNOWN_APN) || 185 (this == UNKNOWN_PDP_ADDRESS) || (this == USER_AUTHENTICATION) || 186 (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) || 187 (this == SERVICE_OPTION_NOT_SUPPORTED) || 188 (this == SERVICE_OPTION_NOT_SUBSCRIBED) || (this == NSAPI_IN_USE) || 189 (this == PROTOCOL_ERRORS); 190 } 191 192 public boolean isEventLoggable() { 193 return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) || 194 (this == UNKNOWN_PDP_ADDRESS) || (this == USER_AUTHENTICATION) || 195 (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) || 196 (this == SERVICE_OPTION_NOT_SUBSCRIBED) || 197 (this == SERVICE_OPTION_NOT_SUPPORTED) || 198 (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) || 199 (this == PROTOCOL_ERRORS); 200 } 201 202 @Override 203 public String toString() { 204 switch (this) { 205 case NONE: 206 return "No Error"; 207 case OPERATOR_BARRED: 208 return "Operator Barred"; 209 case INSUFFICIENT_RESOURCES: 210 return "Insufficient Resources"; 211 case MISSING_UNKNOWN_APN: 212 return "Missing / Unknown APN"; 213 case UNKNOWN_PDP_ADDRESS: 214 return "Unknown PDP Address"; 215 case USER_AUTHENTICATION: 216 return "Error User Authentication"; 217 case ACTIVATION_REJECT_GGSN: 218 return "Activation Reject GGSN"; 219 case ACTIVATION_REJECT_UNSPECIFIED: 220 return "Activation Reject unspecified"; 221 case SERVICE_OPTION_NOT_SUPPORTED: 222 return "Data Not Supported"; 223 case SERVICE_OPTION_NOT_SUBSCRIBED: 224 return "Data Not subscribed"; 225 case SERVICE_OPTION_OUT_OF_ORDER: 226 return "Data Services Out of Order"; 227 case NSAPI_IN_USE: 228 return "NSAPI in use"; 229 case PROTOCOL_ERRORS: 230 return "Protocol Errors"; 231 case REGISTRATION_FAIL: 232 return "Network Registration Failure"; 233 case GPRS_REGISTRATION_FAIL: 234 return "Data Network Registration Failure"; 235 case RADIO_NOT_AVAILABLE: 236 return "Radio Not Available"; 237 default: 238 return "Unknown Data Error"; 239 } 240 } 241 } 242 243 // ***** Event codes for driving the state machine 244 protected static final int EVENT_RESET = 1; 245 protected static final int EVENT_CONNECT = 2; 246 protected static final int EVENT_SETUP_DATA_CONNECTION_DONE = 3; 247 protected static final int EVENT_GET_LAST_FAIL_DONE = 4; 248 protected static final int EVENT_DEACTIVATE_DONE = 5; 249 protected static final int EVENT_DISCONNECT = 6; 250 251 //***** Tag IDs for EventLog 252 protected static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100; 253 254 //***** Member Variables 255 protected int mTag; 256 protected PhoneBase phone; 257 protected int cid; 258 protected String interfaceName; 259 protected String ipAddress; 260 protected String gatewayAddress; 261 protected String[] dnsServers; 262 protected long createTime; 263 protected long lastFailTime; 264 protected FailCause lastFailCause; 265 protected static final String NULL_IP = "0.0.0.0"; 266 Object userData; 267 268 //***** Abstract methods 269 public abstract String toString(); 270 271 protected abstract void onConnect(ConnectionParams cp); 272 273 protected abstract FailCause getFailCauseFromRequest(int rilCause); 274 275 protected abstract boolean isDnsOk(String[] domainNameServers); 276 277 protected abstract void log(String s); 278 279 280 //***** Constructor 281 protected DataConnection(PhoneBase phone, String name) { 282 super(name); 283 if (DBG) log("DataConnection constructor E"); 284 this.phone = phone; 285 this.cid = -1; 286 this.dnsServers = new String[2]; 287 288 clearSettings(); 289 290 setDbg(false); 291 addState(mDefaultState); 292 addState(mInactiveState, mDefaultState); 293 addState(mActivatingState, mDefaultState); 294 addState(mActiveState, mDefaultState); 295 addState(mDisconnectingState, mDefaultState); 296 addState(mDisconnectingBadDnsState, mDefaultState); 297 setInitialState(mInactiveState); 298 if (DBG) log("DataConnection constructor X"); 299 } 300 301 /** 302 * TearDown the data connection. 303 * 304 * @param o will be returned in AsyncResult.userObj 305 * and is either a DisconnectParams or ConnectionParams. 306 */ 307 private void tearDownData(Object o) { 308 if (phone.mCM.getRadioState().isOn()) { 309 if (DBG) log("tearDownData radio is on, call deactivateDataCall"); 310 phone.mCM.deactivateDataCall(cid, obtainMessage(EVENT_DEACTIVATE_DONE, o)); 311 } else { 312 if (DBG) log("tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately"); 313 AsyncResult ar = new AsyncResult(o, null, null); 314 sendMessage(obtainMessage(EVENT_DEACTIVATE_DONE, ar)); 315 } 316 } 317 318 /** 319 * Send the connectionCompletedMsg. 320 * 321 * @param cp is the ConnectionParams 322 * @param cause 323 */ 324 private void notifyConnectCompleted(ConnectionParams cp, FailCause cause) { 325 Message connectionCompletedMsg = cp.onCompletedMsg; 326 if (connectionCompletedMsg == null) { 327 return; 328 } 329 330 long timeStamp = System.currentTimeMillis(); 331 connectionCompletedMsg.arg1 = cid; 332 333 if (cause == FailCause.NONE) { 334 createTime = timeStamp; 335 AsyncResult.forMessage(connectionCompletedMsg); 336 } else { 337 lastFailCause = cause; 338 lastFailTime = timeStamp; 339 AsyncResult.forMessage(connectionCompletedMsg, cause, new Exception()); 340 } 341 if (DBG) log("notifyConnection at " + timeStamp + " cause=" + cause); 342 343 connectionCompletedMsg.sendToTarget(); 344 } 345 346 /** 347 * Send ar.userObj if its a message, which is should be back to originator. 348 * 349 * @param dp is the DisconnectParams. 350 */ 351 private void notifyDisconnectCompleted(DisconnectParams dp) { 352 if (DBG) log("NotifyDisconnectCompleted"); 353 354 if (dp.onCompletedMsg != null) { 355 Message msg = dp.onCompletedMsg; 356 log(String.format("msg.what=%d msg.obj=%s", 357 msg.what, ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>"))); 358 AsyncResult.forMessage(msg); 359 msg.sendToTarget(); 360 } 361 if (dp.lockObj != null) { 362 synchronized(dp.lockObj) { 363 dp.lockObj.notify(); 364 } 365 } 366 367 clearSettings(); 368 } 369 370 /** 371 * Clear all settings called when entering mInactiveState. 372 */ 373 protected void clearSettings() { 374 if (DBG) log("clearSettings"); 375 376 this.createTime = -1; 377 this.lastFailTime = -1; 378 this.lastFailCause = FailCause.NONE; 379 380 interfaceName = null; 381 ipAddress = null; 382 gatewayAddress = null; 383 dnsServers[0] = null; 384 dnsServers[1] = null; 385 } 386 387 /** 388 * Process setup completion. 389 * 390 * @param ar is the result 391 * @return SetupResult. 392 */ 393 private SetupResult onSetupConnectionCompleted(AsyncResult ar) { 394 SetupResult result; 395 String[] response = ((String[]) ar.result); 396 ConnectionParams cp = (ConnectionParams) ar.userObj; 397 398 if (ar.exception != null) { 399 if (DBG) log("DataConnection Init failed " + ar.exception); 400 401 if (ar.exception instanceof CommandException 402 && ((CommandException) (ar.exception)).getCommandError() 403 == CommandException.Error.RADIO_NOT_AVAILABLE) { 404 result = SetupResult.ERR_BadCommand; 405 result.mFailCause = FailCause.RADIO_NOT_AVAILABLE; 406 } else { 407 result = SetupResult.ERR_Other; 408 } 409 } else if (cp.tag != mTag) { 410 if (DBG) { 411 log("BUG: onSetupConnectionCompleted is stale cp.tag=" + cp.tag + ", mtag=" + mTag); 412 } 413 result = SetupResult.ERR_Stale; 414 } else { 415 // log("onSetupConnectionCompleted received " + response.length + " response strings:"); 416 // for (int i = 0; i < response.length; i++) { 417 // log(" response[" + i + "]='" + response[i] + "'"); 418 // } 419 if (response.length >= 2) { 420 cid = Integer.parseInt(response[0]); 421 interfaceName = response[1]; 422 if (response.length > 2) { 423 ipAddress = response[2]; 424 String prefix = "net." + interfaceName + "."; 425 gatewayAddress = SystemProperties.get(prefix + "gw"); 426 dnsServers[0] = SystemProperties.get(prefix + "dns1"); 427 dnsServers[1] = SystemProperties.get(prefix + "dns2"); 428 if (DBG) { 429 log("interface=" + interfaceName + " ipAddress=" + ipAddress 430 + " gateway=" + gatewayAddress + " DNS1=" + dnsServers[0] 431 + " DNS2=" + dnsServers[1]); 432 } 433 434 if (isDnsOk(dnsServers)) { 435 result = SetupResult.SUCCESS; 436 } else { 437 result = SetupResult.ERR_BadDns; 438 } 439 } else { 440 result = SetupResult.SUCCESS; 441 } 442 } else { 443 result = SetupResult.ERR_Other; 444 } 445 } 446 447 if (DBG) log("DataConnection setup result='" + result + "' on cid=" + cid); 448 return result; 449 } 450 451 /** 452 * The parent state for all other states. 453 */ 454 private class DcDefaultState extends HierarchicalState { 455 @Override 456 protected boolean processMessage(Message msg) { 457 AsyncResult ar; 458 459 switch (msg.what) { 460 case EVENT_RESET: 461 if (DBG) log("DcDefaultState: msg.what=EVENT_RESET"); 462 clearSettings(); 463 if (msg.obj != null) { 464 notifyDisconnectCompleted((DisconnectParams) msg.obj); 465 } 466 transitionTo(mInactiveState); 467 break; 468 469 case EVENT_CONNECT: 470 if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected"); 471 ConnectionParams cp = (ConnectionParams) msg.obj; 472 notifyConnectCompleted(cp, FailCause.UNKNOWN); 473 break; 474 475 case EVENT_DISCONNECT: 476 if (DBG) log("DcDefaultState: msg.what=EVENT_DISCONNECT"); 477 notifyDisconnectCompleted((DisconnectParams) msg.obj); 478 break; 479 480 default: 481 if (DBG) { 482 log("DcDefaultState: shouldn't happen but ignore msg.what=" + msg.what); 483 } 484 break; 485 } 486 487 return true; 488 } 489 } 490 private DcDefaultState mDefaultState = new DcDefaultState(); 491 492 /** 493 * The state machine is inactive and expects a EVENT_CONNECT. 494 */ 495 private class DcInactiveState extends HierarchicalState { 496 private ConnectionParams mConnectionParams = null; 497 private FailCause mFailCause = null; 498 private DisconnectParams mDisconnectParams = null; 499 500 public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) { 501 log("DcInactiveState: setEnterNoticationParams cp,cause"); 502 mConnectionParams = cp; 503 mFailCause = cause; 504 } 505 506 public void setEnterNotificationParams(DisconnectParams dp) { 507 log("DcInactiveState: setEnterNoticationParams dp"); 508 mDisconnectParams = dp; 509 } 510 511 @Override protected void enter() { 512 mTag += 1; 513 514 /** 515 * Now that we've transitioned to Inactive state we 516 * can send notifications. Previously we sent the 517 * notifications in the processMessage handler but 518 * that caused a race condition because the synchronous 519 * call to isInactive. 520 */ 521 if ((mConnectionParams != null) && (mFailCause != null)) { 522 log("DcInactiveState: enter notifyConnectCompleted"); 523 notifyConnectCompleted(mConnectionParams, mFailCause); 524 } 525 if (mDisconnectParams != null) { 526 log("DcInactiveState: enter notifyDisconnectCompleted"); 527 notifyDisconnectCompleted(mDisconnectParams); 528 } 529 } 530 531 @Override protected void exit() { 532 // clear notifications 533 mConnectionParams = null; 534 mFailCause = null; 535 mDisconnectParams = null; 536 } 537 538 @Override protected boolean processMessage(Message msg) { 539 boolean retVal; 540 541 switch (msg.what) { 542 case EVENT_RESET: 543 if (DBG) { 544 log("DcInactiveState: msg.what=EVENT_RESET, ignore we're already reset"); 545 } 546 if (msg.obj != null) { 547 notifyDisconnectCompleted((DisconnectParams) msg.obj); 548 } 549 retVal = true; 550 break; 551 552 case EVENT_CONNECT: 553 if (DBG) log("DcInactiveState msg.what=EVENT_CONNECT"); 554 ConnectionParams cp = (ConnectionParams) msg.obj; 555 cp.tag = mTag; 556 onConnect(cp); 557 transitionTo(mActivatingState); 558 retVal = true; 559 break; 560 561 default: 562 if (DBG) log("DcInactiveState nothandled msg.what=" + msg.what); 563 retVal = false; 564 break; 565 } 566 return retVal; 567 } 568 } 569 private DcInactiveState mInactiveState = new DcInactiveState(); 570 571 /** 572 * The state machine is activating a connection. 573 */ 574 private class DcActivatingState extends HierarchicalState { 575 @Override protected boolean processMessage(Message msg) { 576 boolean retVal; 577 AsyncResult ar; 578 ConnectionParams cp; 579 580 switch (msg.what) { 581 case EVENT_DISCONNECT: 582 if (DBG) log("DcActivatingState deferring msg.what=EVENT_DISCONNECT"); 583 deferMessage(msg); 584 retVal = true; 585 break; 586 587 case EVENT_SETUP_DATA_CONNECTION_DONE: 588 if (DBG) log("DcActivatingState msg.what=EVENT_SETUP_DATA_CONNECTION_DONE"); 589 590 ar = (AsyncResult) msg.obj; 591 cp = (ConnectionParams) ar.userObj; 592 593 SetupResult result = onSetupConnectionCompleted(ar); 594 switch (result) { 595 case SUCCESS: 596 // All is well 597 mActiveState.setEnterNotificationParams(cp, FailCause.NONE); 598 transitionTo(mActiveState); 599 break; 600 case ERR_BadCommand: 601 // Vendor ril rejected the command and didn't connect. 602 // Transition to inactive but send notifications after 603 // we've entered the mInactive state. 604 mInactiveState.setEnterNotificationParams(cp, result.mFailCause); 605 transitionTo(mInactiveState); 606 break; 607 case ERR_BadDns: 608 // Connection succeeded but DNS info is bad so disconnect 609 EventLog.writeEvent(EventLogTags.PDP_BAD_DNS_ADDRESS, dnsServers[0]); 610 tearDownData(cp); 611 transitionTo(mDisconnectingBadDnsState); 612 break; 613 case ERR_Other: 614 // Request the failure cause and process in this state 615 phone.mCM.getLastDataCallFailCause( 616 obtainMessage(EVENT_GET_LAST_FAIL_DONE, cp)); 617 break; 618 case ERR_Stale: 619 // Request is stale, ignore. 620 break; 621 default: 622 throw new RuntimeException("Unkown SetupResult, should not happen"); 623 } 624 retVal = true; 625 break; 626 627 case EVENT_GET_LAST_FAIL_DONE: 628 ar = (AsyncResult) msg.obj; 629 cp = (ConnectionParams) ar.userObj; 630 FailCause cause = FailCause.UNKNOWN; 631 632 if (cp.tag == mTag) { 633 if (DBG) log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE"); 634 if (ar.exception == null) { 635 int rilFailCause = ((int[]) (ar.result))[0]; 636 cause = getFailCauseFromRequest(rilFailCause); 637 } 638 // Transition to inactive but send notifications after 639 // we've entered the mInactive state. 640 mInactiveState.setEnterNotificationParams(cp, cause); 641 transitionTo(mInactiveState); 642 } else { 643 if (DBG) { 644 log("DcActivatingState EVENT_GET_LAST_FAIL_DONE is stale cp.tag=" 645 + cp.tag + ", mTag=" + mTag); 646 } 647 } 648 649 retVal = true; 650 break; 651 652 default: 653 if (DBG) log("DcActivatingState not handled msg.what=" + msg.what); 654 retVal = false; 655 break; 656 } 657 return retVal; 658 } 659 } 660 private DcActivatingState mActivatingState = new DcActivatingState(); 661 662 /** 663 * The state machine is connected, expecting an EVENT_DISCONNECT. 664 */ 665 private class DcActiveState extends HierarchicalState { 666 private ConnectionParams mConnectionParams = null; 667 private FailCause mFailCause = null; 668 669 public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) { 670 log("DcInactiveState: setEnterNoticationParams cp,cause"); 671 mConnectionParams = cp; 672 mFailCause = cause; 673 } 674 675 @Override public void enter() { 676 /** 677 * Now that we've transitioned to Active state we 678 * can send notifications. Previously we sent the 679 * notifications in the processMessage handler but 680 * that caused a race condition because the synchronous 681 * call to isActive. 682 */ 683 if ((mConnectionParams != null) && (mFailCause != null)) { 684 log("DcActiveState: enter notifyConnectCompleted"); 685 notifyConnectCompleted(mConnectionParams, mFailCause); 686 } 687 } 688 689 @Override protected void exit() { 690 // clear notifications 691 mConnectionParams = null; 692 mFailCause = null; 693 } 694 695 @Override protected boolean processMessage(Message msg) { 696 boolean retVal; 697 698 switch (msg.what) { 699 case EVENT_DISCONNECT: 700 if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT"); 701 DisconnectParams dp = (DisconnectParams) msg.obj; 702 dp.tag = mTag; 703 tearDownData(dp); 704 transitionTo(mDisconnectingState); 705 retVal = true; 706 break; 707 708 default: 709 if (DBG) log("DcActiveState nothandled msg.what=" + msg.what); 710 retVal = false; 711 break; 712 } 713 return retVal; 714 } 715 } 716 private DcActiveState mActiveState = new DcActiveState(); 717 718 /** 719 * The state machine is disconnecting. 720 */ 721 private class DcDisconnectingState extends HierarchicalState { 722 @Override protected boolean processMessage(Message msg) { 723 boolean retVal; 724 725 switch (msg.what) { 726 case EVENT_DEACTIVATE_DONE: 727 if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE"); 728 AsyncResult ar = (AsyncResult) msg.obj; 729 DisconnectParams dp = (DisconnectParams) ar.userObj; 730 if (dp.tag == mTag) { 731 // Transition to inactive but send notifications after 732 // we've entered the mInactive state. 733 mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj); 734 transitionTo(mInactiveState); 735 } else { 736 if (DBG) log("DcDisconnectState EVENT_DEACTIVATE_DONE stale dp.tag=" 737 + dp.tag + " mTag=" + mTag); 738 } 739 retVal = true; 740 break; 741 742 default: 743 if (DBG) log("DcDisconnectingState not handled msg.what=" + msg.what); 744 retVal = false; 745 break; 746 } 747 return retVal; 748 } 749 } 750 private DcDisconnectingState mDisconnectingState = new DcDisconnectingState(); 751 752 /** 753 * The state machine is disconnecting after a bad dns setup 754 * was found in mInactivatingState. 755 */ 756 private class DcDisconnectingBadDnsState extends HierarchicalState { 757 @Override protected boolean processMessage(Message msg) { 758 boolean retVal; 759 760 switch (msg.what) { 761 case EVENT_DEACTIVATE_DONE: 762 AsyncResult ar = (AsyncResult) msg.obj; 763 ConnectionParams cp = (ConnectionParams) ar.userObj; 764 if (cp.tag == mTag) { 765 if (DBG) log("DcDisconnectingBadDnsState msg.what=EVENT_DEACTIVATE_DONE"); 766 // Transition to inactive but send notifications after 767 // we've entered the mInactive state. 768 mInactiveState.setEnterNotificationParams(cp, FailCause.UNKNOWN); 769 transitionTo(mInactiveState); 770 } else { 771 if (DBG) log("DcDisconnectingBadDnsState EVENT_DEACTIVE_DONE stale dp.tag=" 772 + cp.tag + ", mTag=" + mTag); 773 } 774 retVal = true; 775 break; 776 777 default: 778 if (DBG) log("DcDisconnectingBadDnsState not handled msg.what=" + msg.what); 779 retVal = false; 780 break; 781 } 782 return retVal; 783 } 784 } 785 private DcDisconnectingBadDnsState mDisconnectingBadDnsState = new DcDisconnectingBadDnsState(); 786 787 // ******* public interface 788 789 /** 790 * Disconnect from the network. 791 * 792 * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. 793 * With AsyncResult.userObj set to the original msg.obj. 794 */ 795 public void reset(Message onCompletedMsg) { 796 sendMessage(obtainMessage(EVENT_RESET, new DisconnectParams(onCompletedMsg))); 797 } 798 799 /** 800 * Reset the connection and wait for it to complete. 801 * TODO: Remove when all callers only need the asynchronous 802 * reset defined above. 803 */ 804 public void resetSynchronously() { 805 ResetSynchronouslyLock lockObj = new ResetSynchronouslyLock(); 806 synchronized(lockObj) { 807 sendMessage(obtainMessage(EVENT_RESET, new DisconnectParams(lockObj))); 808 try { 809 lockObj.wait(); 810 } catch (InterruptedException e) { 811 log("blockingReset: unexpected interrupted of wait()"); 812 } 813 } 814 } 815 816 /** 817 * Connect to the apn and return an AsyncResult in onCompletedMsg. 818 * Used for cellular networks that use Acess Point Names (APN) such 819 * as GSM networks. 820 * 821 * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. 822 * With AsyncResult.userObj set to the original msg.obj, 823 * AsyncResult.result = FailCause and AsyncResult.exception = Exception(). 824 * @param apn is the Acces Point Name to connect to 825 */ 826 public void connect(Message onCompletedMsg, ApnSetting apn) { 827 sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(apn, onCompletedMsg))); 828 } 829 830 /** 831 * Connect to the apn and return an AsyncResult in onCompletedMsg. 832 * 833 * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. 834 * With AsyncResult.userObj set to the original msg.obj, 835 * AsyncResult.result = FailCause and AsyncResult.exception = Exception(). 836 */ 837 public void connect(Message onCompletedMsg) { 838 sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(null, onCompletedMsg))); 839 } 840 841 /** 842 * Disconnect from the network. 843 * 844 * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. 845 * With AsyncResult.userObj set to the original msg.obj. 846 */ 847 public void disconnect(Message onCompletedMsg) { 848 sendMessage(obtainMessage(EVENT_DISCONNECT, new DisconnectParams(onCompletedMsg))); 849 } 850 851 // ****** The following are used for debugging. 852 853 /** 854 * TODO: This should be an asynchronous call and we wouldn't 855 * have to use handle the notification in the DcInactiveState.enter. 856 * 857 * @return true if the state machine is in the inactive state. 858 */ 859 public boolean isInactive() { 860 boolean retVal = getCurrentState() == mInactiveState; 861 return retVal; 862 } 863 864 /** 865 * TODO: This should be an asynchronous call and we wouldn't 866 * have to use handle the notification in the DcActiveState.enter. 867 * 868 * @return true if the state machine is in the active state. 869 */ 870 public boolean isActive() { 871 boolean retVal = getCurrentState() == mActiveState; 872 return retVal; 873 } 874 875 /** 876 * @return the interface name as a string. 877 */ 878 public String getInterface() { 879 return interfaceName; 880 } 881 882 /** 883 * @return the ip address as a string. 884 */ 885 public String getIpAddress() { 886 return ipAddress; 887 } 888 889 /** 890 * @return the gateway address as a string. 891 */ 892 public String getGatewayAddress() { 893 return gatewayAddress; 894 } 895 896 /** 897 * @return an array of associated DNS addresses. 898 */ 899 public String[] getDnsServers() { 900 return dnsServers; 901 } 902 903 /** 904 * @return the current state as a string. 905 */ 906 public String getStateAsString() { 907 String retVal = getCurrentState().getName(); 908 return retVal; 909 } 910 911 /** 912 * @return the time of when this connection was created. 913 */ 914 public long getConnectionTime() { 915 return createTime; 916 } 917 918 /** 919 * @return the time of the last failure. 920 */ 921 public long getLastFailTime() { 922 return lastFailTime; 923 } 924 925 /** 926 * @return the last cause of failure. 927 */ 928 public FailCause getLastFailCause() { 929 return lastFailCause; 930 } 931 } 932