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