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