GsmDataConnectionTracker.java revision 01ae863611244b745e47190d3ee8ed79db50b549
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 = getDataRoaming(); 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 private boolean getDataRoaming() { 347 return mGsmPhone.mSST.getDataRoaming(); 348 } 349 350 @Override 351 protected boolean isApnTypeActive(String type) { 352 // TODO: support simultaneous with List instead 353 return mActiveApn != null && mActiveApn.canHandleType(type); 354 } 355 356 @Override 357 protected boolean isApnTypeAvailable(String type) { 358 if (allApns != null) { 359 for (ApnSetting apn : allApns) { 360 if (apn.canHandleType(type)) { 361 return true; 362 } 363 } 364 } 365 return false; 366 } 367 368 /** 369 * Formerly this method was ArrayList<PdpConnection> getAllPdps() 370 */ 371 public ArrayList<DataConnection> getAllDataConnections() { 372 ArrayList<DataConnection> pdps = (ArrayList<DataConnection>)pdpList.clone(); 373 return pdps; 374 } 375 376 private boolean isDataAllowed() { 377 boolean roaming = getDataRoaming(); 378 return getAnyDataEnabled() && (!roaming || getDataOnRoamingEnabled()); 379 } 380 381 //****** Called from ServiceStateTracker 382 /** 383 * Invoked when ServiceStateTracker observes a transition from GPRS 384 * attach to detach. 385 */ 386 protected void onGprsDetached() { 387 /* 388 * We presently believe it is unnecessary to tear down the PDP context 389 * when GPRS detaches, but we should stop the network polling. 390 */ 391 stopNetStatPoll(); 392 phone.notifyDataConnection(Phone.REASON_GPRS_DETACHED); 393 } 394 395 private void onGprsAttached() { 396 if (state == State.CONNECTED) { 397 startNetStatPoll(); 398 phone.notifyDataConnection(Phone.REASON_GPRS_ATTACHED); 399 } else { 400 if (state == State.FAILED) { 401 cleanUpConnection(false, Phone.REASON_GPRS_ATTACHED); 402 mRetryMgr.resetRetryCount(); 403 } 404 trySetupData(Phone.REASON_GPRS_ATTACHED); 405 } 406 } 407 408 private boolean trySetupData(String reason) { 409 if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason)); 410 411 Log.d(LOG_TAG, "[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted); 412 413 if (phone.getSimulatedRadioControl() != null) { 414 // Assume data is connected on the simulator 415 // FIXME this can be improved 416 setState(State.CONNECTED); 417 phone.notifyDataConnection(reason); 418 419 Log.i(LOG_TAG, "(fix?) We're on the simulator; assuming data is connected"); 420 return true; 421 } 422 423 int gprsState = mGsmPhone.mSST.getCurrentGprsState(); 424 boolean roaming = getDataRoaming(); 425 boolean desiredPowerState = mGsmPhone.mSST.getDesiredPowerState(); 426 427 if ((state == State.IDLE || state == State.SCANNING) 428 && (gprsState == ServiceState.STATE_IN_SERVICE || noAutoAttach) 429 && mGsmPhone.mSIMRecords.getRecordsLoaded() 430 && phone.getState() == Phone.State.IDLE 431 && isDataAllowed() 432 && !mIsPsRestricted 433 && desiredPowerState ) { 434 435 if (state == State.IDLE) { 436 waitingApns = buildWaitingApns(); 437 if (waitingApns.isEmpty()) { 438 if (DBG) log("No APN found"); 439 notifyNoData(PdpConnection.FailCause.MISSING_UKNOWN_APN); 440 return false; 441 } else { 442 log ("Create from allApns : " + apnListToString(allApns)); 443 } 444 } 445 446 if (DBG) { 447 log ("Setup waitngApns : " + apnListToString(waitingApns)); 448 } 449 return setupData(reason); 450 } else { 451 if (DBG) 452 log("trySetupData: Not ready for data: " + 453 " dataState=" + state + 454 " gprsState=" + gprsState + 455 " sim=" + mGsmPhone.mSIMRecords.getRecordsLoaded() + 456 " UMTS=" + mGsmPhone.mSST.isConcurrentVoiceAndData() + 457 " phoneState=" + phone.getState() + 458 " isDataAllowed=" + isDataAllowed() + 459 " dataEnabled=" + getAnyDataEnabled() + 460 " roaming=" + roaming + 461 " dataOnRoamingEnable=" + getDataOnRoamingEnabled() + 462 " ps restricted=" + mIsPsRestricted + 463 " desiredPowerState=" + desiredPowerState); 464 return false; 465 } 466 } 467 468 /** 469 * If tearDown is true, this only tears down a CONNECTED session. Presently, 470 * there is no mechanism for abandoning an INITING/CONNECTING session, 471 * but would likely involve cancelling pending async requests or 472 * setting a flag or new state to ignore them when they came in 473 * @param tearDown true if the underlying PdpConnection should be 474 * disconnected. 475 * @param reason reason for the clean up. 476 */ 477 private void cleanUpConnection(boolean tearDown, String reason) { 478 if (DBG) log("Clean up connection due to " + reason); 479 480 // Clear the reconnect alarm, if set. 481 if (mReconnectIntent != null) { 482 AlarmManager am = 483 (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); 484 am.cancel(mReconnectIntent); 485 mReconnectIntent = null; 486 } 487 488 setState(State.DISCONNECTING); 489 490 for (DataConnection conn : pdpList) { 491 PdpConnection pdp = (PdpConnection) conn; 492 if (tearDown) { 493 Message msg = obtainMessage(EVENT_DISCONNECT_DONE, reason); 494 pdp.disconnect(msg); 495 } else { 496 pdp.clearSettings(); 497 } 498 } 499 stopNetStatPoll(); 500 501 if (!tearDown) { 502 setState(State.IDLE); 503 phone.notifyDataConnection(reason); 504 mActiveApn = null; 505 } 506 } 507 508 /** 509 * @param types comma delimited list of APN types 510 * @return array of APN types 511 */ 512 private String[] parseTypes(String types) { 513 String[] result; 514 // If unset, set to DEFAULT. 515 if (types == null || types.equals("")) { 516 result = new String[1]; 517 result[0] = Phone.APN_TYPE_ALL; 518 } else { 519 result = types.split(","); 520 } 521 return result; 522 } 523 524 private ArrayList<ApnSetting> createApnList(Cursor cursor) { 525 ArrayList<ApnSetting> result = new ArrayList<ApnSetting>(); 526 if (cursor.moveToFirst()) { 527 do { 528 String[] types = parseTypes( 529 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE))); 530 ApnSetting apn = new ApnSetting( 531 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)), 532 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)), 533 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)), 534 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)), 535 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY)), 536 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)), 537 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC)), 538 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY)), 539 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)), 540 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)), 541 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)), 542 types); 543 result.add(apn); 544 } while (cursor.moveToNext()); 545 } 546 return result; 547 } 548 549 private PdpConnection findFreePdp() { 550 for (DataConnection conn : pdpList) { 551 PdpConnection pdp = (PdpConnection) conn; 552 if (pdp.getState() == DataConnection.State.INACTIVE) { 553 return pdp; 554 } 555 } 556 return null; 557 } 558 559 private boolean setupData(String reason) { 560 ApnSetting apn; 561 PdpConnection pdp; 562 563 apn = getNextApn(); 564 if (apn == null) return false; 565 pdp = findFreePdp(); 566 if (pdp == null) { 567 if (DBG) log("setupData: No free PdpConnection found!"); 568 return false; 569 } 570 mActiveApn = apn; 571 mActivePdp = pdp; 572 573 Message msg = obtainMessage(); 574 msg.what = EVENT_DATA_SETUP_COMPLETE; 575 msg.obj = reason; 576 pdp.connect(apn, msg); 577 578 setState(State.INITING); 579 phone.notifyDataConnection(reason); 580 return true; 581 } 582 583 protected String getInterfaceName(String apnType) { 584 if (mActivePdp != null 585 && (apnType == null || mActiveApn.canHandleType(apnType))) { 586 return mActivePdp.getInterface(); 587 } 588 return null; 589 } 590 591 protected String getIpAddress(String apnType) { 592 if (mActivePdp != null 593 && (apnType == null || mActiveApn.canHandleType(apnType))) { 594 return mActivePdp.getIpAddress(); 595 } 596 return null; 597 } 598 599 public String getGateway(String apnType) { 600 if (mActivePdp != null 601 && (apnType == null || mActiveApn.canHandleType(apnType))) { 602 return mActivePdp.getGatewayAddress(); 603 } 604 return null; 605 } 606 607 protected String[] getDnsServers(String apnType) { 608 if (mActivePdp != null 609 && (apnType == null || mActiveApn.canHandleType(apnType))) { 610 return mActivePdp.getDnsServers(); 611 } 612 return null; 613 } 614 615 private boolean 616 pdpStatesHasCID (ArrayList<DataCallState> states, int cid) { 617 for (int i = 0, s = states.size() ; i < s ; i++) { 618 if (states.get(i).cid == cid) return true; 619 } 620 621 return false; 622 } 623 624 private boolean 625 pdpStatesHasActiveCID (ArrayList<DataCallState> states, int cid) { 626 for (int i = 0, s = states.size() ; i < s ; i++) { 627 if ((states.get(i).cid == cid) && (states.get(i).active != 0)) { 628 return true; 629 } 630 } 631 632 return false; 633 } 634 635 /** 636 * Handles changes to the APN database. 637 */ 638 private void onApnChanged() { 639 boolean isConnected; 640 641 isConnected = (state != State.IDLE && state != State.FAILED); 642 643 // The "current" may no longer be valid. MMS depends on this to send properly. 644 mGsmPhone.updateCurrentCarrierInProvider(); 645 646 // TODO: It'd be nice to only do this if the changed entrie(s) 647 // match the current operator. 648 createAllApnList(); 649 if (state != State.DISCONNECTING) { 650 cleanUpConnection(isConnected, Phone.REASON_APN_CHANGED); 651 if (!isConnected) { 652 // reset reconnect timer 653 mRetryMgr.resetRetryCount(); 654 mReregisterOnReconnectFailure = false; 655 trySetupData(Phone.REASON_APN_CHANGED); 656 } 657 } 658 } 659 660 /** 661 * @param explicitPoll if true, indicates that *we* polled for this 662 * update while state == CONNECTED rather than having it delivered 663 * via an unsolicited response (which could have happened at any 664 * previous state 665 */ 666 protected void onPdpStateChanged (AsyncResult ar, boolean explicitPoll) { 667 ArrayList<DataCallState> pdpStates; 668 669 pdpStates = (ArrayList<DataCallState>)(ar.result); 670 671 if (ar.exception != null) { 672 // This is probably "radio not available" or something 673 // of that sort. If so, the whole connection is going 674 // to come down soon anyway 675 return; 676 } 677 678 if (state == State.CONNECTED) { 679 // The way things are supposed to work, the PDP list 680 // should not contain the CID after it disconnects. 681 // However, the way things really work, sometimes the PDP 682 // context is still listed with active = false, which 683 // makes it hard to distinguish an activating context from 684 // an activated-and-then deactivated one. 685 if (!pdpStatesHasCID(pdpStates, cidActive)) { 686 // It looks like the PDP context has deactivated. 687 // Tear everything down and try to reconnect. 688 689 Log.i(LOG_TAG, "PDP connection has dropped. Reconnecting"); 690 691 // Add an event log when the network drops PDP 692 int cid = -1; 693 GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); 694 if (loc != null) cid = loc.getCid(); 695 EventLog.List val = new EventLog.List(cid, 696 TelephonyManager.getDefault().getNetworkType()); 697 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_NETWORK_DROP, val); 698 699 cleanUpConnection(true, null); 700 return; 701 } else if (!pdpStatesHasActiveCID(pdpStates, cidActive)) { 702 // Here, we only consider this authoritative if we asked for the 703 // PDP list. If it was an unsolicited response, we poll again 704 // to make sure everyone agrees on the initial state. 705 706 if (!explicitPoll) { 707 // We think it disconnected but aren't sure...poll from our side 708 phone.mCM.getPDPContextList( 709 this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); 710 } else { 711 Log.i(LOG_TAG, "PDP connection has dropped (active=false case). " 712 + " Reconnecting"); 713 714 // Log the network drop on the event log. 715 int cid = -1; 716 GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); 717 if (loc != null) cid = loc.getCid(); 718 EventLog.List val = new EventLog.List(cid, 719 TelephonyManager.getDefault().getNetworkType()); 720 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_NETWORK_DROP, val); 721 722 cleanUpConnection(true, null); 723 } 724 } 725 } 726 } 727 728 private void notifyDefaultData(String reason) { 729 setupDnsProperties(); 730 setState(State.CONNECTED); 731 phone.notifyDataConnection(reason); 732 startNetStatPoll(); 733 // reset reconnect timer 734 mRetryMgr.resetRetryCount(); 735 mReregisterOnReconnectFailure = false; 736 } 737 738 private void setupDnsProperties() { 739 int mypid = android.os.Process.myPid(); 740 String[] servers = getDnsServers(null); 741 String propName; 742 String propVal; 743 int count; 744 745 count = 0; 746 for (int i = 0; i < servers.length; i++) { 747 String serverAddr = servers[i]; 748 if (!TextUtils.equals(serverAddr, "0.0.0.0")) { 749 SystemProperties.set("net.dns" + (i+1) + "." + mypid, serverAddr); 750 count++; 751 } 752 } 753 for (int i = count+1; i <= 4; i++) { 754 propName = "net.dns" + i + "." + mypid; 755 propVal = SystemProperties.get(propName); 756 if (propVal.length() != 0) { 757 SystemProperties.set(propName, ""); 758 } 759 } 760 /* 761 * Bump the property that tells the name resolver library 762 * to reread the DNS server list from the properties. 763 */ 764 propVal = SystemProperties.get("net.dnschange"); 765 if (propVal.length() != 0) { 766 try { 767 int n = Integer.parseInt(propVal); 768 SystemProperties.set("net.dnschange", "" + (n+1)); 769 } catch (NumberFormatException e) { 770 } 771 } 772 } 773 774 /** 775 * This is a kludge to deal with the fact that 776 * the PDP state change notification doesn't always work 777 * with certain RIL impl's/basebands 778 * 779 */ 780 private void startPeriodicPdpPoll() { 781 removeMessages(EVENT_POLL_PDP); 782 783 sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS); 784 } 785 786 private void resetPollStats() { 787 txPkts = -1; 788 rxPkts = -1; 789 sentSinceLastRecv = 0; 790 netStatPollPeriod = POLL_NETSTAT_MILLIS; 791 mNoRecvPollCount = 0; 792 } 793 794 private void doRecovery() { 795 if (state == State.CONNECTED) { 796 int maxPdpReset = Settings.Gservices.getInt(mResolver, 797 Settings.Gservices.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT, 798 DEFAULT_MAX_PDP_RESET_FAIL); 799 if (mPdpResetCount < maxPdpReset) { 800 mPdpResetCount++; 801 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_RESET, sentSinceLastRecv); 802 cleanUpConnection(true, Phone.REASON_PDP_RESET); 803 } else { 804 mPdpResetCount = 0; 805 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_REREGISTER_NETWORK, sentSinceLastRecv); 806 mGsmPhone.mSST.reRegisterNetwork(null); 807 } 808 // TODO: Add increasingly drastic recovery steps, eg, 809 // reset the radio, reset the device. 810 } 811 } 812 813 protected void startNetStatPoll() { 814 if (state == State.CONNECTED && mPingTestActive == false && netStatPollEnabled == false) { 815 Log.d(LOG_TAG, "[DataConnection] Start poll NetStat"); 816 resetPollStats(); 817 netStatPollEnabled = true; 818 mPollNetStat.run(); 819 } 820 } 821 822 protected void stopNetStatPoll() { 823 netStatPollEnabled = false; 824 removeCallbacks(mPollNetStat); 825 Log.d(LOG_TAG, "[DataConnection] Stop poll NetStat"); 826 } 827 828 protected void restartRadio() { 829 Log.d(LOG_TAG, "************TURN OFF RADIO**************"); 830 cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF); 831 phone.mCM.setRadioPower(false, null); 832 /* Note: no need to call setRadioPower(true). Assuming the desired 833 * radio power state is still ON (as tracked by ServiceStateTracker), 834 * ServiceStateTracker will call setRadioPower when it receives the 835 * RADIO_STATE_CHANGED notification for the power off. And if the 836 * desired power state has changed in the interim, we don't want to 837 * override it with an unconditional power on. 838 */ 839 840 int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0")); 841 SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1)); 842 } 843 844 private Runnable mPollNetStat = new Runnable() 845 { 846 847 public void run() { 848 long sent, received; 849 long preTxPkts = -1, preRxPkts = -1; 850 851 Activity newActivity; 852 853 preTxPkts = txPkts; 854 preRxPkts = rxPkts; 855 856 try { 857 txPkts = netstat.getMobileTxPackets(); 858 rxPkts = netstat.getMobileRxPackets(); 859 } catch (RemoteException e) { 860 txPkts = 0; 861 rxPkts = 0; 862 } 863 864 //Log.d(LOG_TAG, "rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts)); 865 866 if (netStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) { 867 sent = txPkts - preTxPkts; 868 received = rxPkts - preRxPkts; 869 870 if ( sent > 0 && received > 0 ) { 871 sentSinceLastRecv = 0; 872 newActivity = Activity.DATAINANDOUT; 873 mPdpResetCount = 0; 874 } else if (sent > 0 && received == 0) { 875 if (phone.getState() == Phone.State.IDLE) { 876 sentSinceLastRecv += sent; 877 } else { 878 sentSinceLastRecv = 0; 879 } 880 newActivity = Activity.DATAOUT; 881 } else if (sent == 0 && received > 0) { 882 sentSinceLastRecv = 0; 883 newActivity = Activity.DATAIN; 884 mPdpResetCount = 0; 885 } else if (sent == 0 && received == 0) { 886 newActivity = Activity.NONE; 887 } else { 888 sentSinceLastRecv = 0; 889 newActivity = Activity.NONE; 890 } 891 892 if (activity != newActivity && mIsScreenOn) { 893 activity = newActivity; 894 phone.notifyDataActivity(); 895 } 896 } 897 898 int watchdogTrigger = Settings.Gservices.getInt(mResolver, 899 Settings.Gservices.PDP_WATCHDOG_TRIGGER_PACKET_COUNT, 900 NUMBER_SENT_PACKETS_OF_HANG); 901 902 if (sentSinceLastRecv >= watchdogTrigger) { 903 // we already have NUMBER_SENT_PACKETS sent without ack 904 if (mNoRecvPollCount == 0) { 905 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_RESET_COUNTDOWN_TRIGGERED, 906 sentSinceLastRecv); 907 } 908 909 int noRecvPollLimit = Settings.Gservices.getInt(mResolver, 910 Settings.Gservices.PDP_WATCHDOG_ERROR_POLL_COUNT, NO_RECV_POLL_LIMIT); 911 912 if (mNoRecvPollCount < noRecvPollLimit) { 913 // It's possible the PDP context went down and we weren't notified. 914 // Start polling the context list in an attempt to recover. 915 if (DBG) log("no DATAIN in a while; polling PDP"); 916 phone.mCM.getDataCallList(obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); 917 918 mNoRecvPollCount++; 919 920 // Slow down the poll interval to let things happen 921 netStatPollPeriod = Settings.Gservices.getInt(mResolver, 922 Settings.Gservices.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS, 923 POLL_NETSTAT_SLOW_MILLIS); 924 } else { 925 if (DBG) log("Sent " + String.valueOf(sentSinceLastRecv) + 926 " pkts since last received"); 927 // We've exceeded the threshold. Run ping test as a final check; 928 // it will proceed with recovery if ping fails. 929 stopNetStatPoll(); 930 Thread pingTest = new Thread() { 931 public void run() { 932 runPingTest(); 933 } 934 }; 935 mPingTestActive = true; 936 pingTest.start(); 937 } 938 } else { 939 mNoRecvPollCount = 0; 940 if (mIsScreenOn) { 941 netStatPollPeriod = Settings.Gservices.getInt(mResolver, 942 Settings.Gservices.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS); 943 } else { 944 netStatPollPeriod = Settings.Gservices.getInt(mResolver, 945 Settings.Gservices.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS, 946 POLL_NETSTAT_SCREEN_OFF_MILLIS); 947 } 948 } 949 950 if (netStatPollEnabled) { 951 mDataConnectionTracker.postDelayed(this, netStatPollPeriod); 952 } 953 } 954 }; 955 956 private void runPingTest () { 957 int status = -1; 958 try { 959 String address = Settings.Gservices.getString(mResolver, 960 Settings.Gservices.PDP_WATCHDOG_PING_ADDRESS); 961 int deadline = Settings.Gservices.getInt(mResolver, 962 Settings.Gservices.PDP_WATCHDOG_PING_DEADLINE, DEFAULT_PING_DEADLINE); 963 if (DBG) log("pinging " + address + " for " + deadline + "s"); 964 if (address != null && !NULL_IP.equals(address)) { 965 Process p = Runtime.getRuntime() 966 .exec("ping -c 1 -i 1 -w "+ deadline + " " + address); 967 status = p.waitFor(); 968 } 969 } catch (IOException e) { 970 Log.w(LOG_TAG, "ping failed: IOException"); 971 } catch (Exception e) { 972 Log.w(LOG_TAG, "exception trying to ping"); 973 } 974 975 if (status == 0) { 976 // ping succeeded. False alarm. Reset netStatPoll. 977 // ("-1" for this event indicates a false alarm) 978 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_PDP_RESET, -1); 979 mPdpResetCount = 0; 980 sendMessage(obtainMessage(EVENT_START_NETSTAT_POLL)); 981 } else { 982 // ping failed. Proceed with recovery. 983 sendMessage(obtainMessage(EVENT_START_RECOVERY)); 984 } 985 } 986 987 /** 988 * Returns true if the last fail cause is something that 989 * seems like it deserves an error notification. 990 * Transient errors are ignored 991 */ 992 private boolean shouldPostNotification(PdpConnection.FailCause cause) { 993 boolean shouldPost = true; 994 // TODO CHECK 995 // if (dataLink != null) { 996 // shouldPost = dataLink.getLastLinkExitCode() != DataLink.EXIT_OPEN_FAILED; 997 //} 998 return (shouldPost && cause != PdpConnection.FailCause.UNKNOWN); 999 } 1000 1001 /** 1002 * Return true if data connection need to be setup after disconnected due to 1003 * reason. 1004 * 1005 * @param reason the reason why data is disconnected 1006 * @return true if try setup data connection is need for this reason 1007 */ 1008 private boolean retryAfterDisconnected(String reason) { 1009 boolean retry = true; 1010 1011 if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) || 1012 Phone.REASON_DATA_DISABLED.equals(reason) ) { 1013 retry = false; 1014 } 1015 return retry; 1016 } 1017 1018 private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) { 1019 if (state == State.FAILED) { 1020 if (!mRetryMgr.isRetryNeeded()) { 1021 if (mReregisterOnReconnectFailure) { 1022 // We've re-registerd once now just retry forever. 1023 mRetryMgr.retryForeverUsingLastTimeout(); 1024 } else { 1025 // Try to re-register to the network. 1026 Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network"); 1027 mReregisterOnReconnectFailure = true; 1028 mGsmPhone.mSST.reRegisterNetwork(null); 1029 mRetryMgr.resetRetryCount(); 1030 return; 1031 } 1032 } 1033 1034 int nextReconnectDelay = mRetryMgr.getRetryTimer(); 1035 Log.d(LOG_TAG, "PDP activate failed. Scheduling next attempt for " 1036 + (nextReconnectDelay / 1000) + "s"); 1037 1038 AlarmManager am = 1039 (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); 1040 Intent intent = new Intent(INTENT_RECONNECT_ALARM); 1041 intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason); 1042 mReconnectIntent = PendingIntent.getBroadcast( 1043 phone.getContext(), 0, intent, 0); 1044 am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1045 SystemClock.elapsedRealtime() + nextReconnectDelay, 1046 mReconnectIntent); 1047 1048 mRetryMgr.increaseRetryCount(); 1049 1050 if (!shouldPostNotification(lastFailCauseCode)) { 1051 Log.d(LOG_TAG,"NOT Posting GPRS Unavailable notification " 1052 + "-- likely transient error"); 1053 } else { 1054 notifyNoData(lastFailCauseCode); 1055 } 1056 } 1057 } 1058 1059 private void notifyNoData(PdpConnection.FailCause lastFailCauseCode) { 1060 setState(State.FAILED); 1061 } 1062 1063 protected void onRecordsLoaded() { 1064 createAllApnList(); 1065 if (state == State.FAILED) { 1066 cleanUpConnection(false, null); 1067 } 1068 sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED)); 1069 } 1070 1071 protected void onEnableNewApn() { 1072 // TODO: To support simultaneous PDP contexts, this should really only call 1073 // cleanUpConnection if it needs to free up a PdpConnection. 1074 cleanUpConnection(true, Phone.REASON_APN_SWITCHED); 1075 } 1076 1077 protected void onTrySetupData(String reason) { 1078 trySetupData(reason); 1079 } 1080 1081 /** 1082 * Check the data roaming consistency since this can be triggered by 1083 * voice roaming flag of ServiceState in setDataOnRoamingEnabled() 1084 * 1085 * TODO make this triggered by data roaming state only 1086 */ 1087 @Override 1088 protected void onRoamingOff() { 1089 if (!getDataRoaming()) { //data roaming is off 1090 trySetupData(Phone.REASON_ROAMING_OFF); 1091 } else { // Inconsistent! data roaming is on 1092 sendMessage(obtainMessage(EVENT_ROAMING_ON)); 1093 } 1094 } 1095 1096 /** 1097 * Check the data roaming consistency since this can be triggered by 1098 * voice roaming flag of ServiceState in setDataOnRoamingEnabled() 1099 * 1100 * TODO make this triggered by data roaming state only 1101 */ 1102 @Override 1103 protected void onRoamingOn() { 1104 if (getDataRoaming()) { // data roaming is on 1105 if (getDataOnRoamingEnabled()) { 1106 trySetupData(Phone.REASON_ROAMING_ON); 1107 } else { 1108 if (DBG) log("Tear down data connection on roaming."); 1109 cleanUpConnection(true, Phone.REASON_ROAMING_ON); 1110 } 1111 } else { // Inconsistent! data roaming is off 1112 sendMessage(obtainMessage(EVENT_ROAMING_OFF)); 1113 } 1114 } 1115 1116 protected void onRadioAvailable() { 1117 if (phone.getSimulatedRadioControl() != null) { 1118 // Assume data is connected on the simulator 1119 // FIXME this can be improved 1120 setState(State.CONNECTED); 1121 phone.notifyDataConnection(null); 1122 1123 Log.i(LOG_TAG, "We're on the simulator; assuming data is connected"); 1124 } 1125 1126 if (state != State.IDLE) { 1127 cleanUpConnection(true, null); 1128 } 1129 } 1130 1131 protected void onRadioOffOrNotAvailable() { 1132 // Make sure our reconnect delay starts at the initial value 1133 // next time the radio comes on 1134 mRetryMgr.resetRetryCount(); 1135 mReregisterOnReconnectFailure = false; 1136 1137 if (phone.getSimulatedRadioControl() != null) { 1138 // Assume data is connected on the simulator 1139 // FIXME this can be improved 1140 Log.i(LOG_TAG, "We're on the simulator; assuming radio off is meaningless"); 1141 } else { 1142 if (DBG) log("Radio is off and clean up all connection"); 1143 // TODO: Should we reset mRequestedApnType to "default"? 1144 cleanUpConnection(false, Phone.REASON_RADIO_TURNED_OFF); 1145 } 1146 } 1147 1148 protected void onDataSetupComplete(AsyncResult ar) { 1149 String reason = null; 1150 if (ar.userObj instanceof String) { 1151 reason = (String) ar.userObj; 1152 } 1153 1154 if (ar.exception == null) { 1155 // everything is setup 1156 if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { 1157 SystemProperties.set("gsm.defaultpdpcontext.active", "true"); 1158 if (canSetPreferApn && preferredApn == null) { 1159 Log.d(LOG_TAG, "PREFERED APN is null"); 1160 preferredApn = mActiveApn; 1161 setPreferredApn(preferredApn.id); 1162 } 1163 } else { 1164 SystemProperties.set("gsm.defaultpdpcontext.active", "false"); 1165 } 1166 notifyDefaultData(reason); 1167 1168 // TODO: For simultaneous PDP support, we need to build another 1169 // trigger another TRY_SETUP_DATA for the next APN type. (Note 1170 // that the existing connection may service that type, in which 1171 // case we should try the next type, etc. 1172 } else { 1173 PdpConnection.FailCause cause; 1174 cause = (PdpConnection.FailCause) (ar.result); 1175 if(DBG) log("PDP setup failed " + cause); 1176 // Log this failure to the Event Logs. 1177 if (cause.isEventLoggable()) { 1178 int cid = -1; 1179 GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation()); 1180 if (loc != null) cid = loc.getCid(); 1181 1182 EventLog.List val = new EventLog.List( 1183 cause.ordinal(), cid, 1184 TelephonyManager.getDefault().getNetworkType()); 1185 EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_RADIO_PDP_SETUP_FAIL, val); 1186 } 1187 1188 // No try for permanent failure 1189 if (cause.isPermanentFail()) { 1190 notifyNoData(cause); 1191 return; 1192 } 1193 1194 waitingApns.remove(0); 1195 if (waitingApns.isEmpty()) { 1196 // No more to try, start delayed retry 1197 startDelayedRetry(cause, reason); 1198 } else { 1199 // we still have more apns to try 1200 setState(State.SCANNING); 1201 // Wait a bit before trying the next APN, so that 1202 // we're not tying up the RIL command channel 1203 sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, reason), APN_DELAY_MILLIS); 1204 } 1205 } 1206 } 1207 1208 protected void onDisconnectDone(AsyncResult ar) { 1209 String reason = null; 1210 if(DBG) log("EVENT_DISCONNECT_DONE"); 1211 if (ar.userObj instanceof String) { 1212 reason = (String) ar.userObj; 1213 } 1214 setState(State.IDLE); 1215 phone.notifyDataConnection(reason); 1216 mActiveApn = null; 1217 if (retryAfterDisconnected(reason)) { 1218 trySetupData(reason); 1219 } 1220 } 1221 1222 protected void onPollPdp() { 1223 if (state == State.CONNECTED) { 1224 // only poll when connected 1225 phone.mCM.getPDPContextList(this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE)); 1226 sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS); 1227 } 1228 } 1229 1230 protected void onVoiceCallStarted() { 1231 if (state == State.CONNECTED && ! mGsmPhone.mSST.isConcurrentVoiceAndData()) { 1232 stopNetStatPoll(); 1233 phone.notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED); 1234 } 1235 } 1236 1237 protected void onVoiceCallEnded() { 1238 if (state == State.CONNECTED) { 1239 if (!mGsmPhone.mSST.isConcurrentVoiceAndData()) { 1240 startNetStatPoll(); 1241 phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED); 1242 } else { 1243 // clean slate after call end. 1244 resetPollStats(); 1245 } 1246 } else { 1247 // reset reconnect timer 1248 mRetryMgr.resetRetryCount(); 1249 mReregisterOnReconnectFailure = false; 1250 // in case data setup was attempted when we were on a voice call 1251 trySetupData(Phone.REASON_VOICE_CALL_ENDED); 1252 } 1253 } 1254 1255 protected void onCleanUpConnection(boolean tearDown, String reason) { 1256 cleanUpConnection(tearDown, reason); 1257 } 1258 1259 /** 1260 * Based on the sim operator numeric, create a list for all possible pdps 1261 * with all apns associated with that pdp 1262 * 1263 * 1264 */ 1265 private void createAllApnList() { 1266 allApns = new ArrayList<ApnSetting>(); 1267 String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric(); 1268 1269 if (operator != null) { 1270 String selection = "numeric = '" + operator + "'"; 1271 1272 Cursor cursor = phone.getContext().getContentResolver().query( 1273 Telephony.Carriers.CONTENT_URI, null, selection, null, null); 1274 1275 if (cursor != null) { 1276 if (cursor.getCount() > 0) { 1277 allApns = createApnList(cursor); 1278 // TODO: Figure out where this fits in. This basically just 1279 // writes the pap-secrets file. No longer tied to PdpConnection 1280 // object. Not used on current platform (no ppp). 1281 //PdpConnection pdp = pdpList.get(pdp_name); 1282 //if (pdp != null && pdp.dataLink != null) { 1283 // pdp.dataLink.setPasswordInfo(cursor); 1284 //} 1285 } 1286 cursor.close(); 1287 } 1288 } 1289 1290 if (allApns.isEmpty()) { 1291 if (DBG) log("No APN found for carrier: " + operator); 1292 preferredApn = null; 1293 notifyNoData(PdpConnection.FailCause.MISSING_UKNOWN_APN); 1294 } else { 1295 preferredApn = getPreferredApn(); 1296 Log.d(LOG_TAG, "Get PreferredAPN"); 1297 if (preferredApn != null && !preferredApn.numeric.equals(operator)) { 1298 preferredApn = null; 1299 setPreferredApn(-1); 1300 } 1301 } 1302 } 1303 1304 private void createAllPdpList() { 1305 pdpList = new ArrayList<DataConnection>(); 1306 DataConnection pdp; 1307 1308 for (int i = 0; i < PDP_CONNECTION_POOL_SIZE; i++) { 1309 pdp = new PdpConnection(mGsmPhone); 1310 pdpList.add(pdp); 1311 } 1312 } 1313 1314 private void destroyAllPdpList() { 1315 if(pdpList != null) { 1316 PdpConnection pdp; 1317 pdpList.removeAll(pdpList); 1318 } 1319 } 1320 1321 /** 1322 * 1323 * @return waitingApns list to be used to create PDP 1324 * error when waitingApns.isEmpty() 1325 */ 1326 private ArrayList<ApnSetting> buildWaitingApns() { 1327 ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>(); 1328 String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric(); 1329 1330 if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { 1331 if (canSetPreferApn && preferredApn != null) { 1332 Log.i(LOG_TAG, "Preferred APN:" + operator + ":" 1333 + preferredApn.numeric + ":" + preferredApn); 1334 if (preferredApn.numeric.equals(operator)) { 1335 Log.i(LOG_TAG, "Waiting APN set to preferred APN"); 1336 apnList.add(preferredApn); 1337 return apnList; 1338 } else { 1339 setPreferredApn(-1); 1340 preferredApn = null; 1341 } 1342 } 1343 } 1344 1345 if (allApns != null) { 1346 for (ApnSetting apn : allApns) { 1347 if (apn.canHandleType(mRequestedApnType)) { 1348 apnList.add(apn); 1349 } 1350 } 1351 } 1352 return apnList; 1353 } 1354 1355 /** 1356 * Get next apn in waitingApns 1357 * @return the first apn found in waitingApns, null if none 1358 */ 1359 private ApnSetting getNextApn() { 1360 ArrayList<ApnSetting> list = waitingApns; 1361 ApnSetting apn = null; 1362 1363 if (list != null) { 1364 if (!list.isEmpty()) { 1365 apn = list.get(0); 1366 } 1367 } 1368 return apn; 1369 } 1370 1371 private String apnListToString (ArrayList<ApnSetting> apns) { 1372 StringBuilder result = new StringBuilder(); 1373 for (int i = 0, size = apns.size(); i < size; i++) { 1374 result.append('[') 1375 .append(apns.get(i).toString()) 1376 .append(']'); 1377 } 1378 return result.toString(); 1379 } 1380 1381 private void startDelayedRetry(PdpConnection.FailCause cause, String reason) { 1382 notifyNoData(cause); 1383 if (mRequestedApnType == Phone.APN_TYPE_DEFAULT) { 1384 reconnectAfterFail(cause, reason); 1385 } 1386 } 1387 1388 private void setPreferredApn(int pos) { 1389 if (!canSetPreferApn) { 1390 return; 1391 } 1392 1393 ContentResolver resolver = phone.getContext().getContentResolver(); 1394 resolver.delete(PREFERAPN_URI, null, null); 1395 1396 if (pos >= 0) { 1397 ContentValues values = new ContentValues(); 1398 values.put(APN_ID, pos); 1399 resolver.insert(PREFERAPN_URI, values); 1400 } 1401 } 1402 1403 private ApnSetting getPreferredApn() { 1404 if (allApns.isEmpty()) { 1405 return null; 1406 } 1407 1408 Cursor cursor = phone.getContext().getContentResolver().query( 1409 PREFERAPN_URI, new String[] { "_id", "name", "apn" }, 1410 null, null, Telephony.Carriers.DEFAULT_SORT_ORDER); 1411 1412 if (cursor != null) { 1413 canSetPreferApn = true; 1414 } else { 1415 canSetPreferApn = false; 1416 } 1417 1418 if (canSetPreferApn && cursor.getCount() > 0) { 1419 int pos; 1420 cursor.moveToFirst(); 1421 pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)); 1422 for(ApnSetting p:allApns) { 1423 if (p.id == pos && p.canHandleType(mRequestedApnType)) { 1424 cursor.close(); 1425 return p; 1426 } 1427 } 1428 } 1429 1430 if (cursor != null) { 1431 cursor.close(); 1432 } 1433 1434 return null; 1435 } 1436 1437 public void handleMessage (Message msg) { 1438 if (DBG) Log.d(LOG_TAG,"GSMDataConnTrack handleMessage "+msg); 1439 switch (msg.what) { 1440 case EVENT_RECORDS_LOADED: 1441 onRecordsLoaded(); 1442 break; 1443 1444 case EVENT_ENABLE_NEW_APN: 1445 onEnableNewApn(); 1446 break; 1447 1448 case EVENT_GPRS_DETACHED: 1449 onGprsDetached(); 1450 break; 1451 1452 case EVENT_GPRS_ATTACHED: 1453 onGprsAttached(); 1454 break; 1455 1456 case EVENT_DATA_STATE_CHANGED: 1457 onPdpStateChanged((AsyncResult) msg.obj, false); 1458 break; 1459 1460 case EVENT_GET_PDP_LIST_COMPLETE: 1461 onPdpStateChanged((AsyncResult) msg.obj, true); 1462 break; 1463 1464 case EVENT_POLL_PDP: 1465 onPollPdp(); 1466 break; 1467 1468 case EVENT_START_NETSTAT_POLL: 1469 mPingTestActive = false; 1470 startNetStatPoll(); 1471 break; 1472 1473 case EVENT_START_RECOVERY: 1474 mPingTestActive = false; 1475 doRecovery(); 1476 break; 1477 1478 case EVENT_APN_CHANGED: 1479 onApnChanged(); 1480 break; 1481 1482 case EVENT_PS_RESTRICT_ENABLED: 1483 /** 1484 * We don't need to explicitly to tear down the PDP context 1485 * when PS restricted is enabled. The base band will deactive 1486 * PDP context and notify us with PDP_CONTEXT_CHANGED. 1487 * But we should stop the network polling and prevent reset PDP. 1488 */ 1489 Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted); 1490 stopNetStatPoll(); 1491 mIsPsRestricted = true; 1492 break; 1493 1494 case EVENT_PS_RESTRICT_DISABLED: 1495 /** 1496 * When PS restrict is removed, we need setup PDP connection if 1497 * PDP connection is down. 1498 */ 1499 Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted); 1500 mIsPsRestricted = false; 1501 if (state == State.CONNECTED) { 1502 startNetStatPoll(); 1503 } else { 1504 if (state == State.FAILED) { 1505 cleanUpConnection(false, Phone.REASON_PS_RESTRICT_ENABLED); 1506 mRetryMgr.resetRetryCount(); 1507 mReregisterOnReconnectFailure = false; 1508 } 1509 trySetupData(Phone.REASON_PS_RESTRICT_ENABLED); 1510 } 1511 break; 1512 1513 default: 1514 // handle the message in the super class DataConnectionTracker 1515 super.handleMessage(msg); 1516 break; 1517 } 1518 } 1519 1520 protected void log(String s) { 1521 Log.d(LOG_TAG, "[GsmDataConnectionTracker] " + s); 1522 } 1523 1524} 1525