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