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