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