GsmDataConnectionTracker.java revision d7d6fb39470e212ce53b05fdc1c4dd8a724e9db7
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.gsm; 18 19import android.app.AlarmManager; 20import android.app.PendingIntent; 21import android.content.BroadcastReceiver; 22import android.content.ContentResolver; 23import android.content.ContentValues; 24import android.content.Context; 25import android.content.Intent; 26import android.content.IntentFilter; 27import android.content.SharedPreferences; 28import android.database.ContentObserver; 29import android.database.Cursor; 30import android.net.NetworkInfo; 31import android.net.Uri; 32import android.net.wifi.WifiManager; 33import android.os.AsyncResult; 34import android.os.INetStatService; 35import android.os.Message; 36import android.os.RemoteException; 37import android.os.ServiceManager; 38import android.os.SystemClock; 39import android.os.SystemProperties; 40import android.preference.PreferenceManager; 41import android.provider.Checkin; 42import android.provider.Settings; 43import android.provider.Telephony; 44import android.telephony.ServiceState; 45import android.telephony.TelephonyManager; 46import android.telephony.gsm.GsmCellLocation; 47import android.text.TextUtils; 48import android.util.EventLog; 49import android.util.Log; 50 51import com.android.internal.telephony.DataCallState; 52import com.android.internal.telephony.DataConnection; 53import com.android.internal.telephony.DataConnectionTracker; 54import com.android.internal.telephony.Phone; 55import com.android.internal.telephony.RetryManager; 56import com.android.internal.telephony.TelephonyEventLog; 57import com.android.internal.telephony.DataConnection.FailCause; 58 59import java.io.IOException; 60import java.util.ArrayList; 61 62/** 63 * {@hide} 64 */ 65public final class GsmDataConnectionTracker extends DataConnectionTracker { 66 protected final String LOG_TAG = "GSM"; 67 68 private GSMPhone mGsmPhone; 69 /** 70 * Handles changes to the APN db. 71 */ 72 private class ApnChangeObserver extends ContentObserver { 73 public ApnChangeObserver () { 74 super(mDataConnectionTracker); 75 } 76 77 @Override 78 public void onChange(boolean selfChange) { 79 sendMessage(obtainMessage(EVENT_APN_CHANGED)); 80 } 81 } 82 83 //***** Instance Variables 84 85 INetStatService netstat; 86 // Indicates baseband will not auto-attach 87 private boolean noAutoAttach = false; 88 89 private boolean mReregisterOnReconnectFailure = false; 90 private ContentResolver mResolver; 91 92 private boolean mPingTestActive = false; 93 // Count of PDP reset attempts; reset when we see incoming, 94 // call reRegisterNetwork, or pingTest succeeds. 95 private int mPdpResetCount = 0; 96 private boolean mIsScreenOn = true; 97 98 /** Delay between APN attempts */ 99 protected static final int APN_DELAY_MILLIS = 5000; 100 101 //useful for debugging 102 boolean failNextConnect = false; 103 104 /** 105 * allApns holds all apns for this sim spn, retrieved from 106 * the Carrier DB. 107 * 108 * Create once after simcard info is loaded 109 */ 110 private ArrayList<ApnSetting> allApns = null; 111 112 /** 113 * waitingApns holds all apns that are waiting to be connected 114 * 115 * It is a subset of allApns and has the same format 116 */ 117 private ArrayList<ApnSetting> waitingApns = null; 118 119 private ApnSetting preferredApn = null; 120 121 /* Currently active APN */ 122 protected ApnSetting mActiveApn; 123 124 /** 125 * pdpList holds all the PDP connection, i.e. IP Link in GPRS 126 */ 127 private ArrayList<DataConnection> pdpList; 128 129 /** Currently active PdpConnection */ 130 private PdpConnection mActivePdp; 131 132 /** Is packet service restricted by network */ 133 private boolean mIsPsRestricted = false; 134 135 //***** Constants 136 137 // TODO: Increase this to match the max number of simultaneous 138 // PDP contexts we plan to support. 139 /** 140 * Pool size of PdpConnection objects. 141 */ 142 private static final int PDP_CONNECTION_POOL_SIZE = 1; 143 144 private static final int POLL_PDP_MILLIS = 5 * 1000; 145 146 private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect"; 147 private static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason"; 148 149 static final Uri PREFERAPN_URI = Uri.parse("content://telephony/carriers/preferapn"); 150 static final String APN_ID = "apn_id"; 151 private boolean canSetPreferApn = false; 152 153 // for tracking retrys on the default APN 154 private RetryManager mDefaultRetryManager; 155 // for tracking retrys on a secondary APN 156 private RetryManager mSecondaryRetryManager; 157 158 BroadcastReceiver mIntentReceiver = new BroadcastReceiver () 159 { 160 @Override 161 public void onReceive(Context context, Intent intent) 162 { 163 String action = intent.getAction(); 164 if (action.equals(Intent.ACTION_SCREEN_ON)) { 165 mIsScreenOn = true; 166 stopNetStatPoll(); 167 startNetStatPoll(); 168 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 169 mIsScreenOn = false; 170 stopNetStatPoll(); 171 startNetStatPoll(); 172 } else if (action.equals((INTENT_RECONNECT_ALARM))) { 173 Log.d(LOG_TAG, "GPRS reconnect alarm. Previous state was " + state); 174 175 String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON); 176 if (state == State.FAILED) { 177 Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION); 178 msg.arg1 = 0; // tearDown is false 179 msg.obj = (String) reason; 180 sendMessage(msg); 181 } 182 sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); 183 } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { 184 final android.net.NetworkInfo networkInfo = (NetworkInfo) 185 intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); 186 mIsWifiConnected = (networkInfo != null && networkInfo.isConnected()); 187 } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { 188 final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 189 WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; 190 191 if (!enabled) { 192 // when wifi got disabeled, the NETWORK_STATE_CHANGED_ACTION 193 // quit and wont report disconnected til next enalbing. 194 mIsWifiConnected = false; 195 } 196 } 197 } 198 }; 199 200 /** Watches for changes to the APN db. */ 201 private ApnChangeObserver apnObserver; 202 203 //***** Constructor 204 205 GsmDataConnectionTracker(GSMPhone p) { 206 super(p); 207 mGsmPhone = p; 208 p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null); 209 p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); 210 p.mSIMRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null); 211 p.mCM.registerForDataStateChanged (this, EVENT_DATA_STATE_CHANGED, null); 212 p.mCT.registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null); 213 p.mCT.registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null); 214 p.mSST.registerForGprsAttached(this, EVENT_GPRS_ATTACHED, null); 215 p.mSST.registerForGprsDetached(this, EVENT_GPRS_DETACHED, null); 216 p.mSST.registerForRoamingOn(this, EVENT_ROAMING_ON, null); 217 p.mSST.registerForRoamingOff(this, EVENT_ROAMING_OFF, null); 218 p.mSST.registerForPsRestrictedEnabled(this, EVENT_PS_RESTRICT_ENABLED, null); 219 p.mSST.registerForPsRestrictedDisabled(this, EVENT_PS_RESTRICT_DISABLED, null); 220 221 this.netstat = INetStatService.Stub.asInterface(ServiceManager.getService("netstat")); 222 223 IntentFilter filter = new IntentFilter(); 224 filter.addAction(INTENT_RECONNECT_ALARM); 225 filter.addAction(Intent.ACTION_SCREEN_ON); 226 filter.addAction(Intent.ACTION_SCREEN_OFF); 227 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 228 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 229 230 // TODO: Why is this registering the phone as the receiver of the intent 231 // and not its own handler? 232 p.getContext().registerReceiver(mIntentReceiver, filter, null, p); 233 234 235 mDataConnectionTracker = this; 236 mResolver = phone.getContext().getContentResolver(); 237 238 apnObserver = new ApnChangeObserver(); 239 p.getContext().getContentResolver().registerContentObserver( 240 Telephony.Carriers.CONTENT_URI, true, apnObserver); 241 242 createAllPdpList(); 243 244 // This preference tells us 1) initial condition for "dataEnabled", 245 // and 2) whether the RIL will setup the baseband to auto-PS attach. 246 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext()); 247 dataEnabled[APN_DEFAULT_ID] = !sp.getBoolean(GSMPhone.DATA_DISABLED_ON_BOOT_KEY, false); 248 if (dataEnabled[APN_DEFAULT_ID]) { 249 enabledCount++; 250 } 251 noAutoAttach = !dataEnabled[APN_DEFAULT_ID]; 252 253 if (!mRetryMgr.configure(SystemProperties.get("ro.gsm.data_retry_config"))) { 254 if (!mRetryMgr.configure(DEFAULT_DATA_RETRY_CONFIG)) { 255 // Should never happen, log an error and default to a simple linear sequence. 256 Log.e(LOG_TAG, "Could not configure using DEFAULT_DATA_RETRY_CONFIG=" 257 + DEFAULT_DATA_RETRY_CONFIG); 258 mRetryMgr.configure(20, 2000, 1000); 259 } 260 } 261 262 mDefaultRetryManager = mRetryMgr; 263 mSecondaryRetryManager = new RetryManager(); 264 265 if (!mSecondaryRetryManager.configure(SystemProperties.get( 266 "ro.gsm.2nd_data_retry_config"))) { 267 if (!mSecondaryRetryManager.configure(SECONDARY_DATA_RETRY_CONFIG)) { 268 // Should never happen, log an error and default to a simple sequence. 269 Log.e(LOG_TAG, "Could note configure using SECONDARY_DATA_RETRY_CONFIG=" 270 + SECONDARY_DATA_RETRY_CONFIG); 271 mSecondaryRetryManager.configure("max_retries=3, 333, 333, 333"); 272 } 273 } 274 } 275 276 public void dispose() { 277 //Unregister for all events 278 phone.mCM.unregisterForAvailable(this); 279 phone.mCM.unregisterForOffOrNotAvailable(this); 280 mGsmPhone.mSIMRecords.unregisterForRecordsLoaded(this); 281 phone.mCM.unregisterForDataStateChanged(this); 282 mGsmPhone.mCT.unregisterForVoiceCallEnded(this); 283 mGsmPhone.mCT.unregisterForVoiceCallStarted(this); 284 mGsmPhone.mSST.unregisterForGprsAttached(this); 285 mGsmPhone.mSST.unregisterForGprsDetached(this); 286 mGsmPhone.mSST.unregisterForRoamingOn(this); 287 mGsmPhone.mSST.unregisterForRoamingOff(this); 288 mGsmPhone.mSST.unregisterForPsRestrictedEnabled(this); 289 mGsmPhone.mSST.unregisterForPsRestrictedDisabled(this); 290 291 phone.getContext().unregisterReceiver(this.mIntentReceiver); 292 phone.getContext().getContentResolver().unregisterContentObserver(this.apnObserver); 293 294 destroyAllPdpList(); 295 } 296 297 protected void finalize() { 298 if(DBG) Log.d(LOG_TAG, "GsmDataConnectionTracker finalized"); 299 } 300 301 protected void setState(State s) { 302 if (DBG) log ("setState: " + s); 303 if (state != s) { 304 if (s == State.INITING) { // request PDP context 305 Checkin.updateStats( 306 phone.getContext().getContentResolver(), 307 Checkin.Stats.Tag.PHONE_GPRS_ATTEMPTED, 1, 0.0); 308 } 309 310 if (s == State.CONNECTED) { // pppd is up 311 Checkin.updateStats( 312 phone.getContext().getContentResolver(), 313 Checkin.Stats.Tag.PHONE_GPRS_CONNECTED, 1, 0.0); 314 } 315 } 316 317 state = s; 318 319 if (state == State.FAILED) { 320 if (waitingApns != null) 321 waitingApns.clear(); // when teardown the connection and set to IDLE 322 } 323 } 324 325 public String[] getActiveApnTypes() { 326 String[] result; 327 if (mActiveApn != null) { 328 result = mActiveApn.types; 329 } else { 330 result = new String[1]; 331 result[0] = Phone.APN_TYPE_DEFAULT; 332 } 333 return result; 334 } 335 336 protected String getActiveApnString() { 337 String result = null; 338 if (mActiveApn != null) { 339 result = mActiveApn.apn; 340 } 341 return result; 342 } 343 344 /** 345 * The data connection is expected to be setup while device 346 * 1. has sim card 347 * 2. registered to gprs service 348 * 3. user doesn't explicitly disable data service 349 * 4. wifi is not on 350 * 351 * @return false while no data connection if all above requirements are met. 352 */ 353 public boolean isDataConnectionAsDesired() { 354 boolean roaming = phone.getServiceState().getRoaming(); 355 356 if (mGsmPhone.mSIMRecords.getRecordsLoaded() && 357 mGsmPhone.mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE && 358 (!roaming || getDataOnRoamingEnabled()) && 359 !mIsWifiConnected && 360 !mIsPsRestricted ) { 361 return (state == State.CONNECTED); 362 } 363 return true; 364 } 365 366 @Override 367 protected boolean isApnTypeActive(String type) { 368 // TODO: support simultaneous with List instead 369 return mActiveApn != null && mActiveApn.canHandleType(type); 370 } 371 372 @Override 373 protected boolean isApnTypeAvailable(String type) { 374 if (allApns != null) { 375 for (ApnSetting apn : allApns) { 376 if (apn.canHandleType(type)) { 377 return true; 378 } 379 } 380 } 381 return false; 382 } 383 384 /** 385 * Formerly this method was ArrayList<PdpConnection> getAllPdps() 386 */ 387 public ArrayList<DataConnection> getAllDataConnections() { 388 ArrayList<DataConnection> pdps = (ArrayList<DataConnection>)pdpList.clone(); 389 return pdps; 390 } 391 392 private boolean isDataAllowed() { 393 boolean roaming = phone.getServiceState().getRoaming(); 394 return getAnyDataEnabled() && (!roaming || getDataOnRoamingEnabled()) && 395 mMasterDataEnabled; 396 } 397 398 //****** Called from ServiceStateTracker 399 /** 400 * Invoked when ServiceStateTracker observes a transition from GPRS 401 * attach to detach. 402 */ 403 protected void onGprsDetached() { 404 /* 405 * We presently believe it is unnecessary to tear down the PDP context 406 * when GPRS detaches, but we should stop the network polling. 407 */ 408 stopNetStatPoll(); 409 phone.notifyDataConnection(Phone.REASON_GPRS_DETACHED); 410 } 411 412 private void onGprsAttached() { 413 if (state == State.CONNECTED) { 414 startNetStatPoll(); 415 phone.notifyDataConnection(Phone.REASON_GPRS_ATTACHED); 416 } else { 417 if (state == State.FAILED) { 418 cleanUpConnection(false, Phone.REASON_GPRS_ATTACHED); 419 mRetryMgr.resetRetryCount(); 420 } 421 trySetupData(Phone.REASON_GPRS_ATTACHED); 422 } 423 } 424 425 private boolean trySetupData(String reason) { 426 if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason)); 427 428 Log.d(LOG_TAG, "[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted); 429 430 if (phone.getSimulatedRadioControl() != null) { 431 // Assume data is connected on the simulator 432 // FIXME this can be improved 433 setState(State.CONNECTED); 434 phone.notifyDataConnection(reason); 435 436 Log.i(LOG_TAG, "(fix?) We're on the simulator; assuming data is connected"); 437 return true; 438 } 439 440 int gprsState = mGsmPhone.mSST.getCurrentGprsState(); 441 boolean desiredPowerState = mGsmPhone.mSST.getDesiredPowerState(); 442 443 if ((state == State.IDLE || state == State.SCANNING) 444 && (gprsState == ServiceState.STATE_IN_SERVICE || noAutoAttach) 445 && mGsmPhone.mSIMRecords.getRecordsLoaded() 446 && phone.getState() == Phone.State.IDLE 447 && isDataAllowed() 448 && !mIsPsRestricted 449 && desiredPowerState ) { 450 451 if (state == State.IDLE) { 452 waitingApns = buildWaitingApns(); 453 if (waitingApns.isEmpty()) { 454 if (DBG) log("No APN found"); 455 notifyNoData(PdpConnection.FailCause.MISSING_UKNOWN_APN); 456 return false; 457 } else { 458 log ("Create from allApns : " + apnListToString(allApns)); 459 } 460 } 461 462 if (DBG) { 463 log ("Setup waitngApns : " + apnListToString(waitingApns)); 464 } 465 return setupData(reason); 466 } else { 467 if (DBG) 468 log("trySetupData: Not ready for data: " + 469 " dataState=" + state + 470 " gprsState=" + gprsState + 471 " sim=" + mGsmPhone.mSIMRecords.getRecordsLoaded() + 472 " UMTS=" + mGsmPhone.mSST.isConcurrentVoiceAndData() + 473 " phoneState=" + phone.getState() + 474 " isDataAllowed=" + isDataAllowed() + 475 " dataEnabled=" + getAnyDataEnabled() + 476 " roaming=" + phone.getServiceState().getRoaming() + 477 " dataOnRoamingEnable=" + getDataOnRoamingEnabled() + 478 " ps restricted=" + mIsPsRestricted + 479 " desiredPowerState=" + desiredPowerState + 480 " MasterDataEnabled=" + mMasterDataEnabled); 481 return false; 482 } 483 } 484 485 /** 486 * If tearDown is true, this only tears down a CONNECTED session. Presently, 487 * there is no mechanism for abandoning an INITING/CONNECTING session, 488 * but would likely involve cancelling pending async requests or 489 * setting a flag or new state to ignore them when they came in 490 * @param tearDown true if the underlying PdpConnection should be 491 * disconnected. 492 * @param reason reason for the clean up. 493 */ 494 private void cleanUpConnection(boolean tearDown, String reason) { 495 if (DBG) log("Clean up connection due to " + reason); 496 497 // Clear the reconnect alarm, if set. 498 if (mReconnectIntent != null) { 499 AlarmManager am = 500 (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); 501 am.cancel(mReconnectIntent); 502 mReconnectIntent = null; 503 } 504 505 setState(State.DISCONNECTING); 506 507 for (DataConnection conn : pdpList) { 508 PdpConnection pdp = (PdpConnection) conn; 509 if (tearDown) { 510 Message msg = obtainMessage(EVENT_DISCONNECT_DONE, reason); 511 pdp.disconnect(msg); 512 } else { 513 pdp.clearSettings(); 514 } 515 } 516 stopNetStatPoll(); 517 518 if (!tearDown) { 519 setState(State.IDLE); 520 phone.notifyDataConnection(reason); 521 mActiveApn = null; 522 } 523 } 524 525 /** 526 * @param types comma delimited list of APN types 527 * @return array of APN types 528 */ 529 private String[] parseTypes(String types) { 530 String[] result; 531 // If unset, set to DEFAULT. 532 if (types == null || types.equals("")) { 533 result = new String[1]; 534 result[0] = Phone.APN_TYPE_ALL; 535 } else { 536 result = types.split(","); 537 } 538 return result; 539 } 540 541 private ArrayList<ApnSetting> createApnList(Cursor cursor) { 542 ArrayList<ApnSetting> result = new ArrayList<ApnSetting>(); 543 if (cursor.moveToFirst()) { 544 do { 545 String[] types = parseTypes( 546 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE))); 547 ApnSetting apn = new ApnSetting( 548 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)), 549 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)), 550 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)), 551 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)), 552 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY)), 553 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)), 554 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC)), 555 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY)), 556 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)), 557 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)), 558 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)), 559 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)), 560 types); 561 result.add(apn); 562 } while (cursor.moveToNext()); 563 } 564 return result; 565 } 566 567 private PdpConnection findFreePdp() { 568 for (DataConnection conn : pdpList) { 569 PdpConnection pdp = (PdpConnection) conn; 570 if (pdp.getState() == DataConnection.State.INACTIVE) { 571 return pdp; 572 } 573 } 574 return null; 575 } 576 577 private boolean setupData(String reason) { 578 ApnSetting apn; 579 PdpConnection pdp; 580 581 apn = getNextApn(); 582 if (apn == null) return false; 583 pdp = findFreePdp(); 584 if (pdp == null) { 585 if (DBG) log("setupData: No free PdpConnection found!"); 586 return false; 587 } 588 mActiveApn = apn; 589 mActivePdp = pdp; 590 591 Message msg = obtainMessage(); 592 msg.what = EVENT_DATA_SETUP_COMPLETE; 593 msg.obj = reason; 594 pdp.connect(apn, msg); 595 596 setState(State.INITING); 597 phone.notifyDataConnection(reason); 598 return true; 599 } 600 601 protected String getInterfaceName(String apnType) { 602 if (mActivePdp != null && 603 (apnType == null || 604 (mActiveApn != null && mActiveApn.canHandleType(apnType)))) { 605 return mActivePdp.getInterface(); 606 } 607 return null; 608 } 609 610 protected String getIpAddress(String apnType) { 611 if (mActivePdp != null && 612 (apnType == null || 613 (mActiveApn != null && mActiveApn.canHandleType(apnType)))) { 614 return mActivePdp.getIpAddress(); 615 } 616 return null; 617 } 618 619 public String getGateway(String apnType) { 620 if (mActivePdp != null && 621 (apnType == null || 622 (mActiveApn != null && mActiveApn.canHandleType(apnType)))) { 623 return mActivePdp.getGatewayAddress(); 624 } 625 return null; 626 } 627 628 protected String[] getDnsServers(String apnType) { 629 if (mActivePdp != null && 630 (apnType == null || 631 (mActiveApn != null && mActiveApn.canHandleType(apnType)))) { 632 return mActivePdp.getDnsServers(); 633 } 634 return null; 635 } 636 637 private boolean 638 pdpStatesHasCID (ArrayList<DataCallState> states, int cid) { 639 for (int i = 0, s = states.size() ; i < s ; i++) { 640 if (states.get(i).cid == cid) return true; 641 } 642 643 return false; 644 } 645 646 private boolean 647 pdpStatesHasActiveCID (ArrayList<DataCallState> states, int cid) { 648 for (int i = 0, s = states.size() ; i < s ; i++) { 649 if ((states.get(i).cid == cid) && (states.get(i).active != 0)) { 650 return true; 651 } 652 } 653 654 return false; 655 } 656 657 /** 658 * Handles changes to the APN database. 659 */ 660 private void onApnChanged() { 661 boolean isConnected; 662 663 isConnected = (state != State.IDLE && state != State.FAILED); 664 665 // The "current" may no longer be valid. MMS depends on this to send properly. 666 mGsmPhone.updateCurrentCarrierInProvider(); 667 668 // TODO: It'd be nice to only do this if the changed entrie(s) 669 // match the current operator. 670 createAllApnList(); 671 if (state != State.DISCONNECTING) { 672 cleanUpConnection(isConnected, Phone.REASON_APN_CHANGED); 673 if (!isConnected) { 674 // reset reconnect timer 675 mRetryMgr.resetRetryCount(); 676 mReregisterOnReconnectFailure = false; 677 trySetupData(Phone.REASON_APN_CHANGED); 678 } 679 } 680 } 681 682 /** 683 * @param explicitPoll if true, indicates that *we* polled for this 684 * update while state == CONNECTED rather than having it delivered 685 * via an unsolicited response (which could have happened at any 686 * previous state 687 */ 688 protected void onPdpStateChanged (AsyncResult ar, boolean explicitPoll) { 689 ArrayList<DataCallState> pdpStates; 690 691 pdpStates = (ArrayList<DataCallState>)(ar.result); 692 693 if (ar.exception != null) { 694 // This is probably "radio not available" or something 695 // of that sort. If so, the whole connection is going 696 // to come down soon anyway 697 return; 698 } 699 700 if (state == State.CONNECTED) { 701 // The way things are supposed to work, the PDP list 702 // should not contain the CID after it disconnects. 703 // However, the way things really work, sometimes the PDP 704 // context is still listed with active = false, which 705 // makes it hard to distinguish an activating context from 706 // an activated-and-then deactivated one. 707 if (!pdpStatesHasCID(pdpStates, cidActive)) { 708 // It looks like the PDP context has deactivated. 709 // Tear everything down and try to reconnect. 710 711 Log.i(LOG_TAG, "PDP connection has dropped. Reconnecting"); 712 713 // Add an event log when the network drops PDP 714 int cid = -1; 715 GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); 716 if (loc != null) cid = loc.getCid(); 717 EventLog.List val = new EventLog.List(cid, 718 TelephonyManager.getDefault().getNetworkType()); 719 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_NETWORK_DROP, val); 720 721 cleanUpConnection(true, null); 722 return; 723 } else if (!pdpStatesHasActiveCID(pdpStates, cidActive)) { 724 // Here, we only consider this authoritative if we asked for the 725 // PDP list. If it was an unsolicited response, we poll again 726 // to make sure everyone agrees on the initial state. 727 728 if (!explicitPoll) { 729 // We think it disconnected but aren't sure...poll from our side 730 phone.mCM.getPDPContextList( 731 this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); 732 } else { 733 Log.i(LOG_TAG, "PDP connection has dropped (active=false case). " 734 + " Reconnecting"); 735 736 // Log the network drop on the event log. 737 int cid = -1; 738 GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); 739 if (loc != null) cid = loc.getCid(); 740 EventLog.List val = new EventLog.List(cid, 741 TelephonyManager.getDefault().getNetworkType()); 742 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_NETWORK_DROP, val); 743 744 cleanUpConnection(true, null); 745 } 746 } 747 } 748 } 749 750 private void notifyDefaultData(String reason) { 751 setupDnsProperties(); 752 setState(State.CONNECTED); 753 phone.notifyDataConnection(reason); 754 startNetStatPoll(); 755 // reset reconnect timer 756 mRetryMgr.resetRetryCount(); 757 mReregisterOnReconnectFailure = false; 758 } 759 760 private void setupDnsProperties() { 761 int mypid = android.os.Process.myPid(); 762 String[] servers = getDnsServers(null); 763 String propName; 764 String propVal; 765 int count; 766 767 count = 0; 768 for (int i = 0; i < servers.length; i++) { 769 String serverAddr = servers[i]; 770 if (!TextUtils.equals(serverAddr, "0.0.0.0")) { 771 SystemProperties.set("net.dns" + (i+1) + "." + mypid, serverAddr); 772 count++; 773 } 774 } 775 for (int i = count+1; i <= 4; i++) { 776 propName = "net.dns" + i + "." + mypid; 777 propVal = SystemProperties.get(propName); 778 if (propVal.length() != 0) { 779 SystemProperties.set(propName, ""); 780 } 781 } 782 /* 783 * Bump the property that tells the name resolver library 784 * to reread the DNS server list from the properties. 785 */ 786 propVal = SystemProperties.get("net.dnschange"); 787 if (propVal.length() != 0) { 788 try { 789 int n = Integer.parseInt(propVal); 790 SystemProperties.set("net.dnschange", "" + (n+1)); 791 } catch (NumberFormatException e) { 792 } 793 } 794 } 795 796 /** 797 * This is a kludge to deal with the fact that 798 * the PDP state change notification doesn't always work 799 * with certain RIL impl's/basebands 800 * 801 */ 802 private void startPeriodicPdpPoll() { 803 removeMessages(EVENT_POLL_PDP); 804 805 sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS); 806 } 807 808 private void resetPollStats() { 809 txPkts = -1; 810 rxPkts = -1; 811 sentSinceLastRecv = 0; 812 netStatPollPeriod = POLL_NETSTAT_MILLIS; 813 mNoRecvPollCount = 0; 814 } 815 816 private void doRecovery() { 817 if (state == State.CONNECTED) { 818 int maxPdpReset = Settings.Gservices.getInt(mResolver, 819 Settings.Gservices.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT, 820 DEFAULT_MAX_PDP_RESET_FAIL); 821 if (mPdpResetCount < maxPdpReset) { 822 mPdpResetCount++; 823 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_RESET, sentSinceLastRecv); 824 cleanUpConnection(true, Phone.REASON_PDP_RESET); 825 } else { 826 mPdpResetCount = 0; 827 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_REREGISTER_NETWORK, sentSinceLastRecv); 828 mGsmPhone.mSST.reRegisterNetwork(null); 829 } 830 // TODO: Add increasingly drastic recovery steps, eg, 831 // reset the radio, reset the device. 832 } 833 } 834 835 protected void startNetStatPoll() { 836 if (state == State.CONNECTED && mPingTestActive == false && netStatPollEnabled == false) { 837 Log.d(LOG_TAG, "[DataConnection] Start poll NetStat"); 838 resetPollStats(); 839 netStatPollEnabled = true; 840 mPollNetStat.run(); 841 } 842 } 843 844 protected void stopNetStatPoll() { 845 netStatPollEnabled = false; 846 removeCallbacks(mPollNetStat); 847 Log.d(LOG_TAG, "[DataConnection] Stop poll NetStat"); 848 } 849 850 protected void restartRadio() { 851 Log.d(LOG_TAG, "************TURN OFF RADIO**************"); 852 cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF); 853 phone.mCM.setRadioPower(false, null); 854 /* Note: no need to call setRadioPower(true). Assuming the desired 855 * radio power state is still ON (as tracked by ServiceStateTracker), 856 * ServiceStateTracker will call setRadioPower when it receives the 857 * RADIO_STATE_CHANGED notification for the power off. And if the 858 * desired power state has changed in the interim, we don't want to 859 * override it with an unconditional power on. 860 */ 861 862 int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0")); 863 SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1)); 864 } 865 866 private Runnable mPollNetStat = new Runnable() 867 { 868 869 public void run() { 870 long sent, received; 871 long preTxPkts = -1, preRxPkts = -1; 872 873 Activity newActivity; 874 875 preTxPkts = txPkts; 876 preRxPkts = rxPkts; 877 878 try { 879 txPkts = netstat.getMobileTxPackets(); 880 rxPkts = netstat.getMobileRxPackets(); 881 } catch (RemoteException e) { 882 txPkts = 0; 883 rxPkts = 0; 884 } 885 886 //Log.d(LOG_TAG, "rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts)); 887 888 if (netStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) { 889 sent = txPkts - preTxPkts; 890 received = rxPkts - preRxPkts; 891 892 if ( sent > 0 && received > 0 ) { 893 sentSinceLastRecv = 0; 894 newActivity = Activity.DATAINANDOUT; 895 mPdpResetCount = 0; 896 } else if (sent > 0 && received == 0) { 897 if (phone.getState() == Phone.State.IDLE) { 898 sentSinceLastRecv += sent; 899 } else { 900 sentSinceLastRecv = 0; 901 } 902 newActivity = Activity.DATAOUT; 903 } else if (sent == 0 && received > 0) { 904 sentSinceLastRecv = 0; 905 newActivity = Activity.DATAIN; 906 mPdpResetCount = 0; 907 } else if (sent == 0 && received == 0) { 908 newActivity = Activity.NONE; 909 } else { 910 sentSinceLastRecv = 0; 911 newActivity = Activity.NONE; 912 } 913 914 if (activity != newActivity && mIsScreenOn) { 915 activity = newActivity; 916 phone.notifyDataActivity(); 917 } 918 } 919 920 int watchdogTrigger = Settings.Gservices.getInt(mResolver, 921 Settings.Gservices.PDP_WATCHDOG_TRIGGER_PACKET_COUNT, 922 NUMBER_SENT_PACKETS_OF_HANG); 923 924 if (sentSinceLastRecv >= watchdogTrigger) { 925 // we already have NUMBER_SENT_PACKETS sent without ack 926 if (mNoRecvPollCount == 0) { 927 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_RESET_COUNTDOWN_TRIGGERED, 928 sentSinceLastRecv); 929 } 930 931 int noRecvPollLimit = Settings.Gservices.getInt(mResolver, 932 Settings.Gservices.PDP_WATCHDOG_ERROR_POLL_COUNT, NO_RECV_POLL_LIMIT); 933 934 if (mNoRecvPollCount < noRecvPollLimit) { 935 // It's possible the PDP context went down and we weren't notified. 936 // Start polling the context list in an attempt to recover. 937 if (DBG) log("no DATAIN in a while; polling PDP"); 938 phone.mCM.getDataCallList(obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); 939 940 mNoRecvPollCount++; 941 942 // Slow down the poll interval to let things happen 943 netStatPollPeriod = Settings.Gservices.getInt(mResolver, 944 Settings.Gservices.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS, 945 POLL_NETSTAT_SLOW_MILLIS); 946 } else { 947 if (DBG) log("Sent " + String.valueOf(sentSinceLastRecv) + 948 " pkts since last received"); 949 // We've exceeded the threshold. Run ping test as a final check; 950 // it will proceed with recovery if ping fails. 951 stopNetStatPoll(); 952 Thread pingTest = new Thread() { 953 public void run() { 954 runPingTest(); 955 } 956 }; 957 mPingTestActive = true; 958 pingTest.start(); 959 } 960 } else { 961 mNoRecvPollCount = 0; 962 if (mIsScreenOn) { 963 netStatPollPeriod = Settings.Gservices.getInt(mResolver, 964 Settings.Gservices.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS); 965 } else { 966 netStatPollPeriod = Settings.Gservices.getInt(mResolver, 967 Settings.Gservices.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS, 968 POLL_NETSTAT_SCREEN_OFF_MILLIS); 969 } 970 } 971 972 if (netStatPollEnabled) { 973 mDataConnectionTracker.postDelayed(this, netStatPollPeriod); 974 } 975 } 976 }; 977 978 private void runPingTest () { 979 int status = -1; 980 try { 981 String address = Settings.Gservices.getString(mResolver, 982 Settings.Gservices.PDP_WATCHDOG_PING_ADDRESS); 983 int deadline = Settings.Gservices.getInt(mResolver, 984 Settings.Gservices.PDP_WATCHDOG_PING_DEADLINE, DEFAULT_PING_DEADLINE); 985 if (DBG) log("pinging " + address + " for " + deadline + "s"); 986 if (address != null && !NULL_IP.equals(address)) { 987 Process p = Runtime.getRuntime() 988 .exec("ping -c 1 -i 1 -w "+ deadline + " " + address); 989 status = p.waitFor(); 990 } 991 } catch (IOException e) { 992 Log.w(LOG_TAG, "ping failed: IOException"); 993 } catch (Exception e) { 994 Log.w(LOG_TAG, "exception trying to ping"); 995 } 996 997 if (status == 0) { 998 // ping succeeded. False alarm. Reset netStatPoll. 999 // ("-1" for this event indicates a false alarm) 1000 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_RESET, -1); 1001 mPdpResetCount = 0; 1002 sendMessage(obtainMessage(EVENT_START_NETSTAT_POLL)); 1003 } else { 1004 // ping failed. Proceed with recovery. 1005 sendMessage(obtainMessage(EVENT_START_RECOVERY)); 1006 } 1007 } 1008 1009 /** 1010 * Returns true if the last fail cause is something that 1011 * seems like it deserves an error notification. 1012 * Transient errors are ignored 1013 */ 1014 private boolean shouldPostNotification(PdpConnection.FailCause cause) { 1015 boolean shouldPost = true; 1016 // TODO CHECK 1017 // if (dataLink != null) { 1018 // shouldPost = dataLink.getLastLinkExitCode() != DataLink.EXIT_OPEN_FAILED; 1019 //} 1020 return (shouldPost && cause != PdpConnection.FailCause.UNKNOWN); 1021 } 1022 1023 /** 1024 * Return true if data connection need to be setup after disconnected due to 1025 * reason. 1026 * 1027 * @param reason the reason why data is disconnected 1028 * @return true if try setup data connection is need for this reason 1029 */ 1030 private boolean retryAfterDisconnected(String reason) { 1031 boolean retry = true; 1032 1033 if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) { 1034 retry = false; 1035 } 1036 return retry; 1037 } 1038 1039 private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) { 1040 if (state == State.FAILED) { 1041 if (!mRetryMgr.isRetryNeeded()) { 1042 if (!mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { 1043 // if no more retries on a secondary APN attempt, tell the world and revert. 1044 phone.notifyDataConnection(Phone.REASON_APN_FAILED); 1045 onEnableApn(apnTypeToId(mRequestedApnType), DISABLED); 1046 return; 1047 } 1048 if (mReregisterOnReconnectFailure) { 1049 // We've re-registerd once now just retry forever. 1050 mRetryMgr.retryForeverUsingLastTimeout(); 1051 } else { 1052 // Try to re-register to the network. 1053 Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network"); 1054 mReregisterOnReconnectFailure = true; 1055 mGsmPhone.mSST.reRegisterNetwork(null); 1056 mRetryMgr.resetRetryCount(); 1057 return; 1058 } 1059 } 1060 1061 int nextReconnectDelay = mRetryMgr.getRetryTimer(); 1062 Log.d(LOG_TAG, "PDP activate failed. Scheduling next attempt for " 1063 + (nextReconnectDelay / 1000) + "s"); 1064 1065 AlarmManager am = 1066 (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); 1067 Intent intent = new Intent(INTENT_RECONNECT_ALARM); 1068 intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason); 1069 mReconnectIntent = PendingIntent.getBroadcast( 1070 phone.getContext(), 0, intent, 0); 1071 am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1072 SystemClock.elapsedRealtime() + nextReconnectDelay, 1073 mReconnectIntent); 1074 1075 mRetryMgr.increaseRetryCount(); 1076 1077 if (!shouldPostNotification(lastFailCauseCode)) { 1078 Log.d(LOG_TAG,"NOT Posting GPRS Unavailable notification " 1079 + "-- likely transient error"); 1080 } else { 1081 notifyNoData(lastFailCauseCode); 1082 } 1083 } 1084 } 1085 1086 private void notifyNoData(PdpConnection.FailCause lastFailCauseCode) { 1087 setState(State.FAILED); 1088 } 1089 1090 protected void onRecordsLoaded() { 1091 createAllApnList(); 1092 if (state == State.FAILED) { 1093 cleanUpConnection(false, null); 1094 } 1095 sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED)); 1096 } 1097 1098 @Override 1099 protected void onEnableNewApn() { 1100 // change our retry manager to use the appropriate numbers for the new APN 1101 if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { 1102 mRetryMgr = mDefaultRetryManager; 1103 } else { 1104 mRetryMgr = mSecondaryRetryManager; 1105 } 1106 mRetryMgr.resetRetryCount(); 1107 1108 // TODO: To support simultaneous PDP contexts, this should really only call 1109 // cleanUpConnection if it needs to free up a PdpConnection. 1110 cleanUpConnection(true, Phone.REASON_APN_SWITCHED); 1111 } 1112 1113 protected boolean onTrySetupData(String reason) { 1114 return trySetupData(reason); 1115 } 1116 1117 @Override 1118 protected void onRoamingOff() { 1119 trySetupData(Phone.REASON_ROAMING_OFF); 1120 } 1121 1122 @Override 1123 protected void onRoamingOn() { 1124 if (getDataOnRoamingEnabled()) { 1125 trySetupData(Phone.REASON_ROAMING_ON); 1126 } else { 1127 if (DBG) log("Tear down data connection on roaming."); 1128 cleanUpConnection(true, Phone.REASON_ROAMING_ON); 1129 } 1130 } 1131 1132 protected void onRadioAvailable() { 1133 if (phone.getSimulatedRadioControl() != null) { 1134 // Assume data is connected on the simulator 1135 // FIXME this can be improved 1136 setState(State.CONNECTED); 1137 phone.notifyDataConnection(null); 1138 1139 Log.i(LOG_TAG, "We're on the simulator; assuming data is connected"); 1140 } 1141 1142 if (state != State.IDLE) { 1143 cleanUpConnection(true, null); 1144 } 1145 } 1146 1147 protected void onRadioOffOrNotAvailable() { 1148 // Make sure our reconnect delay starts at the initial value 1149 // next time the radio comes on 1150 mRetryMgr.resetRetryCount(); 1151 mReregisterOnReconnectFailure = false; 1152 1153 if (phone.getSimulatedRadioControl() != null) { 1154 // Assume data is connected on the simulator 1155 // FIXME this can be improved 1156 Log.i(LOG_TAG, "We're on the simulator; assuming radio off is meaningless"); 1157 } else { 1158 if (DBG) log("Radio is off and clean up all connection"); 1159 // TODO: Should we reset mRequestedApnType to "default"? 1160 cleanUpConnection(false, Phone.REASON_RADIO_TURNED_OFF); 1161 } 1162 } 1163 1164 protected void onDataSetupComplete(AsyncResult ar) { 1165 String reason = null; 1166 if (ar.userObj instanceof String) { 1167 reason = (String) ar.userObj; 1168 } 1169 1170 if (ar.exception == null) { 1171 // everything is setup 1172 if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { 1173 SystemProperties.set("gsm.defaultpdpcontext.active", "true"); 1174 if (canSetPreferApn && preferredApn == null) { 1175 Log.d(LOG_TAG, "PREFERED APN is null"); 1176 preferredApn = mActiveApn; 1177 setPreferredApn(preferredApn.id); 1178 } 1179 } else { 1180 SystemProperties.set("gsm.defaultpdpcontext.active", "false"); 1181 } 1182 notifyDefaultData(reason); 1183 1184 // TODO: For simultaneous PDP support, we need to build another 1185 // trigger another TRY_SETUP_DATA for the next APN type. (Note 1186 // that the existing connection may service that type, in which 1187 // case we should try the next type, etc. 1188 } else { 1189 PdpConnection.FailCause cause; 1190 cause = (PdpConnection.FailCause) (ar.result); 1191 if(DBG) log("PDP setup failed " + cause); 1192 // Log this failure to the Event Logs. 1193 if (cause.isEventLoggable()) { 1194 int cid = -1; 1195 GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); 1196 if (loc != null) cid = loc.getCid(); 1197 1198 EventLog.List val = new EventLog.List( 1199 cause.ordinal(), cid, 1200 TelephonyManager.getDefault().getNetworkType()); 1201 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_PDP_SETUP_FAIL, val); 1202 } 1203 1204 // No try for permanent failure 1205 if (cause.isPermanentFail()) { 1206 notifyNoData(cause); 1207 if (!mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { 1208 phone.notifyDataConnection(Phone.REASON_APN_FAILED); 1209 onEnableApn(apnTypeToId(mRequestedApnType), DISABLED); 1210 } 1211 return; 1212 } 1213 1214 waitingApns.remove(0); 1215 if (waitingApns.isEmpty()) { 1216 // No more to try, start delayed retry 1217 startDelayedRetry(cause, reason); 1218 } else { 1219 // we still have more apns to try 1220 setState(State.SCANNING); 1221 // Wait a bit before trying the next APN, so that 1222 // we're not tying up the RIL command channel 1223 sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, reason), APN_DELAY_MILLIS); 1224 } 1225 } 1226 } 1227 1228 protected void onDisconnectDone(AsyncResult ar) { 1229 String reason = null; 1230 if(DBG) log("EVENT_DISCONNECT_DONE"); 1231 if (ar.userObj instanceof String) { 1232 reason = (String) ar.userObj; 1233 } 1234 setState(State.IDLE); 1235 phone.notifyDataConnection(reason); 1236 mActiveApn = null; 1237 if (retryAfterDisconnected(reason)) { 1238 trySetupData(reason); 1239 } 1240 } 1241 1242 protected void onPollPdp() { 1243 if (state == State.CONNECTED) { 1244 // only poll when connected 1245 phone.mCM.getPDPContextList(this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); 1246 sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS); 1247 } 1248 } 1249 1250 protected void onVoiceCallStarted() { 1251 if (state == State.CONNECTED && ! mGsmPhone.mSST.isConcurrentVoiceAndData()) { 1252 stopNetStatPoll(); 1253 phone.notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED); 1254 } 1255 } 1256 1257 protected void onVoiceCallEnded() { 1258 if (state == State.CONNECTED) { 1259 if (!mGsmPhone.mSST.isConcurrentVoiceAndData()) { 1260 startNetStatPoll(); 1261 phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED); 1262 } else { 1263 // clean slate after call end. 1264 resetPollStats(); 1265 } 1266 } else { 1267 // reset reconnect timer 1268 mRetryMgr.resetRetryCount(); 1269 mReregisterOnReconnectFailure = false; 1270 // in case data setup was attempted when we were on a voice call 1271 trySetupData(Phone.REASON_VOICE_CALL_ENDED); 1272 } 1273 } 1274 1275 protected void onCleanUpConnection(boolean tearDown, String reason) { 1276 cleanUpConnection(tearDown, reason); 1277 } 1278 1279 /** 1280 * Based on the sim operator numeric, create a list for all possible pdps 1281 * with all apns associated with that pdp 1282 * 1283 * 1284 */ 1285 private void createAllApnList() { 1286 allApns = new ArrayList<ApnSetting>(); 1287 String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric(); 1288 1289 if (operator != null) { 1290 String selection = "numeric = '" + operator + "'"; 1291 1292 Cursor cursor = phone.getContext().getContentResolver().query( 1293 Telephony.Carriers.CONTENT_URI, null, selection, null, null); 1294 1295 if (cursor != null) { 1296 if (cursor.getCount() > 0) { 1297 allApns = createApnList(cursor); 1298 // TODO: Figure out where this fits in. This basically just 1299 // writes the pap-secrets file. No longer tied to PdpConnection 1300 // object. Not used on current platform (no ppp). 1301 //PdpConnection pdp = pdpList.get(pdp_name); 1302 //if (pdp != null && pdp.dataLink != null) { 1303 // pdp.dataLink.setPasswordInfo(cursor); 1304 //} 1305 } 1306 cursor.close(); 1307 } 1308 } 1309 1310 if (allApns.isEmpty()) { 1311 if (DBG) log("No APN found for carrier: " + operator); 1312 preferredApn = null; 1313 notifyNoData(PdpConnection.FailCause.MISSING_UKNOWN_APN); 1314 } else { 1315 preferredApn = getPreferredApn(); 1316 Log.d(LOG_TAG, "Get PreferredAPN"); 1317 if (preferredApn != null && !preferredApn.numeric.equals(operator)) { 1318 preferredApn = null; 1319 setPreferredApn(-1); 1320 } 1321 } 1322 } 1323 1324 private void createAllPdpList() { 1325 pdpList = new ArrayList<DataConnection>(); 1326 DataConnection pdp; 1327 1328 for (int i = 0; i < PDP_CONNECTION_POOL_SIZE; i++) { 1329 pdp = new PdpConnection(mGsmPhone); 1330 pdpList.add(pdp); 1331 } 1332 } 1333 1334 private void destroyAllPdpList() { 1335 if(pdpList != null) { 1336 PdpConnection pdp; 1337 pdpList.removeAll(pdpList); 1338 } 1339 } 1340 1341 /** 1342 * 1343 * @return waitingApns list to be used to create PDP 1344 * error when waitingApns.isEmpty() 1345 */ 1346 private ArrayList<ApnSetting> buildWaitingApns() { 1347 ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>(); 1348 String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric(); 1349 1350 if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { 1351 if (canSetPreferApn && preferredApn != null) { 1352 Log.i(LOG_TAG, "Preferred APN:" + operator + ":" 1353 + preferredApn.numeric + ":" + preferredApn); 1354 if (preferredApn.numeric.equals(operator)) { 1355 Log.i(LOG_TAG, "Waiting APN set to preferred APN"); 1356 apnList.add(preferredApn); 1357 return apnList; 1358 } else { 1359 setPreferredApn(-1); 1360 preferredApn = null; 1361 } 1362 } 1363 } 1364 1365 if (allApns != null) { 1366 for (ApnSetting apn : allApns) { 1367 if (apn.canHandleType(mRequestedApnType)) { 1368 apnList.add(apn); 1369 } 1370 } 1371 } 1372 return apnList; 1373 } 1374 1375 /** 1376 * Get next apn in waitingApns 1377 * @return the first apn found in waitingApns, null if none 1378 */ 1379 private ApnSetting getNextApn() { 1380 ArrayList<ApnSetting> list = waitingApns; 1381 ApnSetting apn = null; 1382 1383 if (list != null) { 1384 if (!list.isEmpty()) { 1385 apn = list.get(0); 1386 } 1387 } 1388 return apn; 1389 } 1390 1391 private String apnListToString (ArrayList<ApnSetting> apns) { 1392 StringBuilder result = new StringBuilder(); 1393 for (int i = 0, size = apns.size(); i < size; i++) { 1394 result.append('[') 1395 .append(apns.get(i).toString()) 1396 .append(']'); 1397 } 1398 return result.toString(); 1399 } 1400 1401 private void startDelayedRetry(PdpConnection.FailCause cause, String reason) { 1402 notifyNoData(cause); 1403 reconnectAfterFail(cause, reason); 1404 } 1405 1406 private void setPreferredApn(int pos) { 1407 if (!canSetPreferApn) { 1408 return; 1409 } 1410 1411 ContentResolver resolver = phone.getContext().getContentResolver(); 1412 resolver.delete(PREFERAPN_URI, null, null); 1413 1414 if (pos >= 0) { 1415 ContentValues values = new ContentValues(); 1416 values.put(APN_ID, pos); 1417 resolver.insert(PREFERAPN_URI, values); 1418 } 1419 } 1420 1421 private ApnSetting getPreferredApn() { 1422 if (allApns.isEmpty()) { 1423 return null; 1424 } 1425 1426 Cursor cursor = phone.getContext().getContentResolver().query( 1427 PREFERAPN_URI, new String[] { "_id", "name", "apn" }, 1428 null, null, Telephony.Carriers.DEFAULT_SORT_ORDER); 1429 1430 if (cursor != null) { 1431 canSetPreferApn = true; 1432 } else { 1433 canSetPreferApn = false; 1434 } 1435 1436 if (canSetPreferApn && cursor.getCount() > 0) { 1437 int pos; 1438 cursor.moveToFirst(); 1439 pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)); 1440 for(ApnSetting p:allApns) { 1441 if (p.id == pos && p.canHandleType(mRequestedApnType)) { 1442 cursor.close(); 1443 return p; 1444 } 1445 } 1446 } 1447 1448 if (cursor != null) { 1449 cursor.close(); 1450 } 1451 1452 return null; 1453 } 1454 1455 public void handleMessage (Message msg) { 1456 if (DBG) Log.d(LOG_TAG,"GSMDataConnTrack handleMessage "+msg); 1457 1458 if (!mGsmPhone.mIsTheCurrentActivePhone) { 1459 Log.d(LOG_TAG, "Ignore GSM msgs since GSM phone is inactive"); 1460 return; 1461 } 1462 1463 switch (msg.what) { 1464 case EVENT_RECORDS_LOADED: 1465 onRecordsLoaded(); 1466 break; 1467 1468 case EVENT_GPRS_DETACHED: 1469 onGprsDetached(); 1470 break; 1471 1472 case EVENT_GPRS_ATTACHED: 1473 onGprsAttached(); 1474 break; 1475 1476 case EVENT_DATA_STATE_CHANGED: 1477 onPdpStateChanged((AsyncResult) msg.obj, false); 1478 break; 1479 1480 case EVENT_GET_PDP_LIST_COMPLETE: 1481 onPdpStateChanged((AsyncResult) msg.obj, true); 1482 break; 1483 1484 case EVENT_POLL_PDP: 1485 onPollPdp(); 1486 break; 1487 1488 case EVENT_START_NETSTAT_POLL: 1489 mPingTestActive = false; 1490 startNetStatPoll(); 1491 break; 1492 1493 case EVENT_START_RECOVERY: 1494 mPingTestActive = false; 1495 doRecovery(); 1496 break; 1497 1498 case EVENT_APN_CHANGED: 1499 onApnChanged(); 1500 break; 1501 1502 case EVENT_PS_RESTRICT_ENABLED: 1503 /** 1504 * We don't need to explicitly to tear down the PDP context 1505 * when PS restricted is enabled. The base band will deactive 1506 * PDP context and notify us with PDP_CONTEXT_CHANGED. 1507 * But we should stop the network polling and prevent reset PDP. 1508 */ 1509 Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted); 1510 stopNetStatPoll(); 1511 mIsPsRestricted = true; 1512 break; 1513 1514 case EVENT_PS_RESTRICT_DISABLED: 1515 /** 1516 * When PS restrict is removed, we need setup PDP connection if 1517 * PDP connection is down. 1518 */ 1519 Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted); 1520 mIsPsRestricted = false; 1521 if (state == State.CONNECTED) { 1522 startNetStatPoll(); 1523 } else { 1524 if (state == State.FAILED) { 1525 cleanUpConnection(false, Phone.REASON_PS_RESTRICT_ENABLED); 1526 mRetryMgr.resetRetryCount(); 1527 mReregisterOnReconnectFailure = false; 1528 } 1529 trySetupData(Phone.REASON_PS_RESTRICT_ENABLED); 1530 } 1531 break; 1532 1533 default: 1534 // handle the message in the super class DataConnectionTracker 1535 super.handleMessage(msg); 1536 break; 1537 } 1538 } 1539 1540 protected void log(String s) { 1541 Log.d(LOG_TAG, "[GsmDataConnectionTracker] " + s); 1542 } 1543 1544} 1545