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