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