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