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