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