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