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.cdma; 18 19 import android.app.AlarmManager; 20 import android.app.PendingIntent; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.net.TrafficStats; 24 import android.os.AsyncResult; 25 import android.os.Message; 26 import android.os.SystemClock; 27 import android.os.SystemProperties; 28 import android.telephony.ServiceState; 29 import android.telephony.TelephonyManager; 30 import android.telephony.cdma.CdmaCellLocation; 31 import android.text.TextUtils; 32 import android.util.EventLog; 33 import android.util.Log; 34 35 import com.android.internal.telephony.ApnSetting; 36 import com.android.internal.telephony.CommandsInterface; 37 import com.android.internal.telephony.DataCallState; 38 import com.android.internal.telephony.DataConnection.FailCause; 39 import com.android.internal.telephony.DataConnection; 40 import com.android.internal.telephony.DataConnectionAc; 41 import com.android.internal.telephony.DataConnectionTracker; 42 import com.android.internal.telephony.EventLogTags; 43 import com.android.internal.telephony.RetryManager; 44 import com.android.internal.telephony.Phone; 45 import com.android.internal.util.AsyncChannel; 46 47 import java.util.ArrayList; 48 49 /** 50 * {@hide} 51 */ 52 public final class CdmaDataConnectionTracker extends DataConnectionTracker { 53 protected final String LOG_TAG = "CDMA"; 54 55 private CDMAPhone mCdmaPhone; 56 57 /** The DataConnection being setup */ 58 private CdmaDataConnection mPendingDataConnection; 59 60 private boolean mPendingRestartRadio = false; 61 private static final int TIME_DELAYED_TO_RESTART_RADIO = 62 SystemProperties.getInt("ro.cdma.timetoradiorestart", 60000); 63 64 /** 65 * Pool size of CdmaDataConnection objects. 66 */ 67 private static final int DATA_CONNECTION_POOL_SIZE = 1; 68 69 private static final String INTENT_RECONNECT_ALARM = 70 "com.android.internal.telephony.cdma-reconnect"; 71 72 /** 73 * Constants for the data connection activity: 74 * physical link down/up 75 */ 76 private static final int DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE = 0; 77 private static final int DATA_CONNECTION_ACTIVE_PH_LINK_DOWN = 1; 78 private static final int DATA_CONNECTION_ACTIVE_PH_LINK_UP = 2; 79 80 private static final String[] mSupportedApnTypes = { 81 Phone.APN_TYPE_DEFAULT, 82 Phone.APN_TYPE_MMS, 83 Phone.APN_TYPE_DUN, 84 Phone.APN_TYPE_HIPRI }; 85 86 private static final String[] mDefaultApnTypes = { 87 Phone.APN_TYPE_DEFAULT, 88 Phone.APN_TYPE_MMS, 89 Phone.APN_TYPE_HIPRI }; 90 91 private static final int mDefaultApnId = DataConnectionTracker.APN_DEFAULT_ID; 92 93 /* Constructor */ 94 95 CdmaDataConnectionTracker(CDMAPhone p) { 96 super(p); 97 mCdmaPhone = p; 98 99 p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null); 100 p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); 101 p.mIccRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null); 102 p.mCM.registerForNVReady(this, EVENT_NV_READY, null); 103 p.mCM.registerForDataNetworkStateChanged (this, EVENT_DATA_STATE_CHANGED, null); 104 p.mCT.registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null); 105 p.mCT.registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null); 106 p.mSST.registerForDataConnectionAttached(this, EVENT_TRY_SETUP_DATA, null); 107 p.mSST.registerForDataConnectionDetached(this, EVENT_CDMA_DATA_DETACHED, null); 108 p.mSST.registerForRoamingOn(this, EVENT_ROAMING_ON, null); 109 p.mSST.registerForRoamingOff(this, EVENT_ROAMING_OFF, null); 110 p.mCM.registerForCdmaOtaProvision(this, EVENT_CDMA_OTA_PROVISION, null); 111 112 mDataConnectionTracker = this; 113 114 createAllDataConnectionList(); 115 broadcastMessenger(); 116 } 117 118 @Override 119 public void dispose() { 120 cleanUpConnection(false, null); 121 122 super.dispose(); 123 124 // Unregister from all events 125 mPhone.mCM.unregisterForAvailable(this); 126 mPhone.mCM.unregisterForOffOrNotAvailable(this); 127 mCdmaPhone.mIccRecords.unregisterForRecordsLoaded(this); 128 mPhone.mCM.unregisterForNVReady(this); 129 mPhone.mCM.unregisterForDataNetworkStateChanged(this); 130 mCdmaPhone.mCT.unregisterForVoiceCallEnded(this); 131 mCdmaPhone.mCT.unregisterForVoiceCallStarted(this); 132 mCdmaPhone.mSST.unregisterForDataConnectionAttached(this); 133 mCdmaPhone.mSST.unregisterForDataConnectionDetached(this); 134 mCdmaPhone.mSST.unregisterForRoamingOn(this); 135 mCdmaPhone.mSST.unregisterForRoamingOff(this); 136 mPhone.mCM.unregisterForCdmaOtaProvision(this); 137 138 destroyAllDataConnectionList(); 139 } 140 141 @Override 142 protected void finalize() { 143 if(DBG) log("CdmaDataConnectionTracker finalized"); 144 } 145 146 @Override 147 protected String getActionIntentReconnectAlarm() { 148 return INTENT_RECONNECT_ALARM; 149 } 150 151 @Override 152 protected void setState(State s) { 153 if (DBG) log ("setState: " + s); 154 if (mState != s) { 155 EventLog.writeEvent(EventLogTags.CDMA_DATA_STATE_CHANGE, 156 mState.toString(), s.toString()); 157 mState = s; 158 } 159 } 160 161 @Override 162 public synchronized State getState(String apnType) { 163 return mState; 164 } 165 166 @Override 167 protected boolean isApnTypeAvailable(String type) { 168 for (String s : mSupportedApnTypes) { 169 if (TextUtils.equals(type, s)) { 170 return true; 171 } 172 } 173 return false; 174 } 175 176 @Override 177 protected boolean isDataAllowed() { 178 final boolean internalDataEnabled; 179 synchronized (mDataEnabledLock) { 180 internalDataEnabled = mInternalDataEnabled; 181 } 182 183 int psState = mCdmaPhone.mSST.getCurrentDataConnectionState(); 184 boolean roaming = (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled()); 185 boolean desiredPowerState = mCdmaPhone.mSST.getDesiredPowerState(); 186 187 boolean allowed = 188 (psState == ServiceState.STATE_IN_SERVICE || 189 mAutoAttachOnCreation) && 190 (mPhone.mCM.getNvState() == CommandsInterface.RadioState.NV_READY || 191 mCdmaPhone.mIccRecords.getRecordsLoaded()) && 192 (mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed() || 193 mPhone.getState() == Phone.State.IDLE) && 194 !roaming && 195 internalDataEnabled && 196 desiredPowerState && 197 !mPendingRestartRadio && 198 !mCdmaPhone.needsOtaServiceProvisioning(); 199 if (!allowed && DBG) { 200 String reason = ""; 201 if (!((psState == ServiceState.STATE_IN_SERVICE) || mAutoAttachOnCreation)) { 202 reason += " - psState= " + psState; 203 } 204 if (!(mPhone.mCM.getNvState() == CommandsInterface.RadioState.NV_READY || 205 mCdmaPhone.mIccRecords.getRecordsLoaded())) { 206 reason += " - radioState= " + mPhone.mCM.getNvState() + " - RUIM not loaded"; 207 } 208 if (!(mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed() || 209 mPhone.getState() == Phone.State.IDLE)) { 210 reason += " - concurrentVoiceAndData not allowed and state= " + mPhone.getState(); 211 } 212 if (roaming) reason += " - Roaming"; 213 if (!internalDataEnabled) reason += " - mInternalDataEnabled= false"; 214 if (!desiredPowerState) reason += " - desiredPowerState= false"; 215 if (mPendingRestartRadio) reason += " - mPendingRestartRadio= true"; 216 if (mCdmaPhone.needsOtaServiceProvisioning()) reason += " - needs Provisioning"; 217 log("Data not allowed due to" + reason); 218 } 219 return allowed; 220 } 221 222 @Override 223 protected boolean isDataPossible(String apnType) { 224 boolean possible = isDataAllowed() && !(getAnyDataEnabled() && 225 (mState == State.FAILED || mState == State.IDLE)); 226 if (!possible && DBG && isDataAllowed()) { 227 log("Data not possible. No coverage: dataState = " + mState); 228 } 229 return possible; 230 } 231 232 private boolean trySetupData(String reason) { 233 if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason)); 234 235 if (mPhone.getSimulatedRadioControl() != null) { 236 // Assume data is connected on the simulator 237 // FIXME this can be improved 238 setState(State.CONNECTED); 239 notifyDataConnection(reason); 240 notifyOffApnsOfAvailability(reason); 241 242 log("(fix?) We're on the simulator; assuming data is connected"); 243 return true; 244 } 245 246 int psState = mCdmaPhone.mSST.getCurrentDataConnectionState(); 247 boolean roaming = mPhone.getServiceState().getRoaming(); 248 boolean desiredPowerState = mCdmaPhone.mSST.getDesiredPowerState(); 249 250 if ((mState == State.IDLE || mState == State.SCANNING) && 251 isDataAllowed() && getAnyDataEnabled() && !isEmergency()) { 252 boolean retValue = setupData(reason); 253 notifyOffApnsOfAvailability(reason); 254 return retValue; 255 } else { 256 notifyOffApnsOfAvailability(reason); 257 return false; 258 } 259 } 260 261 /** 262 * Cleanup the CDMA data connection (only one is supported) 263 * 264 * @param tearDown true if the underlying DataConnection should be disconnected. 265 * @param reason for the clean up. 266 */ 267 private void cleanUpConnection(boolean tearDown, String reason) { 268 if (DBG) log("cleanUpConnection: reason: " + reason); 269 270 // Clear the reconnect alarm, if set. 271 if (mReconnectIntent != null) { 272 AlarmManager am = 273 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE); 274 am.cancel(mReconnectIntent); 275 mReconnectIntent = null; 276 } 277 278 setState(State.DISCONNECTING); 279 notifyOffApnsOfAvailability(reason); 280 281 boolean notificationDeferred = false; 282 for (DataConnection conn : mDataConnections.values()) { 283 if(conn != null) { 284 DataConnectionAc dcac = 285 mDataConnectionAsyncChannels.get(conn.getDataConnectionId()); 286 if (tearDown) { 287 if (DBG) log("cleanUpConnection: teardown, call conn.disconnect"); 288 conn.tearDown(reason, obtainMessage(EVENT_DISCONNECT_DONE, 289 conn.getDataConnectionId(), 0, reason)); 290 notificationDeferred = true; 291 } else { 292 if (DBG) log("cleanUpConnection: !tearDown, call conn.resetSynchronously"); 293 if (dcac != null) { 294 dcac.resetSync(); 295 } 296 notificationDeferred = false; 297 } 298 } 299 } 300 301 stopNetStatPoll(); 302 303 if (!notificationDeferred) { 304 if (DBG) log("cleanupConnection: !notificationDeferred"); 305 gotoIdleAndNotifyDataConnection(reason); 306 } 307 } 308 309 private CdmaDataConnection findFreeDataConnection() { 310 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) { 311 if (dcac.isInactiveSync()) { 312 log("found free GsmDataConnection"); 313 return (CdmaDataConnection) dcac.dataConnection; 314 } 315 } 316 log("NO free CdmaDataConnection"); 317 return null; 318 } 319 320 private boolean setupData(String reason) { 321 CdmaDataConnection conn = findFreeDataConnection(); 322 323 if (conn == null) { 324 if (DBG) log("setupData: No free CdmaDataConnection found!"); 325 return false; 326 } 327 328 /** TODO: We probably want the connection being setup to a parameter passed around */ 329 mPendingDataConnection = conn; 330 String[] types; 331 int apnId; 332 if (mRequestedApnType.equals(Phone.APN_TYPE_DUN)) { 333 types = new String[1]; 334 types[0] = Phone.APN_TYPE_DUN; 335 apnId = DataConnectionTracker.APN_DUN_ID; 336 } else { 337 types = mDefaultApnTypes; 338 apnId = mDefaultApnId; 339 } 340 mActiveApn = new ApnSetting(apnId, "", "", "", "", "", "", "", "", "", 341 "", 0, types, "IP", "IP", true, 0); 342 if (DBG) log("call conn.bringUp mActiveApn=" + mActiveApn); 343 344 Message msg = obtainMessage(); 345 msg.what = EVENT_DATA_SETUP_COMPLETE; 346 msg.obj = reason; 347 conn.bringUp(msg, mActiveApn); 348 349 setState(State.INITING); 350 notifyDataConnection(reason); 351 return true; 352 } 353 354 private void notifyDefaultData(String reason) { 355 setState(State.CONNECTED); 356 notifyDataConnection(reason); 357 startNetStatPoll(); 358 mDataConnections.get(0).resetRetryCount(); 359 } 360 361 private void resetPollStats() { 362 mTxPkts = -1; 363 mRxPkts = -1; 364 mSentSinceLastRecv = 0; 365 mNetStatPollPeriod = POLL_NETSTAT_MILLIS; 366 mNoRecvPollCount = 0; 367 } 368 369 @Override 370 protected void startNetStatPoll() { 371 if (mState == State.CONNECTED && mNetStatPollEnabled == false) { 372 log("[DataConnection] Start poll NetStat"); 373 resetPollStats(); 374 mNetStatPollEnabled = true; 375 mPollNetStat.run(); 376 } 377 } 378 379 @Override 380 protected void stopNetStatPoll() { 381 mNetStatPollEnabled = false; 382 removeCallbacks(mPollNetStat); 383 log("[DataConnection] Stop poll NetStat"); 384 } 385 386 @Override 387 protected void restartRadio() { 388 if (DBG) log("Cleanup connection and wait " + 389 (TIME_DELAYED_TO_RESTART_RADIO / 1000) + "s to restart radio"); 390 cleanUpAllConnections(null); 391 sendEmptyMessageDelayed(EVENT_RESTART_RADIO, TIME_DELAYED_TO_RESTART_RADIO); 392 mPendingRestartRadio = true; 393 } 394 395 private Runnable mPollNetStat = new Runnable() { 396 397 public void run() { 398 long sent, received; 399 long preTxPkts = -1, preRxPkts = -1; 400 401 Activity newActivity; 402 403 preTxPkts = mTxPkts; 404 preRxPkts = mRxPkts; 405 406 mTxPkts = TrafficStats.getMobileTxPackets(); 407 mRxPkts = TrafficStats.getMobileRxPackets(); 408 409 //log("rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts)); 410 411 if (mNetStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) { 412 sent = mTxPkts - preTxPkts; 413 received = mRxPkts - preRxPkts; 414 415 if ( sent > 0 && received > 0 ) { 416 mSentSinceLastRecv = 0; 417 newActivity = Activity.DATAINANDOUT; 418 } else if (sent > 0 && received == 0) { 419 if (mPhone.getState() == Phone.State.IDLE) { 420 mSentSinceLastRecv += sent; 421 } else { 422 mSentSinceLastRecv = 0; 423 } 424 newActivity = Activity.DATAOUT; 425 } else if (sent == 0 && received > 0) { 426 mSentSinceLastRecv = 0; 427 newActivity = Activity.DATAIN; 428 } else if (sent == 0 && received == 0) { 429 newActivity = (mActivity == Activity.DORMANT) ? mActivity : Activity.NONE; 430 } else { 431 mSentSinceLastRecv = 0; 432 newActivity = (mActivity == Activity.DORMANT) ? mActivity : Activity.NONE; 433 } 434 435 if (mActivity != newActivity && mIsScreenOn) { 436 mActivity = newActivity; 437 mPhone.notifyDataActivity(); 438 } 439 } 440 441 if (mSentSinceLastRecv >= NUMBER_SENT_PACKETS_OF_HANG) { 442 // Packets sent without ack exceeded threshold. 443 444 if (mNoRecvPollCount == 0) { 445 EventLog.writeEvent( 446 EventLogTags.PDP_RADIO_RESET_COUNTDOWN_TRIGGERED, 447 mSentSinceLastRecv); 448 } 449 450 if (mNoRecvPollCount < NO_RECV_POLL_LIMIT) { 451 mNoRecvPollCount++; 452 // Slow down the poll interval to let things happen 453 mNetStatPollPeriod = POLL_NETSTAT_SLOW_MILLIS; 454 } else { 455 if (DBG) log("Sent " + String.valueOf(mSentSinceLastRecv) + 456 " pkts since last received"); 457 // We've exceeded the threshold. Restart the radio. 458 mNetStatPollEnabled = false; 459 stopNetStatPoll(); 460 restartRadio(); 461 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, NO_RECV_POLL_LIMIT); 462 } 463 } else { 464 mNoRecvPollCount = 0; 465 mNetStatPollPeriod = POLL_NETSTAT_MILLIS; 466 } 467 468 if (mNetStatPollEnabled) { 469 mDataConnectionTracker.postDelayed(this, mNetStatPollPeriod); 470 } 471 } 472 }; 473 474 /** 475 * Returns true if the last fail cause is something that 476 * seems like it deserves an error notification. 477 * Transient errors are ignored 478 */ 479 private boolean 480 shouldPostNotification(FailCause cause) { 481 return (cause != FailCause.UNKNOWN); 482 } 483 484 /** 485 * Return true if data connection need to be setup after disconnected due to 486 * reason. 487 * 488 * @param reason the reason why data is disconnected 489 * @return true if try setup data connection is need for this reason 490 */ 491 private boolean retryAfterDisconnected(String reason) { 492 boolean retry = true; 493 494 if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) { 495 retry = false; 496 } 497 return retry; 498 } 499 500 private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) { 501 if (mState == State.FAILED) { 502 /** 503 * For now With CDMA we never try to reconnect on 504 * error and instead just continue to retry 505 * at the last time until the state is changed. 506 * TODO: Make this configurable? 507 */ 508 int nextReconnectDelay = mDataConnections.get(0).getRetryTimer(); 509 startAlarmForReconnect(nextReconnectDelay, reason); 510 mDataConnections.get(0).increaseRetryCount(); 511 512 if (!shouldPostNotification(lastFailCauseCode)) { 513 log("NOT Posting Data Connection Unavailable notification " 514 + "-- likely transient error"); 515 } else { 516 notifyNoData(lastFailCauseCode); 517 } 518 } 519 } 520 521 private void startAlarmForReconnect(int delay, String reason) { 522 523 log("Data Connection activate failed. Scheduling next attempt for " 524 + (delay / 1000) + "s"); 525 526 AlarmManager am = 527 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE); 528 Intent intent = new Intent(INTENT_RECONNECT_ALARM); 529 intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason); 530 mReconnectIntent = PendingIntent.getBroadcast( 531 mPhone.getContext(), 0, intent, 0); 532 am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 533 SystemClock.elapsedRealtime() + delay, mReconnectIntent); 534 535 } 536 537 private void notifyNoData(FailCause lastFailCauseCode) { 538 setState(State.FAILED); 539 notifyOffApnsOfAvailability(null); 540 } 541 542 protected void gotoIdleAndNotifyDataConnection(String reason) { 543 if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason); 544 setState(State.IDLE); 545 notifyDataConnection(reason); 546 mActiveApn = null; 547 } 548 549 protected void onRecordsLoaded() { 550 if (mState == State.FAILED) { 551 cleanUpAllConnections(null); 552 } 553 sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED)); 554 } 555 556 protected void onNVReady() { 557 if (mState == State.FAILED) { 558 cleanUpAllConnections(null); 559 } 560 sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); 561 } 562 563 /** 564 * @override com.android.internal.telephony.DataConnectionTracker 565 */ 566 @Override 567 protected void onEnableNewApn() { 568 // No mRequestedApnType check; only one connection is supported 569 cleanUpConnection(true, Phone.REASON_APN_SWITCHED); 570 } 571 572 /** 573 * @override com.android.internal.telephony.DataConnectionTracker 574 */ 575 @Override 576 protected boolean onTrySetupData(String reason) { 577 return trySetupData(reason); 578 } 579 580 /** 581 * @override com.android.internal.telephony.DataConnectionTracker 582 */ 583 @Override 584 protected void onRoamingOff() { 585 if (getDataOnRoamingEnabled() == false) { 586 notifyOffApnsOfAvailability(Phone.REASON_ROAMING_OFF); 587 trySetupData(Phone.REASON_ROAMING_OFF); 588 } else { 589 notifyDataConnection(Phone.REASON_ROAMING_OFF); 590 } 591 } 592 593 /** 594 * @override com.android.internal.telephony.DataConnectionTracker 595 */ 596 @Override 597 protected void onRoamingOn() { 598 if (getDataOnRoamingEnabled()) { 599 trySetupData(Phone.REASON_ROAMING_ON); 600 notifyDataConnection(Phone.REASON_ROAMING_ON); 601 } else { 602 if (DBG) log("Tear down data connection on roaming."); 603 cleanUpAllConnections(null); 604 notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON); 605 } 606 } 607 608 /** 609 * @override com.android.internal.telephony.DataConnectionTracker 610 */ 611 @Override 612 protected void onRadioAvailable() { 613 if (mPhone.getSimulatedRadioControl() != null) { 614 // Assume data is connected on the simulator 615 // FIXME this can be improved 616 setState(State.CONNECTED); 617 notifyDataConnection(null); 618 619 log("We're on the simulator; assuming data is connected"); 620 } 621 622 notifyOffApnsOfAvailability(null); 623 624 if (mState != State.IDLE) { 625 cleanUpAllConnections(null); 626 } 627 } 628 629 /** 630 * @override com.android.internal.telephony.DataConnectionTracker 631 */ 632 @Override 633 protected void onRadioOffOrNotAvailable() { 634 mDataConnections.get(0).resetRetryCount(); 635 636 if (mPhone.getSimulatedRadioControl() != null) { 637 // Assume data is connected on the simulator 638 // FIXME this can be improved 639 log("We're on the simulator; assuming radio off is meaningless"); 640 } else { 641 if (DBG) log("Radio is off and clean up all connection"); 642 cleanUpAllConnections(null); 643 } 644 } 645 646 /** 647 * @override com.android.internal.telephony.DataConnectionTracker 648 */ 649 @Override 650 protected void onDataSetupComplete(AsyncResult ar) { 651 String reason = null; 652 if (ar.userObj instanceof String) { 653 reason = (String) ar.userObj; 654 } 655 656 if (isDataSetupCompleteOk(ar)) { 657 // Everything is setup 658 notifyDefaultData(reason); 659 } else { 660 FailCause cause = (FailCause) (ar.result); 661 if(DBG) log("Data Connection setup failed " + cause); 662 663 // No try for permanent failure 664 if (cause.isPermanentFail()) { 665 notifyNoData(cause); 666 return; 667 } 668 startDelayedRetry(cause, reason); 669 } 670 } 671 672 /** 673 * Called when EVENT_DISCONNECT_DONE is received. 674 */ 675 @Override 676 protected void onDisconnectDone(int connId, AsyncResult ar) { 677 if(DBG) log("EVENT_DISCONNECT_DONE connId=" + connId); 678 String reason = null; 679 if (ar.userObj instanceof String) { 680 reason = (String) ar.userObj; 681 } 682 setState(State.IDLE); 683 684 // Since the pending request to turn off or restart radio will be processed here, 685 // remove the pending event to restart radio from the message queue. 686 if (mPendingRestartRadio) removeMessages(EVENT_RESTART_RADIO); 687 688 // Process the pending request to turn off radio in ServiceStateTracker first. 689 // If radio is turned off in ServiceStateTracker, ignore the pending event to restart radio. 690 CdmaServiceStateTracker ssTracker = mCdmaPhone.mSST; 691 if (ssTracker.processPendingRadioPowerOffAfterDataOff()) { 692 mPendingRestartRadio = false; 693 } else { 694 onRestartRadio(); 695 } 696 697 notifyDataConnection(reason); 698 mActiveApn = null; 699 if (retryAfterDisconnected(reason)) { 700 // Wait a bit before trying, so we're not tying up RIL command channel. 701 startAlarmForReconnect(APN_DELAY_MILLIS, reason); 702 } 703 } 704 705 /** 706 * @override com.android.internal.telephony.DataConnectionTracker 707 */ 708 @Override 709 protected void onVoiceCallStarted() { 710 if (mState == State.CONNECTED && !mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed()) { 711 stopNetStatPoll(); 712 notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED); 713 notifyOffApnsOfAvailability(Phone.REASON_VOICE_CALL_STARTED); 714 } 715 } 716 717 /** 718 * @override com.android.internal.telephony.DataConnectionTracker 719 */ 720 @Override 721 protected void onVoiceCallEnded() { 722 if (mState == State.CONNECTED) { 723 if (!mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed()) { 724 startNetStatPoll(); 725 notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED); 726 } else { 727 // clean slate after call end. 728 resetPollStats(); 729 } 730 notifyOffApnsOfAvailability(Phone.REASON_VOICE_CALL_ENDED); 731 } else { 732 mDataConnections.get(0).resetRetryCount(); 733 // in case data setup was attempted when we were on a voice call 734 trySetupData(Phone.REASON_VOICE_CALL_ENDED); 735 } 736 } 737 738 @Override 739 protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) { 740 // No apnId check; only one connection is supported 741 cleanUpConnection(tearDown, reason); 742 } 743 744 @Override 745 protected void onCleanUpAllConnections(String cause) { 746 // Only one CDMA connection is supported 747 cleanUpConnection(true, cause); 748 } 749 750 private void createAllDataConnectionList() { 751 CdmaDataConnection dataConn; 752 753 String retryConfig = SystemProperties.get("ro.cdma.data_retry_config"); 754 for (int i = 0; i < DATA_CONNECTION_POOL_SIZE; i++) { 755 RetryManager rm = new RetryManager(); 756 if (!rm.configure(retryConfig)) { 757 if (!rm.configure(DEFAULT_DATA_RETRY_CONFIG)) { 758 // Should never happen, log an error and default to a simple linear sequence. 759 log("Could not configure using DEFAULT_DATA_RETRY_CONFIG=" 760 + DEFAULT_DATA_RETRY_CONFIG); 761 rm.configure(20, 2000, 1000); 762 } 763 } 764 765 int id = mUniqueIdGenerator.getAndIncrement(); 766 dataConn = CdmaDataConnection.makeDataConnection(mCdmaPhone, id, rm); 767 mDataConnections.put(id, dataConn); 768 DataConnectionAc dcac = new DataConnectionAc(dataConn, LOG_TAG); 769 int status = dcac.fullyConnectSync(mPhone.getContext(), this, dataConn.getHandler()); 770 if (status == AsyncChannel.STATUS_SUCCESSFUL) { 771 log("Fully connected"); 772 mDataConnectionAsyncChannels.put(dcac.dataConnection.getDataConnectionId(), dcac); 773 } else { 774 log("Could not connect to dcac.dataConnection=" + dcac.dataConnection + 775 " status=" + status); 776 } 777 778 } 779 } 780 781 private void destroyAllDataConnectionList() { 782 if(mDataConnections != null) { 783 mDataConnections.clear(); 784 } 785 } 786 787 private void onCdmaDataDetached() { 788 if (mState == State.CONNECTED) { 789 startNetStatPoll(); 790 notifyDataConnection(Phone.REASON_CDMA_DATA_DETACHED); 791 } else { 792 if (mState == State.FAILED) { 793 cleanUpConnection(false, Phone.REASON_CDMA_DATA_DETACHED); 794 mDataConnections.get(0).resetRetryCount(); 795 796 CdmaCellLocation loc = (CdmaCellLocation)(mPhone.getCellLocation()); 797 EventLog.writeEvent(EventLogTags.CDMA_DATA_SETUP_FAILED, 798 loc != null ? loc.getBaseStationId() : -1, 799 TelephonyManager.getDefault().getNetworkType()); 800 } 801 trySetupData(Phone.REASON_CDMA_DATA_DETACHED); 802 } 803 } 804 805 private void onCdmaOtaProvision(AsyncResult ar) { 806 if (ar.exception != null) { 807 int [] otaPrivision = (int [])ar.result; 808 if ((otaPrivision != null) && (otaPrivision.length > 1)) { 809 switch (otaPrivision[0]) { 810 case Phone.CDMA_OTA_PROVISION_STATUS_COMMITTED: 811 case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED: 812 mDataConnections.get(0).resetRetryCount(); 813 break; 814 default: 815 break; 816 } 817 } 818 } 819 } 820 821 private void onRestartRadio() { 822 if (mPendingRestartRadio) { 823 log("************TURN OFF RADIO**************"); 824 mPhone.mCM.setRadioPower(false, null); 825 /* Note: no need to call setRadioPower(true). Assuming the desired 826 * radio power state is still ON (as tracked by ServiceStateTracker), 827 * ServiceStateTracker will call setRadioPower when it receives the 828 * RADIO_STATE_CHANGED notification for the power off. And if the 829 * desired power state has changed in the interim, we don't want to 830 * override it with an unconditional power on. 831 */ 832 mPendingRestartRadio = false; 833 } 834 } 835 836 private void writeEventLogCdmaDataDrop() { 837 CdmaCellLocation loc = (CdmaCellLocation)(mPhone.getCellLocation()); 838 EventLog.writeEvent(EventLogTags.CDMA_DATA_DROP, 839 loc != null ? loc.getBaseStationId() : -1, 840 TelephonyManager.getDefault().getNetworkType()); 841 } 842 843 protected void onDataStateChanged(AsyncResult ar) { 844 ArrayList<DataCallState> dataCallStates = (ArrayList<DataCallState>)(ar.result); 845 846 if (ar.exception != null) { 847 // This is probably "radio not available" or something 848 // of that sort. If so, the whole connection is going 849 // to come down soon anyway 850 return; 851 } 852 853 if (mState == State.CONNECTED) { 854 boolean isActiveOrDormantConnectionPresent = false; 855 int connectionState = DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE; 856 857 // Check for an active or dormant connection element in 858 // the DATA_CALL_LIST array 859 for (int index = 0; index < dataCallStates.size(); index++) { 860 connectionState = dataCallStates.get(index).active; 861 if (connectionState != DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE) { 862 isActiveOrDormantConnectionPresent = true; 863 break; 864 } 865 } 866 867 if (!isActiveOrDormantConnectionPresent) { 868 // No active or dormant connection 869 log("onDataStateChanged: No active connection" 870 + "state is CONNECTED, disconnecting/cleanup"); 871 writeEventLogCdmaDataDrop(); 872 cleanUpConnection(true, null); 873 return; 874 } 875 876 switch (connectionState) { 877 case DATA_CONNECTION_ACTIVE_PH_LINK_UP: 878 log("onDataStateChanged: active=LINK_ACTIVE && CONNECTED, ignore"); 879 mActivity = Activity.NONE; 880 mPhone.notifyDataActivity(); 881 startNetStatPoll(); 882 break; 883 884 case DATA_CONNECTION_ACTIVE_PH_LINK_DOWN: 885 log("onDataStateChanged active=LINK_DOWN && CONNECTED, dormant"); 886 mActivity = Activity.DORMANT; 887 mPhone.notifyDataActivity(); 888 stopNetStatPoll(); 889 break; 890 891 default: 892 log("onDataStateChanged: IGNORE unexpected DataCallState.active=" 893 + connectionState); 894 } 895 } else { 896 // TODO: Do we need to do anything? 897 log("onDataStateChanged: not connected, state=" + mState + " ignoring"); 898 } 899 } 900 901 private void startDelayedRetry(FailCause cause, String reason) { 902 notifyNoData(cause); 903 reconnectAfterFail(cause, reason); 904 } 905 906 @Override 907 public void handleMessage (Message msg) { 908 if (DBG) log("CdmaDCT handleMessage msg=" + msg); 909 910 if (!mPhone.mIsTheCurrentActivePhone || mIsDisposed) { 911 log("Ignore CDMA msgs since CDMA phone is inactive"); 912 return; 913 } 914 915 switch (msg.what) { 916 case EVENT_RECORDS_LOADED: 917 onRecordsLoaded(); 918 break; 919 920 case EVENT_NV_READY: 921 onNVReady(); 922 break; 923 924 case EVENT_CDMA_DATA_DETACHED: 925 onCdmaDataDetached(); 926 break; 927 928 case EVENT_DATA_STATE_CHANGED: 929 onDataStateChanged((AsyncResult) msg.obj); 930 break; 931 932 case EVENT_CDMA_OTA_PROVISION: 933 onCdmaOtaProvision((AsyncResult) msg.obj); 934 break; 935 936 case EVENT_RESTART_RADIO: 937 if (DBG) log("EVENT_RESTART_RADIO"); 938 onRestartRadio(); 939 break; 940 941 default: 942 // handle the message in the super class DataConnectionTracker 943 super.handleMessage(msg); 944 break; 945 } 946 } 947 948 @Override 949 public boolean isDisconnected() { 950 return ((mState == State.IDLE) || (mState == State.FAILED)); 951 } 952 953 @Override 954 protected void log(String s) { 955 Log.d(LOG_TAG, "[CdmaDCT] " + s); 956 } 957 958 @Override 959 protected void loge(String s) { 960 Log.e(LOG_TAG, "[CdmaDCT] " + s); 961 } 962 } 963