GsmDataConnectionTracker.java revision 4c89b14db431dc1c9c2c8d8f14b88f991a29ab62
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.ConnectivityManager; 31import android.net.IConnectivityManager; 32import android.net.NetworkInfo; 33import android.net.TrafficStats; 34import android.net.Uri; 35import android.net.wifi.WifiManager; 36import android.os.AsyncResult; 37import android.os.Message; 38import android.os.RemoteException; 39import android.os.ServiceManager; 40import android.os.SystemClock; 41import android.os.SystemProperties; 42import android.preference.PreferenceManager; 43import android.provider.Settings; 44import android.provider.Telephony; 45import android.telephony.ServiceState; 46import android.telephony.TelephonyManager; 47import android.telephony.gsm.GsmCellLocation; 48import android.util.EventLog; 49import android.util.Log; 50 51import com.android.internal.R; 52import com.android.internal.telephony.DataCallState; 53import com.android.internal.telephony.DataConnection; 54import com.android.internal.telephony.DataConnectionTracker; 55import com.android.internal.telephony.Phone; 56import com.android.internal.telephony.RetryManager; 57import com.android.internal.telephony.EventLogTags; 58import com.android.internal.telephony.DataConnection.FailCause; 59 60import java.io.IOException; 61import java.util.ArrayList; 62 63/** 64 * {@hide} 65 */ 66public final class GsmDataConnectionTracker extends DataConnectionTracker { 67 protected final String LOG_TAG = "GSM"; 68 69 private GSMPhone mGsmPhone; 70 /** 71 * Handles changes to the APN db. 72 */ 73 private class ApnChangeObserver extends ContentObserver { 74 public ApnChangeObserver () { 75 super(mDataConnectionTracker); 76 } 77 78 @Override 79 public void onChange(boolean selfChange) { 80 sendMessage(obtainMessage(EVENT_APN_CHANGED)); 81 } 82 } 83 84 //***** Instance Variables 85 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 private int waitingApnsPermanentFailureCountDown = 0; 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 DataConnection */ 130 private GsmDataConnection 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 DataConnection 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 retries on the default APN 154 private RetryManager mDefaultRetryManager; 155 // for tracking retries 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 disabled, the NETWORK_STATE_CHANGED_ACTION 193 // quit and won't report disconnected til next enabling. 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 IntentFilter filter = new IntentFilter(); 222 filter.addAction(INTENT_RECONNECT_ALARM); 223 filter.addAction(Intent.ACTION_SCREEN_ON); 224 filter.addAction(Intent.ACTION_SCREEN_OFF); 225 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 226 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 227 228 // TODO: Why is this registering the phone as the receiver of the intent 229 // and not its own handler? 230 p.getContext().registerReceiver(mIntentReceiver, filter, null, p); 231 232 233 mDataConnectionTracker = this; 234 mResolver = phone.getContext().getContentResolver(); 235 236 apnObserver = new ApnChangeObserver(); 237 p.getContext().getContentResolver().registerContentObserver( 238 Telephony.Carriers.CONTENT_URI, true, apnObserver); 239 240 createAllPdpList(); 241 242 // This preference tells us 1) initial condition for "dataEnabled", 243 // and 2) whether the RIL will setup the baseband to auto-PS attach. 244 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext()); 245 boolean dataEnabledSetting = true; 246 try { 247 dataEnabledSetting = IConnectivityManager.Stub.asInterface(ServiceManager. 248 getService(Context.CONNECTIVITY_SERVICE)).getMobileDataEnabled(); 249 } catch (Exception e) { 250 // nothing to do - use the old behavior and leave data on 251 } 252 dataEnabled[APN_DEFAULT_ID] = !sp.getBoolean(GSMPhone.DATA_DISABLED_ON_BOOT_KEY, false) && 253 dataEnabledSetting; 254 if (dataEnabled[APN_DEFAULT_ID]) { 255 enabledCount++; 256 } 257 noAutoAttach = !dataEnabled[APN_DEFAULT_ID]; 258 259 if (!mRetryMgr.configure(SystemProperties.get("ro.gsm.data_retry_config"))) { 260 if (!mRetryMgr.configure(DEFAULT_DATA_RETRY_CONFIG)) { 261 // Should never happen, log an error and default to a simple linear sequence. 262 Log.e(LOG_TAG, "Could not configure using DEFAULT_DATA_RETRY_CONFIG=" 263 + DEFAULT_DATA_RETRY_CONFIG); 264 mRetryMgr.configure(20, 2000, 1000); 265 } 266 } 267 268 mDefaultRetryManager = mRetryMgr; 269 mSecondaryRetryManager = new RetryManager(); 270 271 if (!mSecondaryRetryManager.configure(SystemProperties.get( 272 "ro.gsm.2nd_data_retry_config"))) { 273 if (!mSecondaryRetryManager.configure(SECONDARY_DATA_RETRY_CONFIG)) { 274 // Should never happen, log an error and default to a simple sequence. 275 Log.e(LOG_TAG, "Could note configure using SECONDARY_DATA_RETRY_CONFIG=" 276 + SECONDARY_DATA_RETRY_CONFIG); 277 mSecondaryRetryManager.configure("max_retries=3, 333, 333, 333"); 278 } 279 } 280 } 281 282 public void dispose() { 283 //Unregister for all events 284 phone.mCM.unregisterForAvailable(this); 285 phone.mCM.unregisterForOffOrNotAvailable(this); 286 mGsmPhone.mSIMRecords.unregisterForRecordsLoaded(this); 287 phone.mCM.unregisterForDataStateChanged(this); 288 mGsmPhone.mCT.unregisterForVoiceCallEnded(this); 289 mGsmPhone.mCT.unregisterForVoiceCallStarted(this); 290 mGsmPhone.mSST.unregisterForGprsAttached(this); 291 mGsmPhone.mSST.unregisterForGprsDetached(this); 292 mGsmPhone.mSST.unregisterForRoamingOn(this); 293 mGsmPhone.mSST.unregisterForRoamingOff(this); 294 mGsmPhone.mSST.unregisterForPsRestrictedEnabled(this); 295 mGsmPhone.mSST.unregisterForPsRestrictedDisabled(this); 296 297 phone.getContext().unregisterReceiver(this.mIntentReceiver); 298 phone.getContext().getContentResolver().unregisterContentObserver(this.apnObserver); 299 300 destroyAllPdpList(); 301 } 302 303 protected void finalize() { 304 if(DBG) Log.d(LOG_TAG, "GsmDataConnectionTracker finalized"); 305 } 306 307 protected void setState(State s) { 308 if (DBG) log ("setState: " + s); 309 if (state != s) { 310 EventLog.writeEvent(EventLogTags.GSM_DATA_STATE_CHANGE, state.toString(), s.toString()); 311 state = s; 312 } 313 314 if (state == State.FAILED) { 315 if (waitingApns != null) 316 waitingApns.clear(); // when teardown the connection and set to IDLE 317 } 318 } 319 320 public String[] getActiveApnTypes() { 321 String[] result; 322 if (mActiveApn != null) { 323 result = mActiveApn.types; 324 } else { 325 result = new String[1]; 326 result[0] = Phone.APN_TYPE_DEFAULT; 327 } 328 return result; 329 } 330 331 protected String getActiveApnString() { 332 String result = null; 333 if (mActiveApn != null) { 334 result = mActiveApn.apn; 335 } 336 return result; 337 } 338 339 /** 340 * The data connection is expected to be setup while device 341 * 1. has sim card 342 * 2. registered to gprs service 343 * 3. user doesn't explicitly disable data service 344 * 4. wifi is not on 345 * 346 * @return false while no data connection if all above requirements are met. 347 */ 348 public boolean isDataConnectionAsDesired() { 349 boolean roaming = phone.getServiceState().getRoaming(); 350 351 if (mGsmPhone.mSIMRecords.getRecordsLoaded() && 352 mGsmPhone.mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE && 353 (!roaming || getDataOnRoamingEnabled()) && 354 !mIsWifiConnected && 355 !mIsPsRestricted ) { 356 return (state == State.CONNECTED); 357 } 358 return true; 359 } 360 361 @Override 362 protected boolean isApnTypeActive(String type) { 363 // TODO: support simultaneous with List instead 364 if (Phone.APN_TYPE_DUN.equals(type)) { 365 ApnSetting dunApn = fetchDunApn(); 366 if (dunApn != null) { 367 return ((mActiveApn != null) && (dunApn.toString().equals(mActiveApn.toString()))); 368 } 369 } 370 return mActiveApn != null && mActiveApn.canHandleType(type); 371 } 372 373 @Override 374 protected boolean isApnTypeAvailable(String type) { 375 if (type.equals(Phone.APN_TYPE_DUN)) { 376 return (fetchDunApn() != null); 377 } 378 379 if (allApns != null) { 380 for (ApnSetting apn : allApns) { 381 if (apn.canHandleType(type)) { 382 return true; 383 } 384 } 385 } 386 return false; 387 } 388 389 /** 390 * Formerly this method was ArrayList<GsmDataConnection> getAllPdps() 391 */ 392 public ArrayList<DataConnection> getAllDataConnections() { 393 ArrayList<DataConnection> pdps = (ArrayList<DataConnection>)pdpList.clone(); 394 return pdps; 395 } 396 397 private boolean isDataAllowed() { 398 boolean roaming = phone.getServiceState().getRoaming(); 399 return getAnyDataEnabled() && (!roaming || getDataOnRoamingEnabled()) && 400 mMasterDataEnabled; 401 } 402 403 //****** Called from ServiceStateTracker 404 /** 405 * Invoked when ServiceStateTracker observes a transition from GPRS 406 * attach to detach. 407 */ 408 protected void onGprsDetached() { 409 /* 410 * We presently believe it is unnecessary to tear down the PDP context 411 * when GPRS detaches, but we should stop the network polling. 412 */ 413 stopNetStatPoll(); 414 phone.notifyDataConnection(Phone.REASON_GPRS_DETACHED); 415 } 416 417 private void onGprsAttached() { 418 if (state == State.CONNECTED) { 419 startNetStatPoll(); 420 phone.notifyDataConnection(Phone.REASON_GPRS_ATTACHED); 421 } else { 422 if (state == State.FAILED) { 423 cleanUpConnection(false, Phone.REASON_GPRS_ATTACHED); 424 mRetryMgr.resetRetryCount(); 425 } 426 trySetupData(Phone.REASON_GPRS_ATTACHED); 427 } 428 } 429 430 private boolean trySetupData(String reason) { 431 if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason)); 432 433 Log.d(LOG_TAG, "[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted); 434 435 if (phone.getSimulatedRadioControl() != null) { 436 // Assume data is connected on the simulator 437 // FIXME this can be improved 438 setState(State.CONNECTED); 439 phone.notifyDataConnection(reason); 440 441 Log.i(LOG_TAG, "(fix?) We're on the simulator; assuming data is connected"); 442 return true; 443 } 444 445 int gprsState = mGsmPhone.mSST.getCurrentGprsState(); 446 boolean desiredPowerState = mGsmPhone.mSST.getDesiredPowerState(); 447 448 if ((state == State.IDLE || state == State.SCANNING) 449 && (gprsState == ServiceState.STATE_IN_SERVICE || noAutoAttach) 450 && mGsmPhone.mSIMRecords.getRecordsLoaded() 451 && phone.getState() == Phone.State.IDLE 452 && isDataAllowed() 453 && !mIsPsRestricted 454 && desiredPowerState ) { 455 456 if (state == State.IDLE) { 457 waitingApns = buildWaitingApns(); 458 waitingApnsPermanentFailureCountDown = waitingApns.size(); 459 if (waitingApns.isEmpty()) { 460 if (DBG) log("No APN found"); 461 notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN); 462 return false; 463 } else { 464 log ("Create from allApns : " + apnListToString(allApns)); 465 } 466 } 467 468 if (DBG) { 469 log ("Setup waitngApns : " + apnListToString(waitingApns)); 470 } 471 return setupData(reason); 472 } else { 473 if (DBG) 474 log("trySetupData: Not ready for data: " + 475 " dataState=" + state + 476 " gprsState=" + gprsState + 477 " sim=" + mGsmPhone.mSIMRecords.getRecordsLoaded() + 478 " UMTS=" + mGsmPhone.mSST.isConcurrentVoiceAndData() + 479 " phoneState=" + phone.getState() + 480 " isDataAllowed=" + isDataAllowed() + 481 " dataEnabled=" + getAnyDataEnabled() + 482 " roaming=" + phone.getServiceState().getRoaming() + 483 " dataOnRoamingEnable=" + getDataOnRoamingEnabled() + 484 " ps restricted=" + mIsPsRestricted + 485 " desiredPowerState=" + desiredPowerState + 486 " MasterDataEnabled=" + mMasterDataEnabled); 487 return false; 488 } 489 } 490 491 /** 492 * If tearDown is true, this only tears down a CONNECTED session. Presently, 493 * there is no mechanism for abandoning an INITING/CONNECTING session, 494 * but would likely involve cancelling pending async requests or 495 * setting a flag or new state to ignore them when they came in 496 * @param tearDown true if the underlying GsmDataConnection should be 497 * disconnected. 498 * @param reason reason for the clean up. 499 */ 500 private void cleanUpConnection(boolean tearDown, String reason) { 501 if (DBG) log("Clean up connection due to " + reason); 502 503 // Clear the reconnect alarm, if set. 504 if (mReconnectIntent != null) { 505 AlarmManager am = 506 (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); 507 am.cancel(mReconnectIntent); 508 mReconnectIntent = null; 509 } 510 511 setState(State.DISCONNECTING); 512 513 boolean notificationDeferred = false; 514 for (DataConnection conn : pdpList) { 515 if (tearDown) { 516 if (DBG) log("cleanUpConnection: teardown, call conn.disconnect"); 517 conn.disconnect(obtainMessage(EVENT_DISCONNECT_DONE, reason)); 518 notificationDeferred = true; 519 } else { 520 if (DBG) log("cleanUpConnection: !tearDown, call conn.resetSynchronously"); 521 conn.resetSynchronously(); 522 notificationDeferred = false; 523 } 524 } 525 stopNetStatPoll(); 526 527 if (!notificationDeferred) { 528 if (DBG) log("cleanupConnection: !notificationDeferred"); 529 gotoIdleAndNotifyDataConnection(reason); 530 } 531 } 532 533 /** 534 * @param types comma delimited list of APN types 535 * @return array of APN types 536 */ 537 private String[] parseTypes(String types) { 538 String[] result; 539 // If unset, set to DEFAULT. 540 if (types == null || types.equals("")) { 541 result = new String[1]; 542 result[0] = Phone.APN_TYPE_ALL; 543 } else { 544 result = types.split(","); 545 } 546 return result; 547 } 548 549 private ArrayList<ApnSetting> createApnList(Cursor cursor) { 550 ArrayList<ApnSetting> result = new ArrayList<ApnSetting>(); 551 if (cursor.moveToFirst()) { 552 do { 553 String[] types = parseTypes( 554 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE))); 555 ApnSetting apn = new ApnSetting( 556 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)), 557 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)), 558 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)), 559 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)), 560 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY)), 561 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)), 562 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC)), 563 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY)), 564 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)), 565 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)), 566 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)), 567 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)), 568 types, 569 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)), 570 cursor.getString(cursor.getColumnIndexOrThrow( 571 Telephony.Carriers.ROAMING_PROTOCOL))); 572 result.add(apn); 573 } while (cursor.moveToNext()); 574 } 575 return result; 576 } 577 578 private GsmDataConnection findFreePdp() { 579 for (DataConnection conn : pdpList) { 580 GsmDataConnection pdp = (GsmDataConnection) conn; 581 if (pdp.isInactive()) { 582 return pdp; 583 } 584 } 585 return null; 586 } 587 588 private boolean setupData(String reason) { 589 ApnSetting apn; 590 GsmDataConnection pdp; 591 592 apn = getNextApn(); 593 if (apn == null) return false; 594 pdp = findFreePdp(); 595 if (pdp == null) { 596 if (DBG) log("setupData: No free GsmDataConnection found!"); 597 return false; 598 } 599 mActiveApn = apn; 600 mActivePdp = pdp; 601 602 Message msg = obtainMessage(); 603 msg.what = EVENT_DATA_SETUP_COMPLETE; 604 msg.obj = reason; 605 pdp.connect(msg, apn); 606 607 setState(State.INITING); 608 phone.notifyDataConnection(reason); 609 return true; 610 } 611 612 protected String getInterfaceName(String apnType) { 613 if (mActivePdp != null && 614 (apnType == null || 615 (mActiveApn != null && mActiveApn.canHandleType(apnType)))) { 616 return mActivePdp.getInterface(); 617 } 618 return null; 619 } 620 621 protected String getIpAddress(String apnType) { 622 if (mActivePdp != null && 623 (apnType == null || 624 (mActiveApn != null && mActiveApn.canHandleType(apnType)))) { 625 return mActivePdp.getIpAddress(); 626 } 627 return null; 628 } 629 630 public String getGateway(String apnType) { 631 if (mActivePdp != null && 632 (apnType == null || 633 (mActiveApn != null && mActiveApn.canHandleType(apnType)))) { 634 return mActivePdp.getGatewayAddress(); 635 } 636 return null; 637 } 638 639 protected String[] getDnsServers(String apnType) { 640 if (mActivePdp != null && 641 (apnType == null || 642 (mActiveApn != null && mActiveApn.canHandleType(apnType)))) { 643 return mActivePdp.getDnsServers(); 644 } 645 return null; 646 } 647 648 private boolean 649 pdpStatesHasCID (ArrayList<DataCallState> states, int cid) { 650 for (int i = 0, s = states.size() ; i < s ; i++) { 651 if (states.get(i).cid == cid) return true; 652 } 653 654 return false; 655 } 656 657 private boolean 658 pdpStatesHasActiveCID (ArrayList<DataCallState> states, int cid) { 659 for (int i = 0, s = states.size() ; i < s ; i++) { 660 if ((states.get(i).cid == cid) && (states.get(i).active != 0)) { 661 return true; 662 } 663 } 664 665 return false; 666 } 667 668 /** 669 * Handles changes to the APN database. 670 */ 671 private void onApnChanged() { 672 boolean isConnected; 673 674 isConnected = (state != State.IDLE && state != State.FAILED); 675 676 // The "current" may no longer be valid. MMS depends on this to send properly. 677 mGsmPhone.updateCurrentCarrierInProvider(); 678 679 // TODO: It'd be nice to only do this if the changed entrie(s) 680 // match the current operator. 681 createAllApnList(); 682 if (state != State.DISCONNECTING) { 683 cleanUpConnection(isConnected, Phone.REASON_APN_CHANGED); 684 if (!isConnected) { 685 // reset reconnect timer 686 mRetryMgr.resetRetryCount(); 687 mReregisterOnReconnectFailure = false; 688 trySetupData(Phone.REASON_APN_CHANGED); 689 } 690 } 691 } 692 693 /** 694 * @param explicitPoll if true, indicates that *we* polled for this 695 * update while state == CONNECTED rather than having it delivered 696 * via an unsolicited response (which could have happened at any 697 * previous state 698 */ 699 protected void onPdpStateChanged (AsyncResult ar, boolean explicitPoll) { 700 ArrayList<DataCallState> pdpStates; 701 702 pdpStates = (ArrayList<DataCallState>)(ar.result); 703 704 if (ar.exception != null) { 705 // This is probably "radio not available" or something 706 // of that sort. If so, the whole connection is going 707 // to come down soon anyway 708 return; 709 } 710 711 if (state == State.CONNECTED) { 712 // The way things are supposed to work, the PDP list 713 // should not contain the CID after it disconnects. 714 // However, the way things really work, sometimes the PDP 715 // context is still listed with active = false, which 716 // makes it hard to distinguish an activating context from 717 // an activated-and-then deactivated one. 718 if (!pdpStatesHasCID(pdpStates, cidActive)) { 719 // It looks like the PDP context has deactivated. 720 // Tear everything down and try to reconnect. 721 722 Log.i(LOG_TAG, "PDP connection has dropped. Reconnecting"); 723 724 // Add an event log when the network drops PDP 725 GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); 726 EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, 727 loc != null ? loc.getCid() : -1, 728 TelephonyManager.getDefault().getNetworkType()); 729 730 cleanUpConnection(true, null); 731 return; 732 } else if (!pdpStatesHasActiveCID(pdpStates, cidActive)) { 733 // Here, we only consider this authoritative if we asked for the 734 // PDP list. If it was an unsolicited response, we poll again 735 // to make sure everyone agrees on the initial state. 736 737 if (!explicitPoll) { 738 // We think it disconnected but aren't sure...poll from our side 739 phone.mCM.getPDPContextList( 740 this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); 741 } else { 742 Log.i(LOG_TAG, "PDP connection has dropped (active=false case). " 743 + " Reconnecting"); 744 745 // Log the network drop on the event log. 746 GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); 747 EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, 748 loc != null ? loc.getCid() : -1, 749 TelephonyManager.getDefault().getNetworkType()); 750 751 cleanUpConnection(true, null); 752 } 753 } 754 } 755 } 756 757 private void notifyDefaultData(String reason) { 758 setState(State.CONNECTED); 759 phone.notifyDataConnection(reason); 760 startNetStatPoll(); 761 // reset reconnect timer 762 mRetryMgr.resetRetryCount(); 763 mReregisterOnReconnectFailure = false; 764 } 765 766 private void gotoIdleAndNotifyDataConnection(String reason) { 767 if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason); 768 setState(State.IDLE); 769 phone.notifyDataConnection(reason); 770 mActiveApn = null; 771 } 772 773 /** 774 * This is a kludge to deal with the fact that 775 * the PDP state change notification doesn't always work 776 * with certain RIL impl's/basebands 777 * 778 */ 779 private void startPeriodicPdpPoll() { 780 removeMessages(EVENT_POLL_PDP); 781 782 sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS); 783 } 784 785 private void resetPollStats() { 786 txPkts = -1; 787 rxPkts = -1; 788 sentSinceLastRecv = 0; 789 netStatPollPeriod = POLL_NETSTAT_MILLIS; 790 mNoRecvPollCount = 0; 791 } 792 793 private void doRecovery() { 794 if (state == State.CONNECTED) { 795 int maxPdpReset = Settings.Secure.getInt(mResolver, 796 Settings.Secure.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT, 797 DEFAULT_MAX_PDP_RESET_FAIL); 798 if (mPdpResetCount < maxPdpReset) { 799 mPdpResetCount++; 800 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, sentSinceLastRecv); 801 cleanUpConnection(true, Phone.REASON_PDP_RESET); 802 } else { 803 mPdpResetCount = 0; 804 EventLog.writeEvent(EventLogTags.PDP_REREGISTER_NETWORK, sentSinceLastRecv); 805 mGsmPhone.mSST.reRegisterNetwork(null); 806 } 807 // TODO: Add increasingly drastic recovery steps, eg, 808 // reset the radio, reset the device. 809 } 810 } 811 812 protected void startNetStatPoll() { 813 if (state == State.CONNECTED && mPingTestActive == false && netStatPollEnabled == false) { 814 Log.d(LOG_TAG, "[DataConnection] Start poll NetStat"); 815 resetPollStats(); 816 netStatPollEnabled = true; 817 mPollNetStat.run(); 818 } 819 } 820 821 protected void stopNetStatPoll() { 822 netStatPollEnabled = false; 823 removeCallbacks(mPollNetStat); 824 Log.d(LOG_TAG, "[DataConnection] Stop poll NetStat"); 825 } 826 827 protected void restartRadio() { 828 Log.d(LOG_TAG, "************TURN OFF RADIO**************"); 829 cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF); 830 mGsmPhone.mSST.powerOffRadioSafely(); 831 /* Note: no need to call setRadioPower(true). Assuming the desired 832 * radio power state is still ON (as tracked by ServiceStateTracker), 833 * ServiceStateTracker will call setRadioPower when it receives the 834 * RADIO_STATE_CHANGED notification for the power off. And if the 835 * desired power state has changed in the interim, we don't want to 836 * override it with an unconditional power on. 837 */ 838 839 int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0")); 840 SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1)); 841 } 842 843 private Runnable mPollNetStat = new Runnable() 844 { 845 846 public void run() { 847 long sent, received; 848 long preTxPkts = -1, preRxPkts = -1; 849 850 Activity newActivity; 851 852 preTxPkts = txPkts; 853 preRxPkts = rxPkts; 854 855 txPkts = TrafficStats.getMobileTxPackets(); 856 rxPkts = TrafficStats.getMobileRxPackets(); 857 858 //Log.d(LOG_TAG, "rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts)); 859 860 if (netStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) { 861 sent = txPkts - preTxPkts; 862 received = rxPkts - preRxPkts; 863 864 if ( sent > 0 && received > 0 ) { 865 sentSinceLastRecv = 0; 866 newActivity = Activity.DATAINANDOUT; 867 mPdpResetCount = 0; 868 } else if (sent > 0 && received == 0) { 869 if (phone.getState() == Phone.State.IDLE) { 870 sentSinceLastRecv += sent; 871 } else { 872 sentSinceLastRecv = 0; 873 } 874 newActivity = Activity.DATAOUT; 875 } else if (sent == 0 && received > 0) { 876 sentSinceLastRecv = 0; 877 newActivity = Activity.DATAIN; 878 mPdpResetCount = 0; 879 } else if (sent == 0 && received == 0) { 880 newActivity = Activity.NONE; 881 } else { 882 sentSinceLastRecv = 0; 883 newActivity = Activity.NONE; 884 } 885 886 if (activity != newActivity && mIsScreenOn) { 887 activity = newActivity; 888 phone.notifyDataActivity(); 889 } 890 } 891 892 int watchdogTrigger = Settings.Secure.getInt(mResolver, 893 Settings.Secure.PDP_WATCHDOG_TRIGGER_PACKET_COUNT, 894 NUMBER_SENT_PACKETS_OF_HANG); 895 896 if (sentSinceLastRecv >= watchdogTrigger) { 897 // we already have NUMBER_SENT_PACKETS sent without ack 898 if (mNoRecvPollCount == 0) { 899 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET_COUNTDOWN_TRIGGERED, 900 sentSinceLastRecv); 901 } 902 903 int noRecvPollLimit = Settings.Secure.getInt(mResolver, 904 Settings.Secure.PDP_WATCHDOG_ERROR_POLL_COUNT, NO_RECV_POLL_LIMIT); 905 906 if (mNoRecvPollCount < noRecvPollLimit) { 907 // It's possible the PDP context went down and we weren't notified. 908 // Start polling the context list in an attempt to recover. 909 if (DBG) log("no DATAIN in a while; polling PDP"); 910 phone.mCM.getDataCallList(obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); 911 912 mNoRecvPollCount++; 913 914 // Slow down the poll interval to let things happen 915 netStatPollPeriod = Settings.Secure.getInt(mResolver, 916 Settings.Secure.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS, 917 POLL_NETSTAT_SLOW_MILLIS); 918 } else { 919 if (DBG) log("Sent " + String.valueOf(sentSinceLastRecv) + 920 " pkts since last received"); 921 // We've exceeded the threshold. Run ping test as a final check; 922 // it will proceed with recovery if ping fails. 923 stopNetStatPoll(); 924 Thread pingTest = new Thread() { 925 public void run() { 926 runPingTest(); 927 } 928 }; 929 mPingTestActive = true; 930 pingTest.start(); 931 } 932 } else { 933 mNoRecvPollCount = 0; 934 if (mIsScreenOn) { 935 netStatPollPeriod = Settings.Secure.getInt(mResolver, 936 Settings.Secure.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS); 937 } else { 938 netStatPollPeriod = Settings.Secure.getInt(mResolver, 939 Settings.Secure.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS, 940 POLL_NETSTAT_SCREEN_OFF_MILLIS); 941 } 942 } 943 944 if (netStatPollEnabled) { 945 mDataConnectionTracker.postDelayed(this, netStatPollPeriod); 946 } 947 } 948 }; 949 950 private void runPingTest () { 951 int status = -1; 952 try { 953 String address = Settings.Secure.getString(mResolver, 954 Settings.Secure.PDP_WATCHDOG_PING_ADDRESS); 955 int deadline = Settings.Secure.getInt(mResolver, 956 Settings.Secure.PDP_WATCHDOG_PING_DEADLINE, DEFAULT_PING_DEADLINE); 957 if (DBG) log("pinging " + address + " for " + deadline + "s"); 958 if (address != null && !NULL_IP.equals(address)) { 959 Process p = Runtime.getRuntime() 960 .exec("ping -c 1 -i 1 -w "+ deadline + " " + address); 961 status = p.waitFor(); 962 } 963 } catch (IOException e) { 964 Log.w(LOG_TAG, "ping failed: IOException"); 965 } catch (Exception e) { 966 Log.w(LOG_TAG, "exception trying to ping"); 967 } 968 969 if (status == 0) { 970 // ping succeeded. False alarm. Reset netStatPoll. 971 // ("-1" for this event indicates a false alarm) 972 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, -1); 973 mPdpResetCount = 0; 974 sendMessage(obtainMessage(EVENT_START_NETSTAT_POLL)); 975 } else { 976 // ping failed. Proceed with recovery. 977 sendMessage(obtainMessage(EVENT_START_RECOVERY)); 978 } 979 } 980 981 /** 982 * Returns true if the last fail cause is something that 983 * seems like it deserves an error notification. 984 * Transient errors are ignored 985 */ 986 private boolean shouldPostNotification(GsmDataConnection.FailCause cause) { 987 return (cause != GsmDataConnection.FailCause.UNKNOWN); 988 } 989 990 /** 991 * Return true if data connection need to be setup after disconnected due to 992 * reason. 993 * 994 * @param reason the reason why data is disconnected 995 * @return true if try setup data connection is need for this reason 996 */ 997 private boolean retryAfterDisconnected(String reason) { 998 boolean retry = true; 999 1000 if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) { 1001 retry = false; 1002 } 1003 return retry; 1004 } 1005 1006 private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) { 1007 if (state == State.FAILED) { 1008 if (!mRetryMgr.isRetryNeeded()) { 1009 if (!mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { 1010 // if no more retries on a secondary APN attempt, tell the world and revert. 1011 phone.notifyDataConnection(Phone.REASON_APN_FAILED); 1012 onEnableApn(apnTypeToId(mRequestedApnType), DISABLED); 1013 return; 1014 } 1015 if (mReregisterOnReconnectFailure) { 1016 // We've re-registerd once now just retry forever. 1017 mRetryMgr.retryForeverUsingLastTimeout(); 1018 } else { 1019 // Try to re-register to the network. 1020 Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network"); 1021 mReregisterOnReconnectFailure = true; 1022 mGsmPhone.mSST.reRegisterNetwork(null); 1023 mRetryMgr.resetRetryCount(); 1024 return; 1025 } 1026 } 1027 1028 int nextReconnectDelay = mRetryMgr.getRetryTimer(); 1029 Log.d(LOG_TAG, "PDP activate failed. Scheduling next attempt for " 1030 + (nextReconnectDelay / 1000) + "s"); 1031 1032 AlarmManager am = 1033 (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); 1034 Intent intent = new Intent(INTENT_RECONNECT_ALARM); 1035 intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason); 1036 mReconnectIntent = PendingIntent.getBroadcast( 1037 phone.getContext(), 0, intent, 0); 1038 am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1039 SystemClock.elapsedRealtime() + nextReconnectDelay, 1040 mReconnectIntent); 1041 1042 mRetryMgr.increaseRetryCount(); 1043 1044 if (!shouldPostNotification(lastFailCauseCode)) { 1045 Log.d(LOG_TAG,"NOT Posting GPRS Unavailable notification " 1046 + "-- likely transient error"); 1047 } else { 1048 notifyNoData(lastFailCauseCode); 1049 } 1050 } 1051 } 1052 1053 private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode) { 1054 setState(State.FAILED); 1055 } 1056 1057 protected void onRecordsLoaded() { 1058 createAllApnList(); 1059 if (state == State.FAILED) { 1060 cleanUpConnection(false, null); 1061 } 1062 sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED)); 1063 } 1064 1065 @Override 1066 protected void onEnableNewApn() { 1067 // change our retry manager to use the appropriate numbers for the new APN 1068 if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { 1069 mRetryMgr = mDefaultRetryManager; 1070 } else { 1071 mRetryMgr = mSecondaryRetryManager; 1072 } 1073 mRetryMgr.resetRetryCount(); 1074 1075 // TODO: To support simultaneous PDP contexts, this should really only call 1076 // cleanUpConnection if it needs to free up a GsmDataConnection. 1077 cleanUpConnection(true, Phone.REASON_APN_SWITCHED); 1078 } 1079 1080 protected boolean onTrySetupData(String reason) { 1081 return trySetupData(reason); 1082 } 1083 1084 @Override 1085 protected void onRoamingOff() { 1086 trySetupData(Phone.REASON_ROAMING_OFF); 1087 } 1088 1089 @Override 1090 protected void onRoamingOn() { 1091 if (getDataOnRoamingEnabled()) { 1092 trySetupData(Phone.REASON_ROAMING_ON); 1093 } else { 1094 if (DBG) log("Tear down data connection on roaming."); 1095 cleanUpConnection(true, Phone.REASON_ROAMING_ON); 1096 } 1097 } 1098 1099 protected void onRadioAvailable() { 1100 if (phone.getSimulatedRadioControl() != null) { 1101 // Assume data is connected on the simulator 1102 // FIXME this can be improved 1103 setState(State.CONNECTED); 1104 phone.notifyDataConnection(null); 1105 1106 Log.i(LOG_TAG, "We're on the simulator; assuming data is connected"); 1107 } 1108 1109 if (state != State.IDLE) { 1110 cleanUpConnection(true, null); 1111 } 1112 } 1113 1114 protected void onRadioOffOrNotAvailable() { 1115 // Make sure our reconnect delay starts at the initial value 1116 // next time the radio comes on 1117 mRetryMgr.resetRetryCount(); 1118 mReregisterOnReconnectFailure = false; 1119 1120 if (phone.getSimulatedRadioControl() != null) { 1121 // Assume data is connected on the simulator 1122 // FIXME this can be improved 1123 Log.i(LOG_TAG, "We're on the simulator; assuming radio off is meaningless"); 1124 } else { 1125 if (DBG) log("Radio is off and clean up all connection"); 1126 // TODO: Should we reset mRequestedApnType to "default"? 1127 cleanUpConnection(false, Phone.REASON_RADIO_TURNED_OFF); 1128 } 1129 } 1130 1131 protected void onDataSetupComplete(AsyncResult ar) { 1132 String reason = null; 1133 if (ar.userObj instanceof String) { 1134 reason = (String) ar.userObj; 1135 } 1136 1137 if (ar.exception == null) { 1138 // everything is setup 1139 if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { 1140 SystemProperties.set("gsm.defaultpdpcontext.active", "true"); 1141 if (canSetPreferApn && preferredApn == null) { 1142 Log.d(LOG_TAG, "PREFERRED APN is null"); 1143 preferredApn = mActiveApn; 1144 setPreferredApn(preferredApn.id); 1145 } 1146 } else { 1147 SystemProperties.set("gsm.defaultpdpcontext.active", "false"); 1148 } 1149 notifyDefaultData(reason); 1150 1151 // TODO: For simultaneous PDP support, we need to build another 1152 // trigger another TRY_SETUP_DATA for the next APN type. (Note 1153 // that the existing connection may service that type, in which 1154 // case we should try the next type, etc. 1155 } else { 1156 GsmDataConnection.FailCause cause; 1157 cause = (GsmDataConnection.FailCause) (ar.result); 1158 if(DBG) log("PDP setup failed " + cause); 1159 // Log this failure to the Event Logs. 1160 if (cause.isEventLoggable()) { 1161 GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); 1162 EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL, 1163 cause.ordinal(), loc != null ? loc.getCid() : -1, 1164 TelephonyManager.getDefault().getNetworkType()); 1165 } 1166 1167 // Count permanent failures and remove the APN we just tried 1168 waitingApnsPermanentFailureCountDown -= cause.isPermanentFail() ? 1 : 0; 1169 waitingApns.remove(0); 1170 if (DBG) log(String.format("onDataSetupComplete: waitingApns.size=%d" + 1171 " waitingApnsPermanenatFailureCountDown=%d", 1172 waitingApns.size(), waitingApnsPermanentFailureCountDown)); 1173 1174 // See if there are more APN's to try 1175 if (waitingApns.isEmpty()) { 1176 if (waitingApnsPermanentFailureCountDown == 0) { 1177 if (DBG) log("onDataSetupComplete: Permanent failures stop retrying"); 1178 notifyNoData(cause); 1179 phone.notifyDataConnection(Phone.REASON_APN_FAILED); 1180 } else { 1181 if (DBG) log("onDataSetupComplete: Not all permanent failures, retry"); 1182 startDelayedRetry(cause, reason); 1183 } 1184 } else { 1185 if (DBG) log("onDataSetupComplete: Try next APN"); 1186 setState(State.SCANNING); 1187 // Wait a bit before trying the next APN, so that 1188 // we're not tying up the RIL command channel 1189 sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, reason), APN_DELAY_MILLIS); 1190 } 1191 } 1192 } 1193 1194 /** 1195 * Called when EVENT_DISCONNECT_DONE is received. 1196 */ 1197 protected void onDisconnectDone(AsyncResult ar) { 1198 String reason = null; 1199 if(DBG) log("EVENT_DISCONNECT_DONE"); 1200 if (ar.userObj instanceof String) { 1201 reason = (String) ar.userObj; 1202 } 1203 setState(State.IDLE); 1204 phone.notifyDataConnection(reason); 1205 mActiveApn = null; 1206 if (retryAfterDisconnected(reason)) { 1207 trySetupData(reason); 1208 } 1209 } 1210 1211 /** 1212 * Called when EVENT_RESET_DONE is received. 1213 */ 1214 @Override 1215 protected void onResetDone(AsyncResult ar) { 1216 if (DBG) log("EVENT_RESET_DONE"); 1217 String reason = null; 1218 if (ar.userObj instanceof String) { 1219 reason = (String) ar.userObj; 1220 } 1221 gotoIdleAndNotifyDataConnection(reason); 1222 } 1223 1224 protected void onPollPdp() { 1225 if (state == State.CONNECTED) { 1226 // only poll when connected 1227 phone.mCM.getPDPContextList(this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); 1228 sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS); 1229 } 1230 } 1231 1232 protected void onVoiceCallStarted() { 1233 if (state == State.CONNECTED && ! mGsmPhone.mSST.isConcurrentVoiceAndData()) { 1234 stopNetStatPoll(); 1235 phone.notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED); 1236 } 1237 } 1238 1239 protected void onVoiceCallEnded() { 1240 if (state == State.CONNECTED) { 1241 if (!mGsmPhone.mSST.isConcurrentVoiceAndData()) { 1242 startNetStatPoll(); 1243 phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED); 1244 } else { 1245 // clean slate after call end. 1246 resetPollStats(); 1247 } 1248 } else { 1249 // reset reconnect timer 1250 mRetryMgr.resetRetryCount(); 1251 mReregisterOnReconnectFailure = false; 1252 // in case data setup was attempted when we were on a voice call 1253 trySetupData(Phone.REASON_VOICE_CALL_ENDED); 1254 } 1255 } 1256 1257 protected void onCleanUpConnection(boolean tearDown, String reason) { 1258 cleanUpConnection(tearDown, reason); 1259 } 1260 1261 /** 1262 * Based on the sim operator numeric, create a list for all possible pdps 1263 * with all apns associated with that pdp 1264 * 1265 * 1266 */ 1267 private void createAllApnList() { 1268 allApns = new ArrayList<ApnSetting>(); 1269 String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric(); 1270 1271 if (operator != null) { 1272 String selection = "numeric = '" + operator + "'"; 1273 1274 Cursor cursor = phone.getContext().getContentResolver().query( 1275 Telephony.Carriers.CONTENT_URI, null, selection, null, null); 1276 1277 if (cursor != null) { 1278 if (cursor.getCount() > 0) { 1279 allApns = createApnList(cursor); 1280 // TODO: Figure out where this fits in. This basically just 1281 // writes the pap-secrets file. No longer tied to GsmDataConnection 1282 // object. Not used on current platform (no ppp). 1283 //GsmDataConnection pdp = pdpList.get(pdp_name); 1284 //if (pdp != null && pdp.dataLink != null) { 1285 // pdp.dataLink.setPasswordInfo(cursor); 1286 //} 1287 } 1288 cursor.close(); 1289 } 1290 } 1291 1292 if (allApns.isEmpty()) { 1293 if (DBG) log("No APN found for carrier: " + operator); 1294 preferredApn = null; 1295 notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN); 1296 } else { 1297 preferredApn = getPreferredApn(); 1298 Log.d(LOG_TAG, "Get PreferredAPN"); 1299 if (preferredApn != null && !preferredApn.numeric.equals(operator)) { 1300 preferredApn = null; 1301 setPreferredApn(-1); 1302 } 1303 } 1304 } 1305 1306 private void createAllPdpList() { 1307 pdpList = new ArrayList<DataConnection>(); 1308 DataConnection pdp; 1309 1310 for (int i = 0; i < PDP_CONNECTION_POOL_SIZE; i++) { 1311 pdp = GsmDataConnection.makeDataConnection(mGsmPhone); 1312 pdpList.add(pdp); 1313 } 1314 } 1315 1316 private void destroyAllPdpList() { 1317 if(pdpList != null) { 1318 GsmDataConnection pdp; 1319 pdpList.removeAll(pdpList); 1320 } 1321 } 1322 1323 private ApnSetting fetchDunApn() { 1324 Context c = phone.getContext(); 1325 String apnData = Settings.Secure.getString(c.getContentResolver(), 1326 Settings.Secure.TETHER_DUN_APN); 1327 ApnSetting dunSetting = ApnSetting.fromString(apnData); 1328 if (dunSetting != null) return dunSetting; 1329 1330 apnData = c.getResources().getString(R.string.config_tether_apndata); 1331 return ApnSetting.fromString(apnData); 1332 } 1333 1334 /** 1335 * 1336 * @return waitingApns list to be used to create PDP 1337 * error when waitingApns.isEmpty() 1338 */ 1339 private ArrayList<ApnSetting> buildWaitingApns() { 1340 ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>(); 1341 1342 if (mRequestedApnType.equals(Phone.APN_TYPE_DUN)) { 1343 ApnSetting dun = fetchDunApn(); 1344 if (dun != null) apnList.add(dun); 1345 return apnList; 1346 } 1347 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(GsmDataConnection.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