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