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