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