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) { 703 log("DcDefaultState deferring msg.what=EVENT_DISCONNECT" + mRefCount); 704 } 705 deferMessage(msg); 706 break; 707 708 case EVENT_RIL_CONNECTED: 709 ar = (AsyncResult)msg.obj; 710 if (ar.exception == null) { 711 mRilVersion = (Integer)ar.result; 712 if (DBG) { 713 log("DcDefaultState: msg.what=EVENT_RIL_CONNECTED mRilVersion=" + 714 mRilVersion); 715 } 716 } else { 717 log("Unexpected exception on EVENT_RIL_CONNECTED"); 718 mRilVersion = -1; 719 } 720 break; 721 722 default: 723 if (DBG) { 724 log("DcDefaultState: shouldn't happen but ignore msg.what=0x" + 725 Integer.toHexString(msg.what)); 726 } 727 break; 728 } 729 730 return HANDLED; 731 } 732 } 733 private DcDefaultState mDefaultState = new DcDefaultState(); 734 735 /** 736 * The state machine is inactive and expects a EVENT_CONNECT. 737 */ 738 private class DcInactiveState extends State { 739 private ConnectionParams mConnectionParams = null; 740 private FailCause mFailCause = null; 741 private DisconnectParams mDisconnectParams = null; 742 743 public void setEnterNotificationParams(ConnectionParams cp, FailCause cause, 744 int retryOverride) { 745 if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause"); 746 mConnectionParams = cp; 747 mFailCause = cause; 748 mRetryOverride = retryOverride; 749 } 750 751 public void setEnterNotificationParams(DisconnectParams dp) { 752 if (VDBG) log("DcInactiveState: setEnterNoticationParams dp"); 753 mDisconnectParams = dp; 754 } 755 756 @Override 757 public void enter() { 758 mTag += 1; 759 760 /** 761 * Now that we've transitioned to Inactive state we 762 * can send notifications. Previously we sent the 763 * notifications in the processMessage handler but 764 * that caused a race condition because the synchronous 765 * call to isInactive. 766 */ 767 if ((mConnectionParams != null) && (mFailCause != null)) { 768 if (VDBG) log("DcInactiveState: enter notifyConnectCompleted"); 769 notifyConnectCompleted(mConnectionParams, mFailCause); 770 } 771 if (mDisconnectParams != null) { 772 if (VDBG) log("DcInactiveState: enter notifyDisconnectCompleted"); 773 notifyDisconnectCompleted(mDisconnectParams); 774 } 775 clearSettings(); 776 } 777 778 @Override 779 public void exit() { 780 // clear notifications 781 mConnectionParams = null; 782 mFailCause = null; 783 mDisconnectParams = null; 784 } 785 786 @Override 787 public boolean processMessage(Message msg) { 788 boolean retVal; 789 790 switch (msg.what) { 791 case DataConnectionAc.REQ_RESET: 792 if (DBG) { 793 log("DcInactiveState: msg.what=RSP_RESET, ignore we're already reset"); 794 } 795 mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET); 796 retVal = HANDLED; 797 break; 798 799 case EVENT_CONNECT: 800 ConnectionParams cp = (ConnectionParams) msg.obj; 801 cp.tag = mTag; 802 if (DBG) { 803 log("DcInactiveState msg.what=EVENT_CONNECT." + "RefCount = " 804 + mRefCount); 805 } 806 mRefCount = 1; 807 onConnect(cp); 808 transitionTo(mActivatingState); 809 retVal = HANDLED; 810 break; 811 812 case EVENT_DISCONNECT: 813 if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT"); 814 notifyDisconnectCompleted((DisconnectParams)msg.obj); 815 retVal = HANDLED; 816 break; 817 818 default: 819 if (VDBG) { 820 log("DcInactiveState nothandled msg.what=0x" + 821 Integer.toHexString(msg.what)); 822 } 823 retVal = NOT_HANDLED; 824 break; 825 } 826 return retVal; 827 } 828 } 829 private DcInactiveState mInactiveState = new DcInactiveState(); 830 831 /** 832 * The state machine is activating a connection. 833 */ 834 private class DcActivatingState extends State { 835 @Override 836 public boolean processMessage(Message msg) { 837 boolean retVal; 838 AsyncResult ar; 839 ConnectionParams cp; 840 841 switch (msg.what) { 842 case EVENT_CONNECT: 843 if (DBG) log("DcActivatingState deferring msg.what=EVENT_CONNECT refCount = " 844 + mRefCount); 845 deferMessage(msg); 846 retVal = HANDLED; 847 break; 848 849 case EVENT_SETUP_DATA_CONNECTION_DONE: 850 if (DBG) log("DcActivatingState msg.what=EVENT_SETUP_DATA_CONNECTION_DONE"); 851 852 ar = (AsyncResult) msg.obj; 853 cp = (ConnectionParams) ar.userObj; 854 855 DataCallState.SetupResult result = onSetupConnectionCompleted(ar); 856 if (DBG) log("DcActivatingState onSetupConnectionCompleted result=" + result); 857 switch (result) { 858 case SUCCESS: 859 // All is well 860 mActiveState.setEnterNotificationParams(cp, FailCause.NONE); 861 transitionTo(mActiveState); 862 break; 863 case ERR_BadCommand: 864 // Vendor ril rejected the command and didn't connect. 865 // Transition to inactive but send notifications after 866 // we've entered the mInactive state. 867 mInactiveState.setEnterNotificationParams(cp, result.mFailCause, -1); 868 transitionTo(mInactiveState); 869 break; 870 case ERR_UnacceptableParameter: 871 // The addresses given from the RIL are bad 872 tearDownData(cp); 873 transitionTo(mDisconnectingErrorCreatingConnection); 874 break; 875 case ERR_GetLastErrorFromRil: 876 // Request failed and this is an old RIL 877 phone.mCM.getLastDataCallFailCause( 878 obtainMessage(EVENT_GET_LAST_FAIL_DONE, cp)); 879 break; 880 case ERR_RilError: 881 // Request failed and mFailCause has the reason 882 mInactiveState.setEnterNotificationParams(cp, result.mFailCause, 883 getSuggestedRetryTime(ar)); 884 transitionTo(mInactiveState); 885 break; 886 case ERR_Stale: 887 // Request is stale, ignore. 888 break; 889 default: 890 throw new RuntimeException("Unknown SetupResult, should not happen"); 891 } 892 retVal = HANDLED; 893 break; 894 895 case EVENT_GET_LAST_FAIL_DONE: 896 ar = (AsyncResult) msg.obj; 897 cp = (ConnectionParams) ar.userObj; 898 FailCause cause = FailCause.UNKNOWN; 899 900 if (cp.tag == mTag) { 901 if (DBG) log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE"); 902 if (ar.exception == null) { 903 int rilFailCause = ((int[]) (ar.result))[0]; 904 cause = FailCause.fromInt(rilFailCause); 905 } 906 // Transition to inactive but send notifications after 907 // we've entered the mInactive state. 908 mInactiveState.setEnterNotificationParams(cp, cause, -1); 909 transitionTo(mInactiveState); 910 } else { 911 if (DBG) { 912 log("DcActivatingState EVENT_GET_LAST_FAIL_DONE is stale cp.tag=" 913 + cp.tag + ", mTag=" + mTag); 914 } 915 } 916 917 retVal = HANDLED; 918 break; 919 920 default: 921 if (VDBG) { 922 log("DcActivatingState not handled msg.what=0x" + 923 Integer.toHexString(msg.what)); 924 } 925 retVal = NOT_HANDLED; 926 break; 927 } 928 return retVal; 929 } 930 } 931 private DcActivatingState mActivatingState = new DcActivatingState(); 932 933 /** 934 * The state machine is connected, expecting an EVENT_DISCONNECT. 935 */ 936 private class DcActiveState extends State { 937 private ConnectionParams mConnectionParams = null; 938 private FailCause mFailCause = null; 939 940 public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) { 941 if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause"); 942 mConnectionParams = cp; 943 mFailCause = cause; 944 } 945 946 @Override public void enter() { 947 /** 948 * Now that we've transitioned to Active state we 949 * can send notifications. Previously we sent the 950 * notifications in the processMessage handler but 951 * that caused a race condition because the synchronous 952 * call to isActive. 953 */ 954 if ((mConnectionParams != null) && (mFailCause != null)) { 955 if (VDBG) log("DcActiveState: enter notifyConnectCompleted"); 956 notifyConnectCompleted(mConnectionParams, mFailCause); 957 } 958 } 959 960 @Override 961 public void exit() { 962 // clear notifications 963 mConnectionParams = null; 964 mFailCause = null; 965 } 966 967 @Override 968 public boolean processMessage(Message msg) { 969 boolean retVal; 970 971 switch (msg.what) { 972 case EVENT_CONNECT: 973 mRefCount++; 974 if (DBG) log("DcActiveState msg.what=EVENT_CONNECT RefCount=" + mRefCount); 975 if (msg.obj != null) { 976 notifyConnectCompleted((ConnectionParams) msg.obj, FailCause.NONE); 977 } 978 retVal = HANDLED; 979 break; 980 case EVENT_DISCONNECT: 981 mRefCount--; 982 if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT RefCount=" + mRefCount); 983 if (mRefCount == 0) 984 { 985 DisconnectParams dp = (DisconnectParams) msg.obj; 986 dp.tag = mTag; 987 tearDownData(dp); 988 transitionTo(mDisconnectingState); 989 } else { 990 if (msg.obj != null) { 991 notifyDisconnectCompleted((DisconnectParams) msg.obj); 992 } 993 } 994 retVal = HANDLED; 995 break; 996 997 default: 998 if (VDBG) { 999 log("DcActiveState not handled msg.what=0x" + 1000 Integer.toHexString(msg.what)); 1001 } 1002 retVal = NOT_HANDLED; 1003 break; 1004 } 1005 return retVal; 1006 } 1007 } 1008 private DcActiveState mActiveState = new DcActiveState(); 1009 1010 /** 1011 * The state machine is disconnecting. 1012 */ 1013 private class DcDisconnectingState extends State { 1014 @Override 1015 public boolean processMessage(Message msg) { 1016 boolean retVal; 1017 1018 switch (msg.what) { 1019 case EVENT_CONNECT: 1020 if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = " 1021 + mRefCount); 1022 deferMessage(msg); 1023 retVal = HANDLED; 1024 break; 1025 1026 case EVENT_DEACTIVATE_DONE: 1027 if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE"); 1028 AsyncResult ar = (AsyncResult) msg.obj; 1029 DisconnectParams dp = (DisconnectParams) ar.userObj; 1030 if (dp.tag == mTag) { 1031 // Transition to inactive but send notifications after 1032 // we've entered the mInactive state. 1033 mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj); 1034 transitionTo(mInactiveState); 1035 } else { 1036 if (DBG) log("DcDisconnectState EVENT_DEACTIVATE_DONE stale dp.tag=" 1037 + dp.tag + " mTag=" + mTag); 1038 } 1039 retVal = HANDLED; 1040 break; 1041 1042 default: 1043 if (VDBG) { 1044 log("DcDisconnectingState not handled msg.what=0x" + 1045 Integer.toHexString(msg.what)); 1046 } 1047 retVal = NOT_HANDLED; 1048 break; 1049 } 1050 return retVal; 1051 } 1052 } 1053 private DcDisconnectingState mDisconnectingState = new DcDisconnectingState(); 1054 1055 /** 1056 * The state machine is disconnecting after an creating a connection. 1057 */ 1058 private class DcDisconnectionErrorCreatingConnection extends State { 1059 @Override 1060 public boolean processMessage(Message msg) { 1061 boolean retVal; 1062 1063 switch (msg.what) { 1064 case EVENT_DEACTIVATE_DONE: 1065 AsyncResult ar = (AsyncResult) msg.obj; 1066 ConnectionParams cp = (ConnectionParams) ar.userObj; 1067 if (cp.tag == mTag) { 1068 if (DBG) { 1069 log("DcDisconnectionErrorCreatingConnection" + 1070 " msg.what=EVENT_DEACTIVATE_DONE"); 1071 } 1072 1073 // Transition to inactive but send notifications after 1074 // we've entered the mInactive state. 1075 mInactiveState.setEnterNotificationParams(cp, 1076 FailCause.UNACCEPTABLE_NETWORK_PARAMETER, -1); 1077 transitionTo(mInactiveState); 1078 } else { 1079 if (DBG) { 1080 log("DcDisconnectionErrorCreatingConnection EVENT_DEACTIVATE_DONE" + 1081 " stale dp.tag=" + cp.tag + ", mTag=" + mTag); 1082 } 1083 } 1084 retVal = HANDLED; 1085 break; 1086 1087 default: 1088 if (VDBG) { 1089 log("DcDisconnectionErrorCreatingConnection not handled msg.what=0x" 1090 + Integer.toHexString(msg.what)); 1091 } 1092 retVal = NOT_HANDLED; 1093 break; 1094 } 1095 return retVal; 1096 } 1097 } 1098 private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection = 1099 new DcDisconnectionErrorCreatingConnection(); 1100 1101 // ******* public interface 1102 1103 /** 1104 * Bring up a connection to the apn and return an AsyncResult in onCompletedMsg. 1105 * Used for cellular networks that use Acesss Point Names (APN) such 1106 * as GSM networks. 1107 * 1108 * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. 1109 * With AsyncResult.userObj set to the original msg.obj, 1110 * AsyncResult.result = FailCause and AsyncResult.exception = Exception(). 1111 * @param apn is the Access Point Name to bring up a connection to 1112 */ 1113 public void bringUp(Message onCompletedMsg, ApnSetting apn) { 1114 sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(apn, onCompletedMsg))); 1115 } 1116 1117 /** 1118 * Tear down the connection through the apn on the network. 1119 * 1120 * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object. 1121 * With AsyncResult.userObj set to the original msg.obj. 1122 */ 1123 public void tearDown(String reason, Message onCompletedMsg) { 1124 sendMessage(obtainMessage(EVENT_DISCONNECT, new DisconnectParams(reason, onCompletedMsg))); 1125 } 1126 } 1127