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 17 package com.android.internal.telephony.gsm; 18 19 import android.app.AlarmManager; 20 import android.app.PendingIntent; 21 import android.content.ContentResolver; 22 import android.content.ContentValues; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.database.ContentObserver; 27 import android.database.Cursor; 28 import android.net.ConnectivityManager; 29 import android.net.LinkAddress; 30 import android.net.LinkCapabilities; 31 import android.net.LinkProperties; 32 import android.net.LinkProperties.CompareResult; 33 import android.net.NetworkConfig; 34 import android.net.NetworkUtils; 35 import android.net.ProxyProperties; 36 import android.net.TrafficStats; 37 import android.net.Uri; 38 import android.os.AsyncResult; 39 import android.os.Message; 40 import android.os.SystemClock; 41 import android.os.SystemProperties; 42 import android.provider.Settings; 43 import android.provider.Telephony; 44 import android.telephony.CellLocation; 45 import android.telephony.ServiceState; 46 import android.telephony.TelephonyManager; 47 import android.telephony.cdma.CdmaCellLocation; 48 import android.telephony.gsm.GsmCellLocation; 49 import android.text.TextUtils; 50 import android.util.EventLog; 51 import android.util.Log; 52 53 import com.android.internal.telephony.ApnContext; 54 import com.android.internal.telephony.ApnSetting; 55 import com.android.internal.telephony.DataCallState; 56 import com.android.internal.telephony.DataConnection; 57 import com.android.internal.telephony.DataConnection.FailCause; 58 import com.android.internal.telephony.DataConnection.UpdateLinkPropertyResult; 59 import com.android.internal.telephony.DataConnectionAc; 60 import com.android.internal.telephony.DataConnectionTracker; 61 import com.android.internal.telephony.EventLogTags; 62 import com.android.internal.telephony.Phone; 63 import com.android.internal.telephony.PhoneBase; 64 import com.android.internal.telephony.RILConstants; 65 import com.android.internal.telephony.RetryManager; 66 import com.android.internal.util.AsyncChannel; 67 68 import java.util.ArrayList; 69 import java.util.Collection; 70 import java.util.HashMap; 71 import java.util.List; 72 import java.util.concurrent.ConcurrentHashMap; 73 74 /** 75 * {@hide} 76 */ 77 public final class GsmDataConnectionTracker extends DataConnectionTracker { 78 protected final String LOG_TAG = "GSM"; 79 private static final boolean RADIO_TESTS = false; 80 81 /** 82 * Handles changes to the APN db. 83 */ 84 private class ApnChangeObserver extends ContentObserver { 85 public ApnChangeObserver () { 86 super(mDataConnectionTracker); 87 } 88 89 @Override 90 public void onChange(boolean selfChange) { 91 sendMessage(obtainMessage(EVENT_APN_CHANGED)); 92 } 93 } 94 95 //***** Instance Variables 96 97 private boolean mReregisterOnReconnectFailure = false; 98 private ContentResolver mResolver; 99 100 // Recovery action taken in case of data stall 101 private static class RecoveryAction { 102 public static final int GET_DATA_CALL_LIST = 0; 103 public static final int CLEANUP = 1; 104 public static final int REREGISTER = 2; 105 public static final int RADIO_RESTART = 3; 106 public static final int RADIO_RESTART_WITH_PROP = 4; 107 108 private static boolean isAggressiveRecovery(int value) { 109 return ((value == RecoveryAction.CLEANUP) || 110 (value == RecoveryAction.REREGISTER) || 111 (value == RecoveryAction.RADIO_RESTART) || 112 (value == RecoveryAction.RADIO_RESTART_WITH_PROP)); 113 } 114 } 115 116 public int getRecoveryAction() { 117 int action = Settings.System.getInt(mPhone.getContext().getContentResolver(), 118 "radio.data.stall.recovery.action", RecoveryAction.GET_DATA_CALL_LIST); 119 if (VDBG) log("getRecoveryAction: " + action); 120 return action; 121 } 122 public void putRecoveryAction(int action) { 123 Settings.System.putInt(mPhone.getContext().getContentResolver(), 124 "radio.data.stall.recovery.action", action); 125 if (VDBG) log("putRecoveryAction: " + action); 126 } 127 128 //***** Constants 129 130 private static final int POLL_PDP_MILLIS = 5 * 1000; 131 132 private static final String INTENT_RECONNECT_ALARM = 133 "com.android.internal.telephony.gprs-reconnect"; 134 private static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "type"; 135 136 private static final String INTENT_DATA_STALL_ALARM = 137 "com.android.internal.telephony.gprs-data-stall"; 138 139 static final Uri PREFERAPN_NO_UPDATE_URI = 140 Uri.parse("content://telephony/carriers/preferapn_no_update"); 141 static final String APN_ID = "apn_id"; 142 private boolean canSetPreferApn = false; 143 144 private static final boolean DATA_STALL_SUSPECTED = true; 145 private static final boolean DATA_STALL_NOT_SUSPECTED = false; 146 147 @Override 148 protected void onActionIntentReconnectAlarm(Intent intent) { 149 if (DBG) log("GPRS reconnect alarm. Previous state was " + mState); 150 151 String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON); 152 int connectionId = intent.getIntExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, -1); 153 154 DataConnectionAc dcac= mDataConnectionAsyncChannels.get(connectionId); 155 156 if (dcac != null) { 157 for (ApnContext apnContext : dcac.getApnListSync()) { 158 apnContext.setReason(reason); 159 if (apnContext.getState() == State.FAILED) { 160 apnContext.setState(State.IDLE); 161 } 162 sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext)); 163 } 164 // Alram had expired. Clear pending intent recorded on the DataConnection. 165 dcac.setReconnectIntentSync(null); 166 } 167 } 168 169 /** Watches for changes to the APN db. */ 170 private ApnChangeObserver mApnObserver; 171 172 //***** Constructor 173 174 public GsmDataConnectionTracker(PhoneBase p) { 175 super(p); 176 177 p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null); 178 p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); 179 p.mIccRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null); 180 p.mCM.registerForDataNetworkStateChanged (this, EVENT_DATA_STATE_CHANGED, null); 181 p.getCallTracker().registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null); 182 p.getCallTracker().registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null); 183 p.getServiceStateTracker().registerForDataConnectionAttached(this, 184 EVENT_DATA_CONNECTION_ATTACHED, null); 185 p.getServiceStateTracker().registerForDataConnectionDetached(this, 186 EVENT_DATA_CONNECTION_DETACHED, null); 187 p.getServiceStateTracker().registerForRoamingOn(this, EVENT_ROAMING_ON, null); 188 p.getServiceStateTracker().registerForRoamingOff(this, EVENT_ROAMING_OFF, null); 189 p.getServiceStateTracker().registerForPsRestrictedEnabled(this, 190 EVENT_PS_RESTRICT_ENABLED, null); 191 p.getServiceStateTracker().registerForPsRestrictedDisabled(this, 192 EVENT_PS_RESTRICT_DISABLED, null); 193 194 // install reconnect intent filter for this data connection. 195 IntentFilter filter = new IntentFilter(); 196 filter.addAction(INTENT_DATA_STALL_ALARM); 197 p.getContext().registerReceiver(mIntentReceiver, filter, null, p); 198 199 mDataConnectionTracker = this; 200 mResolver = mPhone.getContext().getContentResolver(); 201 202 mApnObserver = new ApnChangeObserver(); 203 p.getContext().getContentResolver().registerContentObserver( 204 Telephony.Carriers.CONTENT_URI, true, mApnObserver); 205 206 mApnContexts = new ConcurrentHashMap<String, ApnContext>(); 207 initApnContextsAndDataConnection(); 208 broadcastMessenger(); 209 } 210 211 @Override 212 public void dispose() { 213 cleanUpAllConnections(false, null); 214 215 super.dispose(); 216 217 //Unregister for all events 218 mPhone.mCM.unregisterForAvailable(this); 219 mPhone.mCM.unregisterForOffOrNotAvailable(this); 220 mPhone.mIccRecords.unregisterForRecordsLoaded(this); 221 mPhone.mCM.unregisterForDataNetworkStateChanged(this); 222 mPhone.getCallTracker().unregisterForVoiceCallEnded(this); 223 mPhone.getCallTracker().unregisterForVoiceCallStarted(this); 224 mPhone.getServiceStateTracker().unregisterForDataConnectionAttached(this); 225 mPhone.getServiceStateTracker().unregisterForDataConnectionDetached(this); 226 mPhone.getServiceStateTracker().unregisterForRoamingOn(this); 227 mPhone.getServiceStateTracker().unregisterForRoamingOff(this); 228 mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this); 229 mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this); 230 231 mPhone.getContext().getContentResolver().unregisterContentObserver(this.mApnObserver); 232 mApnContexts.clear(); 233 234 destroyDataConnections(); 235 } 236 237 @Override 238 public boolean isApnTypeActive(String type) { 239 ApnContext apnContext = mApnContexts.get(type); 240 if (apnContext == null) return false; 241 242 return (apnContext.getDataConnection() != null); 243 } 244 245 @Override 246 protected boolean isDataPossible(String apnType) { 247 ApnContext apnContext = mApnContexts.get(apnType); 248 if (apnContext == null) { 249 return false; 250 } 251 boolean apnContextIsEnabled = apnContext.isEnabled(); 252 State apnContextState = apnContext.getState(); 253 boolean apnTypePossible = !(apnContextIsEnabled && 254 (apnContextState == State.FAILED)); 255 boolean dataAllowed = isDataAllowed(); 256 boolean possible = dataAllowed && apnTypePossible; 257 258 if (DBG) { 259 log(String.format("isDataPossible(%s): possible=%b isDataAllowed=%b " + 260 "apnTypePossible=%b apnContextisEnabled=%b apnContextState()=%s", 261 apnType, possible, dataAllowed, apnTypePossible, 262 apnContextIsEnabled, apnContextState)); 263 } 264 return possible; 265 } 266 267 @Override 268 protected void finalize() { 269 if(DBG) log("finalize"); 270 } 271 272 @Override 273 protected String getActionIntentReconnectAlarm() { 274 return INTENT_RECONNECT_ALARM; 275 } 276 277 @Override 278 protected String getActionIntentDataStallAlarm() { 279 return INTENT_DATA_STALL_ALARM; 280 } 281 282 private ApnContext addApnContext(String type) { 283 ApnContext apnContext = new ApnContext(type, LOG_TAG); 284 apnContext.setDependencyMet(false); 285 mApnContexts.put(type, apnContext); 286 return apnContext; 287 } 288 289 protected void initApnContextsAndDataConnection() { 290 boolean defaultEnabled = SystemProperties.getBoolean(DEFALUT_DATA_ON_BOOT_PROP, true); 291 // Load device network attributes from resources 292 String[] networkConfigStrings = mPhone.getContext().getResources().getStringArray( 293 com.android.internal.R.array.networkAttributes); 294 for (String networkConfigString : networkConfigStrings) { 295 NetworkConfig networkConfig = new NetworkConfig(networkConfigString); 296 ApnContext apnContext = null; 297 298 switch (networkConfig.type) { 299 case ConnectivityManager.TYPE_MOBILE: 300 apnContext = addApnContext(Phone.APN_TYPE_DEFAULT); 301 apnContext.setEnabled(defaultEnabled); 302 break; 303 case ConnectivityManager.TYPE_MOBILE_MMS: 304 apnContext = addApnContext(Phone.APN_TYPE_MMS); 305 break; 306 case ConnectivityManager.TYPE_MOBILE_SUPL: 307 apnContext = addApnContext(Phone.APN_TYPE_SUPL); 308 break; 309 case ConnectivityManager.TYPE_MOBILE_DUN: 310 apnContext = addApnContext(Phone.APN_TYPE_DUN); 311 break; 312 case ConnectivityManager.TYPE_MOBILE_HIPRI: 313 apnContext = addApnContext(Phone.APN_TYPE_HIPRI); 314 ApnContext defaultContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT); 315 if (defaultContext != null) { 316 applyNewState(apnContext, apnContext.isEnabled(), 317 defaultContext.getDependencyMet()); 318 } else { 319 // the default will set the hipri dep-met when it is created 320 } 321 continue; 322 case ConnectivityManager.TYPE_MOBILE_FOTA: 323 apnContext = addApnContext(Phone.APN_TYPE_FOTA); 324 break; 325 case ConnectivityManager.TYPE_MOBILE_IMS: 326 apnContext = addApnContext(Phone.APN_TYPE_IMS); 327 break; 328 case ConnectivityManager.TYPE_MOBILE_CBS: 329 apnContext = addApnContext(Phone.APN_TYPE_CBS); 330 break; 331 default: 332 // skip unknown types 333 continue; 334 } 335 if (apnContext != null) { 336 // set the prop, but also apply the newly set enabled and dependency values 337 onSetDependencyMet(apnContext.getApnType(), networkConfig.dependencyMet); 338 } 339 } 340 } 341 342 @Override 343 protected LinkProperties getLinkProperties(String apnType) { 344 ApnContext apnContext = mApnContexts.get(apnType); 345 if (apnContext != null) { 346 DataConnectionAc dcac = apnContext.getDataConnectionAc(); 347 if (dcac != null) { 348 if (DBG) log("return link properites for " + apnType); 349 return dcac.getLinkPropertiesSync(); 350 } 351 } 352 if (DBG) log("return new LinkProperties"); 353 return new LinkProperties(); 354 } 355 356 @Override 357 protected LinkCapabilities getLinkCapabilities(String apnType) { 358 ApnContext apnContext = mApnContexts.get(apnType); 359 if (apnContext!=null) { 360 DataConnectionAc dataConnectionAc = apnContext.getDataConnectionAc(); 361 if (dataConnectionAc != null) { 362 if (DBG) log("get active pdp is not null, return link Capabilities for " + apnType); 363 return dataConnectionAc.getLinkCapabilitiesSync(); 364 } 365 } 366 if (DBG) log("return new LinkCapabilities"); 367 return new LinkCapabilities(); 368 } 369 370 @Override 371 // Return all active apn types 372 public String[] getActiveApnTypes() { 373 if (DBG) log("get all active apn types"); 374 ArrayList<String> result = new ArrayList<String>(); 375 376 for (ApnContext apnContext : mApnContexts.values()) { 377 if (apnContext.isReady()) { 378 result.add(apnContext.getApnType()); 379 } 380 } 381 382 return (String[])result.toArray(new String[0]); 383 } 384 385 @Override 386 // Return active apn of specific apn type 387 public String getActiveApnString(String apnType) { 388 if (DBG) log( "get active apn string for type:" + apnType); 389 ApnContext apnContext = mApnContexts.get(apnType); 390 if (apnContext != null) { 391 ApnSetting apnSetting = apnContext.getApnSetting(); 392 if (apnSetting != null) { 393 return apnSetting.apn; 394 } 395 } 396 return null; 397 } 398 399 @Override 400 public boolean isApnTypeEnabled(String apnType) { 401 ApnContext apnContext = mApnContexts.get(apnType); 402 if (apnContext == null) { 403 return false; 404 } 405 return apnContext.isEnabled(); 406 } 407 408 @Override 409 protected void setState(State s) { 410 if (DBG) log("setState should not be used in GSM" + s); 411 } 412 413 // Return state of specific apn type 414 @Override 415 public State getState(String apnType) { 416 ApnContext apnContext = mApnContexts.get(apnType); 417 if (apnContext != null) { 418 return apnContext.getState(); 419 } 420 return State.FAILED; 421 } 422 423 // Return state of overall 424 public State getOverallState() { 425 boolean isConnecting = false; 426 boolean isFailed = true; // All enabled Apns should be FAILED. 427 boolean isAnyEnabled = false; 428 429 for (ApnContext apnContext : mApnContexts.values()) { 430 if (apnContext.isEnabled()) { 431 isAnyEnabled = true; 432 switch (apnContext.getState()) { 433 case CONNECTED: 434 case DISCONNECTING: 435 if (DBG) log("overall state is CONNECTED"); 436 return State.CONNECTED; 437 case CONNECTING: 438 case INITING: 439 isConnecting = true; 440 isFailed = false; 441 break; 442 case IDLE: 443 case SCANNING: 444 isFailed = false; 445 break; 446 } 447 } 448 } 449 450 if (!isAnyEnabled) { // Nothing enabled. return IDLE. 451 if (DBG) log( "overall state is IDLE"); 452 return State.IDLE; 453 } 454 455 if (isConnecting) { 456 if (DBG) log( "overall state is CONNECTING"); 457 return State.CONNECTING; 458 } else if (!isFailed) { 459 if (DBG) log( "overall state is IDLE"); 460 return State.IDLE; 461 } else { 462 if (DBG) log( "overall state is FAILED"); 463 return State.FAILED; 464 } 465 } 466 467 /** 468 * Ensure that we are connected to an APN of the specified type. 469 * 470 * @param type the APN type 471 * @return Success is indicated by {@code Phone.APN_ALREADY_ACTIVE} or 472 * {@code Phone.APN_REQUEST_STARTED}. In the latter case, a 473 * broadcast will be sent by the ConnectivityManager when a 474 * connection to the APN has been established. 475 */ 476 @Override 477 public synchronized int enableApnType(String apnType) { 478 ApnContext apnContext = mApnContexts.get(apnType); 479 if (apnContext == null || !isApnTypeAvailable(apnType)) { 480 if (DBG) log("enableApnType: " + apnType + " is type not available"); 481 return Phone.APN_TYPE_NOT_AVAILABLE; 482 } 483 484 // If already active, return 485 if (DBG) log("enableApnType: " + apnType + " mState(" + apnContext.getState() + ")"); 486 487 if (apnContext.getState() == State.CONNECTED) { 488 if (DBG) log("enableApnType: return APN_ALREADY_ACTIVE"); 489 return Phone.APN_ALREADY_ACTIVE; 490 } 491 setEnabled(apnTypeToId(apnType), true); 492 if (DBG) { 493 log("enableApnType: new apn request for type " + apnType + 494 " return APN_REQUEST_STARTED"); 495 } 496 return Phone.APN_REQUEST_STARTED; 497 } 498 499 // A new APN has gone active and needs to send events to catch up with the 500 // current condition 501 private void notifyApnIdUpToCurrent(String reason, ApnContext apnContext, String type) { 502 switch (apnContext.getState()) { 503 case IDLE: 504 case INITING: 505 break; 506 case CONNECTING: 507 case SCANNING: 508 mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTING); 509 break; 510 case CONNECTED: 511 case DISCONNECTING: 512 mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTING); 513 mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTED); 514 break; 515 } 516 } 517 518 @Override 519 public synchronized int disableApnType(String type) { 520 if (DBG) log("disableApnType:" + type); 521 ApnContext apnContext = mApnContexts.get(type); 522 523 if (apnContext != null) { 524 setEnabled(apnTypeToId(type), false); 525 if (apnContext.getState() != State.IDLE && apnContext.getState() != State.FAILED) { 526 if (DBG) log("diableApnType: return APN_REQUEST_STARTED"); 527 return Phone.APN_REQUEST_STARTED; 528 } else { 529 if (DBG) log("disableApnType: return APN_ALREADY_INACTIVE"); 530 return Phone.APN_ALREADY_INACTIVE; 531 } 532 533 } else { 534 if (DBG) { 535 log("disableApnType: no apn context was found, return APN_REQUEST_FAILED"); 536 } 537 return Phone.APN_REQUEST_FAILED; 538 } 539 } 540 541 @Override 542 protected boolean isApnTypeAvailable(String type) { 543 if (type.equals(Phone.APN_TYPE_DUN) && fetchDunApn() != null) { 544 return true; 545 } 546 547 if (mAllApns != null) { 548 for (ApnSetting apn : mAllApns) { 549 if (apn.canHandleType(type)) { 550 return true; 551 } 552 } 553 } 554 return false; 555 } 556 557 /** 558 * Report on whether data connectivity is enabled for any APN. 559 * @return {@code false} if data connectivity has been explicitly disabled, 560 * {@code true} otherwise. 561 */ 562 @Override 563 public boolean getAnyDataEnabled() { 564 synchronized (mDataEnabledLock) { 565 if (!(mInternalDataEnabled && mUserDataEnabled && sPolicyDataEnabled)) return false; 566 for (ApnContext apnContext : mApnContexts.values()) { 567 // Make sure we dont have a context that going down 568 // and is explicitly disabled. 569 if (isDataAllowed(apnContext)) { 570 return true; 571 } 572 } 573 return false; 574 } 575 } 576 577 private boolean isDataAllowed(ApnContext apnContext) { 578 return apnContext.isReady() && isDataAllowed(); 579 } 580 581 //****** Called from ServiceStateTracker 582 /** 583 * Invoked when ServiceStateTracker observes a transition from GPRS 584 * attach to detach. 585 */ 586 protected void onDataConnectionDetached() { 587 /* 588 * We presently believe it is unnecessary to tear down the PDP context 589 * when GPRS detaches, but we should stop the network polling. 590 */ 591 if (DBG) log ("onDataConnectionDetached: stop polling and notify detached"); 592 stopNetStatPoll(); 593 stopDataStallAlarm(); 594 notifyDataConnection(Phone.REASON_DATA_DETACHED); 595 } 596 597 private void onDataConnectionAttached() { 598 if (DBG) log("onDataConnectionAttached"); 599 if (getOverallState() == State.CONNECTED) { 600 if (DBG) log("onDataConnectionAttached: start polling notify attached"); 601 startNetStatPoll(); 602 startDataStallAlarm(DATA_STALL_NOT_SUSPECTED); 603 notifyDataConnection(Phone.REASON_DATA_ATTACHED); 604 } else { 605 // update APN availability so that APN can be enabled. 606 notifyOffApnsOfAvailability(Phone.REASON_DATA_ATTACHED); 607 } 608 609 setupDataOnReadyApns(Phone.REASON_DATA_ATTACHED); 610 } 611 612 @Override 613 protected boolean isDataAllowed() { 614 final boolean internalDataEnabled; 615 synchronized (mDataEnabledLock) { 616 internalDataEnabled = mInternalDataEnabled; 617 } 618 619 int gprsState = mPhone.getServiceStateTracker().getCurrentDataConnectionState(); 620 boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState(); 621 622 boolean allowed = 623 (gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) && 624 mPhone.mIccRecords.getRecordsLoaded() && 625 (mPhone.getState() == Phone.State.IDLE || 626 mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) && 627 internalDataEnabled && 628 (!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) && 629 !mIsPsRestricted && 630 desiredPowerState; 631 if (!allowed && DBG) { 632 String reason = ""; 633 if (!((gprsState == ServiceState.STATE_IN_SERVICE) || mAutoAttachOnCreation)) { 634 reason += " - gprs= " + gprsState; 635 } 636 if (!mPhone.mIccRecords.getRecordsLoaded()) reason += " - SIM not loaded"; 637 if (mPhone.getState() != Phone.State.IDLE && 638 !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) { 639 reason += " - PhoneState= " + mPhone.getState(); 640 reason += " - Concurrent voice and data not allowed"; 641 } 642 if (!internalDataEnabled) reason += " - mInternalDataEnabled= false"; 643 if (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled()) { 644 reason += " - Roaming and data roaming not enabled"; 645 } 646 if (mIsPsRestricted) reason += " - mIsPsRestricted= true"; 647 if (!desiredPowerState) reason += " - desiredPowerState= false"; 648 if (DBG) log("isDataAllowed: not allowed due to" + reason); 649 } 650 return allowed; 651 } 652 653 private void setupDataOnReadyApns(String reason) { 654 // Stop reconnect alarms on all data connections pending 655 // retry. Reset ApnContext state to IDLE. 656 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) { 657 if (dcac.getReconnectIntentSync() != null) { 658 cancelReconnectAlarm(dcac); 659 } 660 // update retry config for existing calls to match up 661 // ones for the new RAT. 662 if (dcac.dataConnection != null) { 663 Collection<ApnContext> apns = dcac.getApnListSync(); 664 665 boolean hasDefault = false; 666 for (ApnContext apnContext : apns) { 667 if (apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)) { 668 hasDefault = true; 669 break; 670 } 671 } 672 configureRetry(dcac.dataConnection, hasDefault); 673 } 674 } 675 676 // Only check for default APN state 677 for (ApnContext apnContext : mApnContexts.values()) { 678 if (apnContext.getState() == State.FAILED) { 679 // By this time, alarms for all failed Apns 680 // should be stopped if any. 681 // Make sure to set the state back to IDLE 682 // so that setup data can happen. 683 apnContext.setState(State.IDLE); 684 } 685 if (apnContext.isReady()) { 686 if (apnContext.getState() == State.IDLE) { 687 apnContext.setReason(reason); 688 trySetupData(apnContext); 689 } 690 } 691 } 692 } 693 694 private boolean trySetupData(String reason, String type) { 695 if (DBG) { 696 log("trySetupData: " + type + " due to " + (reason == null ? "(unspecified)" : reason) 697 + " isPsRestricted=" + mIsPsRestricted); 698 } 699 700 if (type == null) { 701 type = Phone.APN_TYPE_DEFAULT; 702 } 703 704 ApnContext apnContext = mApnContexts.get(type); 705 706 if (apnContext == null ){ 707 if (DBG) log("trySetupData new apn context for type:" + type); 708 apnContext = new ApnContext(type, LOG_TAG); 709 mApnContexts.put(type, apnContext); 710 } 711 apnContext.setReason(reason); 712 713 return trySetupData(apnContext); 714 } 715 716 private boolean trySetupData(ApnContext apnContext) { 717 if (DBG) { 718 log("trySetupData for type:" + apnContext.getApnType() + 719 " due to " + apnContext.getReason()); 720 log("trySetupData with mIsPsRestricted=" + mIsPsRestricted); 721 } 722 723 if (mPhone.getSimulatedRadioControl() != null) { 724 // Assume data is connected on the simulator 725 // FIXME this can be improved 726 apnContext.setState(State.CONNECTED); 727 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); 728 729 log("trySetupData: (fix?) We're on the simulator; assuming data is connected"); 730 return true; 731 } 732 733 boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState(); 734 735 if ((apnContext.getState() == State.IDLE || apnContext.getState() == State.SCANNING) && 736 isDataAllowed(apnContext) && getAnyDataEnabled() && !isEmergency()) { 737 738 if (apnContext.getState() == State.IDLE) { 739 ArrayList<ApnSetting> waitingApns = buildWaitingApns(apnContext.getApnType()); 740 if (waitingApns.isEmpty()) { 741 if (DBG) log("trySetupData: No APN found"); 742 notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN, apnContext); 743 notifyOffApnsOfAvailability(apnContext.getReason()); 744 return false; 745 } else { 746 apnContext.setWaitingApns(waitingApns); 747 if (DBG) { 748 log ("trySetupData: Create from mAllApns : " + apnListToString(mAllApns)); 749 } 750 } 751 } 752 753 if (DBG) { 754 log ("Setup watingApns : " + apnListToString(apnContext.getWaitingApns())); 755 } 756 // apnContext.setReason(apnContext.getReason()); 757 boolean retValue = setupData(apnContext); 758 notifyOffApnsOfAvailability(apnContext.getReason()); 759 return retValue; 760 } else { 761 // TODO: check the condition. 762 if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT) 763 && (apnContext.getState() == State.IDLE 764 || apnContext.getState() == State.SCANNING)) 765 mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType()); 766 notifyOffApnsOfAvailability(apnContext.getReason()); 767 return false; 768 } 769 } 770 771 @Override 772 // Disabled apn's still need avail/unavail notificiations - send them out 773 protected void notifyOffApnsOfAvailability(String reason) { 774 for (ApnContext apnContext : mApnContexts.values()) { 775 if (!apnContext.isReady()) { 776 if (DBG) log("notifyOffApnOfAvailability type:" + apnContext.getApnType()); 777 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(), 778 apnContext.getApnType(), 779 Phone.DataState.DISCONNECTED); 780 } else { 781 if (DBG) { 782 log("notifyOffApnsOfAvailability skipped apn due to isReady==false: " + 783 apnContext.toString()); 784 } 785 } 786 } 787 } 788 789 /** 790 * If tearDown is true, this only tears down a CONNECTED session. Presently, 791 * there is no mechanism for abandoning an INITING/CONNECTING session, 792 * but would likely involve cancelling pending async requests or 793 * setting a flag or new state to ignore them when they came in 794 * @param tearDown true if the underlying GsmDataConnection should be 795 * disconnected. 796 * @param reason reason for the clean up. 797 */ 798 protected void cleanUpAllConnections(boolean tearDown, String reason) { 799 if (DBG) log("cleanUpAllConnections: tearDown=" + tearDown + " reason=" + reason); 800 801 for (ApnContext apnContext : mApnContexts.values()) { 802 apnContext.setReason(reason); 803 cleanUpConnection(tearDown, apnContext); 804 } 805 806 stopNetStatPoll(); 807 stopDataStallAlarm(); 808 809 // TODO: Do we need mRequestedApnType? 810 mRequestedApnType = Phone.APN_TYPE_DEFAULT; 811 } 812 813 /** 814 * Cleanup all connections. 815 * 816 * TODO: Cleanup only a specified connection passed as a parameter. 817 * Also, make sure when you clean up a conn, if it is last apply 818 * logic as though it is cleanupAllConnections 819 * 820 * @param tearDown true if the underlying DataConnection should be disconnected. 821 * @param reason for the clean up. 822 */ 823 824 @Override 825 protected void onCleanUpAllConnections(String cause) { 826 cleanUpAllConnections(true, cause); 827 } 828 829 private void cleanUpConnection(boolean tearDown, ApnContext apnContext) { 830 831 if (apnContext == null) { 832 if (DBG) log("cleanUpConnection: apn context is null"); 833 return; 834 } 835 836 if (DBG) { 837 log("cleanUpConnection: tearDown=" + tearDown + " reason=" + apnContext.getReason()); 838 } 839 DataConnectionAc dcac = apnContext.getDataConnectionAc(); 840 if (tearDown) { 841 if (apnContext.isDisconnected()) { 842 // The request is tearDown and but ApnContext is not connected. 843 // If apnContext is not enabled anymore, break the linkage to the DCAC/DC. 844 apnContext.setState(State.IDLE); 845 if (!apnContext.isReady()) { 846 apnContext.setDataConnection(null); 847 apnContext.setDataConnectionAc(null); 848 } 849 } else { 850 // Connection is still there. Try to clean up. 851 if (dcac != null) { 852 if (apnContext.getState() != State.DISCONNECTING) { 853 boolean disconnectAll = false; 854 if (Phone.APN_TYPE_DUN.equals(apnContext.getApnType())) { 855 ApnSetting dunSetting = fetchDunApn(); 856 if (dunSetting != null && 857 dunSetting.equals(apnContext.getApnSetting())) { 858 if (DBG) log("tearing down dedicated DUN connection"); 859 // we need to tear it down - we brought it up just for dun and 860 // other people are camped on it and now dun is done. We need 861 // to stop using it and let the normal apn list get used to find 862 // connections for the remaining desired connections 863 disconnectAll = true; 864 } 865 } 866 if (DBG) { 867 log("cleanUpConnection: tearing down" + (disconnectAll ? " all" :"")); 868 } 869 Message msg = obtainMessage(EVENT_DISCONNECT_DONE, apnContext); 870 if (disconnectAll) { 871 apnContext.getDataConnection().tearDownAll(apnContext.getReason(), msg); 872 } else { 873 apnContext.getDataConnection().tearDown(apnContext.getReason(), msg); 874 } 875 apnContext.setState(State.DISCONNECTING); 876 } 877 } else { 878 // apn is connected but no reference to dcac. 879 // Should not be happen, but reset the state in case. 880 apnContext.setState(State.IDLE); 881 mPhone.notifyDataConnection(apnContext.getReason(), 882 apnContext.getApnType()); 883 } 884 } 885 } else { 886 // force clean up the data connection. 887 if (dcac != null) dcac.resetSync(); 888 apnContext.setState(State.IDLE); 889 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); 890 apnContext.setDataConnection(null); 891 apnContext.setDataConnectionAc(null); 892 } 893 894 // make sure reconnection alarm is cleaned up if there is no ApnContext 895 // associated to the connection. 896 if (dcac != null) { 897 Collection<ApnContext> apnList = dcac.getApnListSync(); 898 if (apnList.isEmpty()) { 899 cancelReconnectAlarm(dcac); 900 } 901 } 902 } 903 904 /** 905 * Cancels the alarm associated with DCAC. 906 * 907 * @param DataConnectionAc on which the alarm should be stopped. 908 */ 909 private void cancelReconnectAlarm(DataConnectionAc dcac) { 910 if (dcac == null) return; 911 912 PendingIntent intent = dcac.getReconnectIntentSync(); 913 914 if (intent != null) { 915 AlarmManager am = 916 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE); 917 am.cancel(intent); 918 dcac.setReconnectIntentSync(null); 919 } 920 } 921 922 /** 923 * @param types comma delimited list of APN types 924 * @return array of APN types 925 */ 926 private String[] parseTypes(String types) { 927 String[] result; 928 // If unset, set to DEFAULT. 929 if (types == null || types.equals("")) { 930 result = new String[1]; 931 result[0] = Phone.APN_TYPE_ALL; 932 } else { 933 result = types.split(","); 934 } 935 return result; 936 } 937 938 private ArrayList<ApnSetting> createApnList(Cursor cursor) { 939 ArrayList<ApnSetting> result = new ArrayList<ApnSetting>(); 940 if (cursor.moveToFirst()) { 941 do { 942 String[] types = parseTypes( 943 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE))); 944 ApnSetting apn = new ApnSetting( 945 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)), 946 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)), 947 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)), 948 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)), 949 NetworkUtils.trimV4AddrZeros( 950 cursor.getString( 951 cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))), 952 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)), 953 NetworkUtils.trimV4AddrZeros( 954 cursor.getString( 955 cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))), 956 NetworkUtils.trimV4AddrZeros( 957 cursor.getString( 958 cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))), 959 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)), 960 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)), 961 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)), 962 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)), 963 types, 964 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)), 965 cursor.getString(cursor.getColumnIndexOrThrow( 966 Telephony.Carriers.ROAMING_PROTOCOL)), 967 cursor.getInt(cursor.getColumnIndexOrThrow( 968 Telephony.Carriers.CARRIER_ENABLED)) == 1, 969 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER))); 970 result.add(apn); 971 } while (cursor.moveToNext()); 972 } 973 if (DBG) log("createApnList: X result=" + result); 974 return result; 975 } 976 977 private boolean dataConnectionNotInUse(DataConnectionAc dcac) { 978 for (ApnContext apnContext : mApnContexts.values()) { 979 if (apnContext.getDataConnectionAc() == dcac) return false; 980 } 981 return true; 982 } 983 984 private GsmDataConnection findFreeDataConnection() { 985 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) { 986 if (dcac.isInactiveSync() && dataConnectionNotInUse(dcac)) { 987 log("findFreeDataConnection: found free GsmDataConnection"); 988 return (GsmDataConnection) dcac.dataConnection; 989 } 990 } 991 log("findFreeDataConnection: NO free GsmDataConnection"); 992 return null; 993 } 994 995 protected GsmDataConnection findReadyDataConnection(ApnSetting apn) { 996 if (DBG) 997 log("findReadyDataConnection: apn string <" + 998 (apn!=null?(apn.toString()):"null") +">"); 999 if (apn == null) { 1000 return null; 1001 } 1002 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) { 1003 ApnSetting apnSetting = dcac.getApnSettingSync(); 1004 if (DBG) { 1005 log("findReadyDataConnection: dc apn string <" + 1006 (apnSetting != null ? (apnSetting.toString()) : "null") + ">"); 1007 } 1008 if ((apnSetting != null) && TextUtils.equals(apnSetting.toString(), apn.toString())) { 1009 return (GsmDataConnection) dcac.dataConnection; 1010 } 1011 } 1012 return null; 1013 } 1014 1015 1016 private boolean setupData(ApnContext apnContext) { 1017 if (DBG) log("setupData: apnContext=" + apnContext); 1018 ApnSetting apn; 1019 GsmDataConnection dc; 1020 1021 int profileId = getApnProfileID(apnContext.getApnType()); 1022 apn = apnContext.getNextWaitingApn(); 1023 if (apn == null) { 1024 if (DBG) log("setupData: return for no apn found!"); 1025 return false; 1026 } 1027 1028 // First, check to see if ApnContext already has DC. 1029 // This could happen if the retries are currently engaged. 1030 dc = (GsmDataConnection)apnContext.getDataConnection(); 1031 1032 if (dc == null) { 1033 1034 dc = (GsmDataConnection) checkForConnectionForApnContext(apnContext); 1035 1036 if (dc == null) { 1037 dc = findReadyDataConnection(apn); 1038 } 1039 1040 if (dc == null) { 1041 if (DBG) log("setupData: No ready GsmDataConnection found!"); 1042 // TODO: When allocating you are mapping type to id. If more than 1 free, 1043 // then could findFreeDataConnection get the wrong one?? 1044 dc = findFreeDataConnection(); 1045 } 1046 1047 if (dc == null) { 1048 dc = createDataConnection(); 1049 } 1050 1051 if (dc == null) { 1052 if (DBG) log("setupData: No free GsmDataConnection found!"); 1053 return false; 1054 } 1055 1056 DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId()); 1057 dc.setProfileId( profileId ); 1058 dc.setActiveApnType(apnContext.getApnType()); 1059 int refCount = dcac.getRefCountSync(); 1060 if (DBG) log("setupData: init dc and apnContext refCount=" + refCount); 1061 1062 // configure retry count if no other Apn is using the same connection. 1063 if (refCount == 0) { 1064 configureRetry(dc, apn.canHandleType(Phone.APN_TYPE_DEFAULT)); 1065 } 1066 apnContext.setDataConnectionAc(dcac); 1067 apnContext.setDataConnection(dc); 1068 } 1069 1070 apnContext.setApnSetting(apn); 1071 apnContext.setState(State.INITING); 1072 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); 1073 // If reconnect alarm is active on this DataConnection, wait for the alarm being 1074 // fired so that we don't disruppt data retry pattern engaged. 1075 if (apnContext.getDataConnectionAc().getReconnectIntentSync() != null) { 1076 if (DBG) log("setupData: data reconnection pending"); 1077 apnContext.setState(State.FAILED); 1078 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); 1079 return true; 1080 } 1081 1082 Message msg = obtainMessage(); 1083 msg.what = EVENT_DATA_SETUP_COMPLETE; 1084 msg.obj = apnContext; 1085 dc.bringUp(msg, apn); 1086 1087 if (DBG) log("setupData: initing!"); 1088 return true; 1089 } 1090 1091 /** 1092 * Handles changes to the APN database. 1093 */ 1094 private void onApnChanged() { 1095 State overallState = getOverallState(); 1096 boolean isDisconnected = (overallState == State.IDLE || overallState == State.FAILED); 1097 1098 if (mPhone instanceof GSMPhone) { 1099 // The "current" may no longer be valid. MMS depends on this to send properly. TBD 1100 ((GSMPhone)mPhone).updateCurrentCarrierInProvider(); 1101 } 1102 1103 // TODO: It'd be nice to only do this if the changed entrie(s) 1104 // match the current operator. 1105 if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections"); 1106 createAllApnList(); 1107 cleanUpAllConnections(!isDisconnected, Phone.REASON_APN_CHANGED); 1108 if (isDisconnected) { 1109 setupDataOnReadyApns(Phone.REASON_APN_CHANGED); 1110 } 1111 } 1112 1113 /** 1114 * @param cid Connection id provided from RIL. 1115 * @return DataConnectionAc associated with specified cid. 1116 */ 1117 private DataConnectionAc findDataConnectionAcByCid(int cid) { 1118 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) { 1119 if (dcac.getCidSync() == cid) { 1120 return dcac; 1121 } 1122 } 1123 return null; 1124 } 1125 1126 /** 1127 * @param dcacs Collection of DataConnectionAc reported from RIL. 1128 * @return List of ApnContext which is connected, but is not present in 1129 * data connection list reported from RIL. 1130 */ 1131 private List<ApnContext> findApnContextToClean(Collection<DataConnectionAc> dcacs) { 1132 if (dcacs == null) return null; 1133 1134 ArrayList<ApnContext> list = new ArrayList<ApnContext>(); 1135 for (ApnContext apnContext : mApnContexts.values()) { 1136 if (apnContext.getState() == State.CONNECTED) { 1137 boolean found = false; 1138 for (DataConnectionAc dcac : dcacs) { 1139 if (dcac == apnContext.getDataConnectionAc()) { 1140 // ApnContext holds the ref to dcac present in data call list. 1141 found = true; 1142 break; 1143 } 1144 } 1145 if (!found) { 1146 // ApnContext does not have dcac reported in data call list. 1147 // Fetch all the ApnContexts that map to this dcac which are in 1148 // INITING state too. 1149 if (DBG) log("onDataStateChanged(ar): Connected apn not found in the list (" + 1150 apnContext.toString() + ")"); 1151 if (apnContext.getDataConnectionAc() != null) { 1152 list.addAll(apnContext.getDataConnectionAc().getApnListSync()); 1153 } else { 1154 list.add(apnContext); 1155 } 1156 } 1157 } 1158 } 1159 return list; 1160 } 1161 1162 /** 1163 * @param ar is the result of RIL_REQUEST_DATA_CALL_LIST 1164 * or RIL_UNSOL_DATA_CALL_LIST_CHANGED 1165 */ 1166 private void onDataStateChanged (AsyncResult ar) { 1167 ArrayList<DataCallState> dataCallStates; 1168 1169 if (DBG) log("onDataStateChanged(ar): E"); 1170 dataCallStates = (ArrayList<DataCallState>)(ar.result); 1171 1172 if (ar.exception != null) { 1173 // This is probably "radio not available" or something 1174 // of that sort. If so, the whole connection is going 1175 // to come down soon anyway 1176 if (DBG) log("onDataStateChanged(ar): exception; likely radio not available, ignore"); 1177 return; 1178 } 1179 if (DBG) log("onDataStateChanged(ar): DataCallState size=" + dataCallStates.size()); 1180 1181 // Create a hash map to store the dataCallState of each DataConnectionAc 1182 HashMap<DataCallState, DataConnectionAc> dataCallStateToDcac; 1183 dataCallStateToDcac = new HashMap<DataCallState, DataConnectionAc>(); 1184 for (DataCallState dataCallState : dataCallStates) { 1185 DataConnectionAc dcac = findDataConnectionAcByCid(dataCallState.cid); 1186 1187 if (dcac != null) dataCallStateToDcac.put(dataCallState, dcac); 1188 } 1189 1190 // A list of apns to cleanup, those that aren't in the list we know we have to cleanup 1191 List<ApnContext> apnsToCleanup = findApnContextToClean(dataCallStateToDcac.values()); 1192 1193 // Find which connections have changed state and send a notification or cleanup 1194 for (DataCallState newState : dataCallStates) { 1195 DataConnectionAc dcac = dataCallStateToDcac.get(newState); 1196 1197 if (dcac == null) { 1198 loge("onDataStateChanged(ar): No associated DataConnection ignore"); 1199 continue; 1200 } 1201 1202 // The list of apn's associated with this DataConnection 1203 Collection<ApnContext> apns = dcac.getApnListSync(); 1204 1205 // Find which ApnContexts of this DC are in the "Connected/Connecting" state. 1206 ArrayList<ApnContext> connectedApns = new ArrayList<ApnContext>(); 1207 for (ApnContext apnContext : apns) { 1208 if (apnContext.getState() == State.CONNECTED || 1209 apnContext.getState() == State.CONNECTING || 1210 apnContext.getState() == State.INITING) { 1211 connectedApns.add(apnContext); 1212 } 1213 } 1214 if (connectedApns.size() == 0) { 1215 if (DBG) log("onDataStateChanged(ar): no connected apns"); 1216 } else { 1217 // Determine if the connection/apnContext should be cleaned up 1218 // or just a notification should be sent out. 1219 if (DBG) log("onDataStateChanged(ar): Found ConnId=" + newState.cid 1220 + " newState=" + newState.toString()); 1221 if (newState.active == 0) { 1222 if (DBG) { 1223 log("onDataStateChanged(ar): inactive, cleanup apns=" + connectedApns); 1224 } 1225 apnsToCleanup.addAll(connectedApns); 1226 } else { 1227 // Its active so update the DataConnections link properties 1228 UpdateLinkPropertyResult result = 1229 dcac.updateLinkPropertiesDataCallStateSync(newState); 1230 if (result.oldLp.equals(result.newLp)) { 1231 if (DBG) log("onDataStateChanged(ar): no change"); 1232 } else { 1233 if (result.oldLp.isIdenticalInterfaceName(result.newLp)) { 1234 if (! result.oldLp.isIdenticalDnses(result.newLp) || 1235 ! result.oldLp.isIdenticalRoutes(result.newLp) || 1236 ! result.oldLp.isIdenticalHttpProxy(result.newLp) || 1237 ! result.oldLp.isIdenticalAddresses(result.newLp)) { 1238 // If the same address type was removed and added we need to cleanup 1239 CompareResult<LinkAddress> car = 1240 result.oldLp.compareAddresses(result.newLp); 1241 boolean needToClean = false; 1242 for (LinkAddress added : car.added) { 1243 for (LinkAddress removed : car.removed) { 1244 if (NetworkUtils.addressTypeMatches(removed.getAddress(), 1245 added.getAddress())) { 1246 needToClean = true; 1247 break; 1248 } 1249 } 1250 } 1251 if (needToClean) { 1252 if (DBG) { 1253 log("onDataStateChanged(ar): addr change, cleanup apns=" + 1254 connectedApns); 1255 } 1256 apnsToCleanup.addAll(connectedApns); 1257 } else { 1258 if (DBG) log("onDataStateChanged(ar): simple change"); 1259 for (ApnContext apnContext : connectedApns) { 1260 mPhone.notifyDataConnection( 1261 Phone.REASON_LINK_PROPERTIES_CHANGED, 1262 apnContext.getApnType()); 1263 } 1264 } 1265 } else { 1266 if (DBG) { 1267 log("onDataStateChanged(ar): no changes"); 1268 } 1269 } 1270 } else { 1271 if (DBG) { 1272 log("onDataStateChanged(ar): interface change, cleanup apns=" 1273 + connectedApns); 1274 } 1275 apnsToCleanup.addAll(connectedApns); 1276 } 1277 } 1278 } 1279 } 1280 } 1281 1282 if (apnsToCleanup.size() != 0) { 1283 // Add an event log when the network drops PDP 1284 int cid = getCellLocationId(); 1285 EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid, 1286 TelephonyManager.getDefault().getNetworkType()); 1287 } 1288 1289 // Cleanup those dropped connections 1290 for (ApnContext apnContext : apnsToCleanup) { 1291 cleanUpConnection(true, apnContext); 1292 } 1293 1294 if (DBG) log("onDataStateChanged(ar): X"); 1295 } 1296 1297 private void notifyDefaultData(ApnContext apnContext) { 1298 if (DBG) { 1299 log("notifyDefaultData: type=" + apnContext.getApnType() 1300 + ", reason:" + apnContext.getReason()); 1301 } 1302 apnContext.setState(State.CONNECTED); 1303 // setState(State.CONNECTED); 1304 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); 1305 startNetStatPoll(); 1306 startDataStallAlarm(DATA_STALL_NOT_SUSPECTED); 1307 // reset reconnect timer 1308 apnContext.getDataConnection().resetRetryCount(); 1309 } 1310 1311 // TODO: For multiple Active APNs not exactly sure how to do this. 1312 protected void gotoIdleAndNotifyDataConnection(String reason) { 1313 if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason); 1314 notifyDataConnection(reason); 1315 mActiveApn = null; 1316 } 1317 1318 private void resetPollStats() { 1319 mTxPkts = -1; 1320 mRxPkts = -1; 1321 mNetStatPollPeriod = POLL_NETSTAT_MILLIS; 1322 } 1323 1324 private void doRecovery() { 1325 if (getOverallState() == State.CONNECTED) { 1326 // Go through a series of recovery steps, each action transitions to the next action 1327 int recoveryAction = getRecoveryAction(); 1328 switch (recoveryAction) { 1329 case RecoveryAction.GET_DATA_CALL_LIST: 1330 EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST, 1331 mSentSinceLastRecv); 1332 if (DBG) log("doRecovery() get data call list"); 1333 mPhone.mCM.getDataCallList(obtainMessage(EVENT_DATA_STATE_CHANGED)); 1334 putRecoveryAction(RecoveryAction.CLEANUP); 1335 break; 1336 case RecoveryAction.CLEANUP: 1337 EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_CLEANUP, mSentSinceLastRecv); 1338 if (DBG) log("doRecovery() cleanup all connections"); 1339 cleanUpAllConnections(true, Phone.REASON_PDP_RESET); 1340 putRecoveryAction(RecoveryAction.REREGISTER); 1341 break; 1342 case RecoveryAction.REREGISTER: 1343 EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_REREGISTER, 1344 mSentSinceLastRecv); 1345 if (DBG) log("doRecovery() re-register"); 1346 mPhone.getServiceStateTracker().reRegisterNetwork(null); 1347 putRecoveryAction(RecoveryAction.RADIO_RESTART); 1348 break; 1349 case RecoveryAction.RADIO_RESTART: 1350 EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART, 1351 mSentSinceLastRecv); 1352 if (DBG) log("restarting radio"); 1353 putRecoveryAction(RecoveryAction.RADIO_RESTART_WITH_PROP); 1354 restartRadio(); 1355 break; 1356 case RecoveryAction.RADIO_RESTART_WITH_PROP: 1357 // This is in case radio restart has not recovered the data. 1358 // It will set an additional "gsm.radioreset" property to tell 1359 // RIL or system to take further action. 1360 // The implementation of hard reset recovery action is up to OEM product. 1361 // Once gsm.radioreset property is consumed, it is expected to set back 1362 // to false by RIL. 1363 EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART_WITH_PROP, -1); 1364 if (DBG) log("restarting radio with gsm.radioreset to true"); 1365 SystemProperties.set("gsm.radioreset", "true"); 1366 // give 1 sec so property change can be notified. 1367 try { 1368 Thread.sleep(1000); 1369 } catch (InterruptedException e) {} 1370 restartRadio(); 1371 putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST); 1372 break; 1373 default: 1374 throw new RuntimeException("doRecovery: Invalid recoveryAction=" + 1375 recoveryAction); 1376 } 1377 } 1378 } 1379 1380 @Override 1381 protected void startNetStatPoll() { 1382 if (getOverallState() == State.CONNECTED && mNetStatPollEnabled == false) { 1383 if (DBG) log("startNetStatPoll"); 1384 resetPollStats(); 1385 mNetStatPollEnabled = true; 1386 mPollNetStat.run(); 1387 } 1388 } 1389 1390 @Override 1391 protected void stopNetStatPoll() { 1392 mNetStatPollEnabled = false; 1393 removeCallbacks(mPollNetStat); 1394 if (DBG) log("stopNetStatPoll"); 1395 } 1396 1397 @Override 1398 protected void restartRadio() { 1399 if (DBG) log("restartRadio: ************TURN OFF RADIO**************"); 1400 cleanUpAllConnections(true, Phone.REASON_RADIO_TURNED_OFF); 1401 mPhone.getServiceStateTracker().powerOffRadioSafely(this); 1402 /* Note: no need to call setRadioPower(true). Assuming the desired 1403 * radio power state is still ON (as tracked by ServiceStateTracker), 1404 * ServiceStateTracker will call setRadioPower when it receives the 1405 * RADIO_STATE_CHANGED notification for the power off. And if the 1406 * desired power state has changed in the interim, we don't want to 1407 * override it with an unconditional power on. 1408 */ 1409 1410 int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0")); 1411 SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1)); 1412 } 1413 1414 1415 private void updateDataStallInfo() { 1416 long sent, received; 1417 1418 TxRxSum preTxRxSum = new TxRxSum(mDataStallTxRxSum); 1419 mDataStallTxRxSum.updateTxRxSum(); 1420 1421 if (VDBG) { 1422 log("updateDataStallInfo: mDataStallTxRxSum=" + mDataStallTxRxSum + 1423 " preTxRxSum=" + preTxRxSum); 1424 } 1425 1426 sent = mDataStallTxRxSum.txPkts - preTxRxSum.txPkts; 1427 received = mDataStallTxRxSum.rxPkts - preTxRxSum.rxPkts; 1428 1429 if (RADIO_TESTS) { 1430 if (SystemProperties.getBoolean("radio.test.data.stall", false)) { 1431 log("updateDataStallInfo: radio.test.data.stall true received = 0;"); 1432 received = 0; 1433 } 1434 } 1435 if ( sent > 0 && received > 0 ) { 1436 if (VDBG) log("updateDataStallInfo: IN/OUT"); 1437 mSentSinceLastRecv = 0; 1438 putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST); 1439 } else if (sent > 0 && received == 0) { 1440 if (mPhone.getState() == Phone.State.IDLE) { 1441 mSentSinceLastRecv += sent; 1442 } else { 1443 mSentSinceLastRecv = 0; 1444 } 1445 if (DBG) { 1446 log("updateDataStallInfo: OUT sent=" + sent + 1447 " mSentSinceLastRecv=" + mSentSinceLastRecv); 1448 } 1449 } else if (sent == 0 && received > 0) { 1450 if (VDBG) log("updateDataStallInfo: IN"); 1451 mSentSinceLastRecv = 0; 1452 putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST); 1453 } else { 1454 if (VDBG) log("updateDataStallInfo: NONE"); 1455 } 1456 } 1457 1458 @Override 1459 protected void onDataStallAlarm(int tag) { 1460 if (mDataStallAlarmTag != tag) { 1461 if (DBG) { 1462 log("onDataStallAlarm: ignore, tag=" + tag + " expecting " + mDataStallAlarmTag); 1463 } 1464 return; 1465 } 1466 updateDataStallInfo(); 1467 1468 int hangWatchdogTrigger = Settings.Secure.getInt(mResolver, 1469 Settings.Secure.PDP_WATCHDOG_TRIGGER_PACKET_COUNT, 1470 NUMBER_SENT_PACKETS_OF_HANG); 1471 1472 boolean suspectedStall = DATA_STALL_NOT_SUSPECTED; 1473 if (mSentSinceLastRecv >= hangWatchdogTrigger) { 1474 if (DBG) { 1475 log("onDataStallAlarm: tag=" + tag + " do recovery action=" + getRecoveryAction()); 1476 } 1477 suspectedStall = DATA_STALL_SUSPECTED; 1478 sendMessage(obtainMessage(EVENT_DO_RECOVERY)); 1479 } else { 1480 if (VDBG) { 1481 log("onDataStallAlarm: tag=" + tag + " Sent " + String.valueOf(mSentSinceLastRecv) + 1482 " pkts since last received, < watchdogTrigger=" + hangWatchdogTrigger); 1483 } 1484 } 1485 startDataStallAlarm(suspectedStall); 1486 } 1487 1488 1489 private void updateDataActivity() { 1490 long sent, received; 1491 1492 Activity newActivity; 1493 1494 TxRxSum preTxRxSum = new TxRxSum(mTxPkts, mRxPkts); 1495 TxRxSum curTxRxSum = new TxRxSum(); 1496 curTxRxSum.updateTxRxSum(); 1497 mTxPkts = curTxRxSum.txPkts; 1498 mRxPkts = curTxRxSum.rxPkts; 1499 1500 if (VDBG) { 1501 log("updateDataActivity: curTxRxSum=" + curTxRxSum + " preTxRxSum=" + preTxRxSum); 1502 } 1503 1504 if (mNetStatPollEnabled && (preTxRxSum.txPkts > 0 || preTxRxSum.rxPkts > 0)) { 1505 sent = mTxPkts - preTxRxSum.txPkts; 1506 received = mRxPkts - preTxRxSum.rxPkts; 1507 1508 if (VDBG) log("updateDataActivity: sent=" + sent + " received=" + received); 1509 if ( sent > 0 && received > 0 ) { 1510 newActivity = Activity.DATAINANDOUT; 1511 } else if (sent > 0 && received == 0) { 1512 newActivity = Activity.DATAOUT; 1513 } else if (sent == 0 && received > 0) { 1514 newActivity = Activity.DATAIN; 1515 } else { 1516 newActivity = Activity.NONE; 1517 } 1518 1519 if (mActivity != newActivity && mIsScreenOn) { 1520 if (VDBG) log("updateDataActivity: newActivity=" + newActivity); 1521 mActivity = newActivity; 1522 mPhone.notifyDataActivity(); 1523 } 1524 } 1525 } 1526 1527 private Runnable mPollNetStat = new Runnable() 1528 { 1529 @Override 1530 public void run() { 1531 updateDataActivity(); 1532 1533 if (mIsScreenOn) { 1534 mNetStatPollPeriod = Settings.Secure.getInt(mResolver, 1535 Settings.Secure.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS); 1536 } else { 1537 mNetStatPollPeriod = Settings.Secure.getInt(mResolver, 1538 Settings.Secure.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS, 1539 POLL_NETSTAT_SCREEN_OFF_MILLIS); 1540 } 1541 1542 if (mNetStatPollEnabled) { 1543 mDataConnectionTracker.postDelayed(this, mNetStatPollPeriod); 1544 } 1545 } 1546 }; 1547 1548 /** 1549 * Returns true if the last fail cause is something that 1550 * seems like it deserves an error notification. 1551 * Transient errors are ignored 1552 */ 1553 private boolean shouldPostNotification(GsmDataConnection.FailCause cause) { 1554 return (cause != GsmDataConnection.FailCause.UNKNOWN); 1555 } 1556 1557 /** 1558 * Return true if data connection need to be setup after disconnected due to 1559 * reason. 1560 * 1561 * @param reason the reason why data is disconnected 1562 * @return true if try setup data connection is need for this reason 1563 */ 1564 private boolean retryAfterDisconnected(String reason) { 1565 boolean retry = true; 1566 1567 if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) { 1568 retry = false; 1569 } 1570 return retry; 1571 } 1572 1573 private void reconnectAfterFail(FailCause lastFailCauseCode, 1574 ApnContext apnContext, int retryOverride) { 1575 if (apnContext == null) { 1576 loge("reconnectAfterFail: apnContext == null, impossible"); 1577 return; 1578 } 1579 if ((apnContext.getState() == State.FAILED) && 1580 (apnContext.getDataConnection() != null)) { 1581 if (!apnContext.getDataConnection().isRetryNeeded()) { 1582 if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)) { 1583 mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType()); 1584 return; 1585 } 1586 if (mReregisterOnReconnectFailure) { 1587 // We've re-registerd once now just retry forever. 1588 apnContext.getDataConnection().retryForeverUsingLastTimeout(); 1589 } else { 1590 // Try to Re-register to the network. 1591 if (DBG) log("reconnectAfterFail: activate failed, Reregistering to network"); 1592 mReregisterOnReconnectFailure = true; 1593 mPhone.getServiceStateTracker().reRegisterNetwork(null); 1594 apnContext.getDataConnection().resetRetryCount(); 1595 return; 1596 } 1597 } 1598 1599 // If retry needs to be backed off for specific case (determined by RIL/Modem) 1600 // use the specified timer instead of pre-configured retry pattern. 1601 int nextReconnectDelay = retryOverride; 1602 if (nextReconnectDelay < 0) { 1603 nextReconnectDelay = apnContext.getDataConnection().getRetryTimer(); 1604 apnContext.getDataConnection().increaseRetryCount(); 1605 } 1606 startAlarmForReconnect(nextReconnectDelay, apnContext); 1607 1608 if (!shouldPostNotification(lastFailCauseCode)) { 1609 if (DBG) { 1610 log("reconnectAfterFail: NOT Posting GPRS Unavailable notification " 1611 + "-- likely transient error"); 1612 } 1613 } else { 1614 notifyNoData(lastFailCauseCode, apnContext); 1615 } 1616 } 1617 } 1618 1619 private void startAlarmForReconnect(int delay, ApnContext apnContext) { 1620 1621 if (DBG) { 1622 log("Schedule alarm for reconnect: activate failed. Scheduling next attempt for " 1623 + (delay / 1000) + "s"); 1624 } 1625 1626 DataConnectionAc dcac = apnContext.getDataConnectionAc(); 1627 1628 if ((dcac == null) || (dcac.dataConnection == null)) { 1629 // should not happen, but just in case. 1630 loge("null dcac or dc."); 1631 return; 1632 } 1633 1634 AlarmManager am = 1635 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE); 1636 1637 Intent intent = new Intent(INTENT_RECONNECT_ALARM + '.' + 1638 dcac.dataConnection.getDataConnectionId()); 1639 intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason()); 1640 intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, 1641 dcac.dataConnection.getDataConnectionId()); 1642 1643 PendingIntent alarmIntent = PendingIntent.getBroadcast (mPhone.getContext(), 0, 1644 intent, 0); 1645 dcac.setReconnectIntentSync(alarmIntent); 1646 am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1647 SystemClock.elapsedRealtime() + delay, alarmIntent); 1648 1649 } 1650 1651 private void startDataStallAlarm(boolean suspectedStall) { 1652 int nextAction = getRecoveryAction(); 1653 int delayInMs; 1654 1655 // If screen is on or data stall is currently suspected, set the alarm 1656 // with an aggresive timeout. 1657 if (mIsScreenOn || suspectedStall || RecoveryAction.isAggressiveRecovery(nextAction)) { 1658 delayInMs = Settings.Secure.getInt(mResolver, 1659 Settings.Secure.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS, 1660 DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS_DEFAULT); 1661 } else { 1662 delayInMs = Settings.Secure.getInt(mResolver, 1663 Settings.Secure.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS, 1664 DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS_DEFAULT); 1665 } 1666 1667 mDataStallAlarmTag += 1; 1668 if (VDBG) { 1669 log("startDataStallAlarm: tag=" + mDataStallAlarmTag + 1670 " delay=" + (delayInMs / 1000) + "s"); 1671 } 1672 AlarmManager am = 1673 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE); 1674 1675 Intent intent = new Intent(INTENT_DATA_STALL_ALARM); 1676 intent.putExtra(DATA_STALL_ALARM_TAG_EXTRA, mDataStallAlarmTag); 1677 mDataStallAlarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0, intent, 1678 PendingIntent.FLAG_UPDATE_CURRENT); 1679 am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1680 SystemClock.elapsedRealtime() + delayInMs, mDataStallAlarmIntent); 1681 } 1682 1683 private void stopDataStallAlarm() { 1684 AlarmManager am = 1685 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE); 1686 1687 if (VDBG) { 1688 log("stopDataStallAlarm: current tag=" + mDataStallAlarmTag + 1689 " mDataStallAlarmIntent=" + mDataStallAlarmIntent); 1690 } 1691 mDataStallAlarmTag += 1; 1692 if (mDataStallAlarmIntent != null) { 1693 am.cancel(mDataStallAlarmIntent); 1694 mDataStallAlarmIntent = null; 1695 } 1696 } 1697 1698 @Override 1699 protected void restartDataStallAlarm() { 1700 // To be called on screen status change. 1701 // Do not cancel the alarm if it is set with aggressive timeout. 1702 int nextAction = getRecoveryAction(); 1703 1704 if (RecoveryAction.isAggressiveRecovery(nextAction)) { 1705 if (DBG) log("data stall recovery action is pending. not resetting the alarm."); 1706 return; 1707 } 1708 stopDataStallAlarm(); 1709 startDataStallAlarm(DATA_STALL_NOT_SUSPECTED); 1710 } 1711 1712 private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode, 1713 ApnContext apnContext) { 1714 if (DBG) log( "notifyNoData: type=" + apnContext.getApnType()); 1715 apnContext.setState(State.FAILED); 1716 if (lastFailCauseCode.isPermanentFail() 1717 && (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT))) { 1718 mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType()); 1719 } 1720 } 1721 1722 private void onRecordsLoaded() { 1723 if (DBG) log("onRecordsLoaded: createAllApnList"); 1724 createAllApnList(); 1725 if (mPhone.mCM.getRadioState().isOn()) { 1726 if (DBG) log("onRecordsLoaded: notifying data availability"); 1727 notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED); 1728 } 1729 setupDataOnReadyApns(Phone.REASON_SIM_LOADED); 1730 } 1731 1732 @Override 1733 protected void onSetDependencyMet(String apnType, boolean met) { 1734 // don't allow users to tweak hipri to work around default dependency not met 1735 if (Phone.APN_TYPE_HIPRI.equals(apnType)) return; 1736 1737 ApnContext apnContext = mApnContexts.get(apnType); 1738 if (apnContext == null) { 1739 loge("onSetDependencyMet: ApnContext not found in onSetDependencyMet(" + 1740 apnType + ", " + met + ")"); 1741 return; 1742 } 1743 applyNewState(apnContext, apnContext.isEnabled(), met); 1744 if (Phone.APN_TYPE_DEFAULT.equals(apnType)) { 1745 // tie actions on default to similar actions on HIPRI regarding dependencyMet 1746 apnContext = mApnContexts.get(Phone.APN_TYPE_HIPRI); 1747 if (apnContext != null) applyNewState(apnContext, apnContext.isEnabled(), met); 1748 } 1749 } 1750 1751 private void applyNewState(ApnContext apnContext, boolean enabled, boolean met) { 1752 boolean cleanup = false; 1753 boolean trySetup = false; 1754 if (DBG) { 1755 log("applyNewState(" + apnContext.getApnType() + ", " + enabled + 1756 "(" + apnContext.isEnabled() + "), " + met + "(" + 1757 apnContext.getDependencyMet() +"))"); 1758 } 1759 if (apnContext.isReady()) { 1760 if (enabled && met) return; 1761 if (!enabled) { 1762 apnContext.setReason(Phone.REASON_DATA_DISABLED); 1763 } else { 1764 apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET); 1765 } 1766 cleanup = true; 1767 } else { 1768 if (enabled && met) { 1769 if (apnContext.isEnabled()) { 1770 apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_MET); 1771 } else { 1772 apnContext.setReason(Phone.REASON_DATA_ENABLED); 1773 } 1774 if (apnContext.getState() == State.FAILED) { 1775 apnContext.setState(State.IDLE); 1776 } 1777 trySetup = true; 1778 } 1779 } 1780 apnContext.setEnabled(enabled); 1781 apnContext.setDependencyMet(met); 1782 if (cleanup) cleanUpConnection(true, apnContext); 1783 if (trySetup) trySetupData(apnContext); 1784 } 1785 1786 private DataConnection checkForConnectionForApnContext(ApnContext apnContext) { 1787 // Loop through all apnContexts looking for one with a conn that satisfies this apnType 1788 String apnType = apnContext.getApnType(); 1789 ApnSetting dunSetting = null; 1790 1791 if (Phone.APN_TYPE_DUN.equals(apnType)) { 1792 dunSetting = fetchDunApn(); 1793 } 1794 1795 for (ApnContext c : mApnContexts.values()) { 1796 DataConnection conn = c.getDataConnection(); 1797 if (conn != null) { 1798 ApnSetting apnSetting = c.getApnSetting(); 1799 if (dunSetting != null) { 1800 if (dunSetting.equals(apnSetting)) { 1801 if (DBG) { 1802 log("checkForConnectionForApnContext: apnContext=" + apnContext + 1803 " found conn=" + conn); 1804 } 1805 return conn; 1806 } 1807 } else if (apnSetting != null && apnSetting.canHandleType(apnType)) { 1808 if (DBG) { 1809 log("checkForConnectionForApnContext: apnContext=" + apnContext + 1810 " found conn=" + conn); 1811 } 1812 return conn; 1813 } 1814 } 1815 } 1816 if (DBG) log("checkForConnectionForApnContext: apnContext=" + apnContext + " NO conn"); 1817 return null; 1818 } 1819 1820 @Override 1821 protected void onEnableApn(int apnId, int enabled) { 1822 ApnContext apnContext = mApnContexts.get(apnIdToType(apnId)); 1823 if (apnContext == null) { 1824 loge("onEnableApn(" + apnId + ", " + enabled + "): NO ApnContext"); 1825 return; 1826 } 1827 // TODO change our retry manager to use the appropriate numbers for the new APN 1828 if (DBG) log("onEnableApn: apnContext=" + apnContext + " call applyNewState"); 1829 applyNewState(apnContext, enabled == ENABLED, apnContext.getDependencyMet()); 1830 } 1831 1832 @Override 1833 // TODO: We shouldnt need this. 1834 protected boolean onTrySetupData(String reason) { 1835 if (DBG) log("onTrySetupData: reason=" + reason); 1836 setupDataOnReadyApns(reason); 1837 return true; 1838 } 1839 1840 protected boolean onTrySetupData(ApnContext apnContext) { 1841 if (DBG) log("onTrySetupData: apnContext=" + apnContext); 1842 return trySetupData(apnContext); 1843 } 1844 1845 @Override 1846 protected void onRoamingOff() { 1847 if (DBG) log("onRoamingOff"); 1848 1849 if (getDataOnRoamingEnabled() == false) { 1850 notifyOffApnsOfAvailability(Phone.REASON_ROAMING_OFF); 1851 setupDataOnReadyApns(Phone.REASON_ROAMING_OFF); 1852 } else { 1853 notifyDataConnection(Phone.REASON_ROAMING_OFF); 1854 } 1855 } 1856 1857 @Override 1858 protected void onRoamingOn() { 1859 if (getDataOnRoamingEnabled()) { 1860 if (DBG) log("onRoamingOn: setup data on roaming"); 1861 setupDataOnReadyApns(Phone.REASON_ROAMING_ON); 1862 notifyDataConnection(Phone.REASON_ROAMING_ON); 1863 } else { 1864 if (DBG) log("onRoamingOn: Tear down data connection on roaming."); 1865 cleanUpAllConnections(true, Phone.REASON_ROAMING_ON); 1866 notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON); 1867 } 1868 } 1869 1870 @Override 1871 protected void onRadioAvailable() { 1872 if (DBG) log("onRadioAvailable"); 1873 if (mPhone.getSimulatedRadioControl() != null) { 1874 // Assume data is connected on the simulator 1875 // FIXME this can be improved 1876 // setState(State.CONNECTED); 1877 notifyDataConnection(null); 1878 1879 log("onRadioAvailable: We're on the simulator; assuming data is connected"); 1880 } 1881 1882 if (mPhone.mIccRecords.getRecordsLoaded()) { 1883 notifyOffApnsOfAvailability(null); 1884 } 1885 1886 if (getOverallState() != State.IDLE) { 1887 cleanUpConnection(true, null); 1888 } 1889 } 1890 1891 @Override 1892 protected void onRadioOffOrNotAvailable() { 1893 // Make sure our reconnect delay starts at the initial value 1894 // next time the radio comes on 1895 1896 for (DataConnection dc : mDataConnections.values()) { 1897 dc.resetRetryCount(); 1898 } 1899 mReregisterOnReconnectFailure = false; 1900 1901 if (mPhone.getSimulatedRadioControl() != null) { 1902 // Assume data is connected on the simulator 1903 // FIXME this can be improved 1904 log("We're on the simulator; assuming radio off is meaningless"); 1905 } else { 1906 if (DBG) log("onRadioOffOrNotAvailable: is off and clean up all connections"); 1907 cleanUpAllConnections(false, Phone.REASON_RADIO_TURNED_OFF); 1908 } 1909 notifyOffApnsOfAvailability(null); 1910 } 1911 1912 @Override 1913 protected void onDataSetupComplete(AsyncResult ar) { 1914 1915 DataConnection.FailCause cause = DataConnection.FailCause.UNKNOWN; 1916 boolean handleError = false; 1917 ApnContext apnContext = null; 1918 1919 if(ar.userObj instanceof ApnContext){ 1920 apnContext = (ApnContext)ar.userObj; 1921 } else { 1922 throw new RuntimeException("onDataSetupComplete: No apnContext"); 1923 } 1924 1925 if (isDataSetupCompleteOk(ar)) { 1926 DataConnectionAc dcac = apnContext.getDataConnectionAc(); 1927 1928 if (RADIO_TESTS) { 1929 // Note: To change radio.test.onDSC.null.dcac from command line you need to 1930 // adb root and adb remount and from the command line you can only change the 1931 // value to 1 once. To change it a second time you can reboot or execute 1932 // adb shell stop and then adb shell start. The command line to set the value is: 1933 // adb shell sqlite3 /data/data/com.android.providers.settings/databases/settings.db "insert into system (name,value) values ('radio.test.onDSC.null.dcac', '1');" 1934 ContentResolver cr = mPhone.getContext().getContentResolver(); 1935 String radioTestProperty = "radio.test.onDSC.null.dcac"; 1936 if (Settings.System.getInt(cr, radioTestProperty, 0) == 1) { 1937 log("onDataSetupComplete: " + radioTestProperty + 1938 " is true, set dcac to null and reset property to false"); 1939 dcac = null; 1940 Settings.System.putInt(cr, radioTestProperty, 0); 1941 log("onDataSetupComplete: " + radioTestProperty + "=" + 1942 Settings.System.getInt(mPhone.getContext().getContentResolver(), 1943 radioTestProperty, -1)); 1944 } 1945 } 1946 if (dcac == null) { 1947 log("onDataSetupComplete: no connection to DC, handle as error"); 1948 cause = DataConnection.FailCause.CONNECTION_TO_DATACONNECTIONAC_BROKEN; 1949 handleError = true; 1950 } else { 1951 DataConnection dc = apnContext.getDataConnection(); 1952 1953 if (DBG) { 1954 // TODO We may use apnContext.getApnSetting() directly 1955 // instead of getWaitingApns().get(0) 1956 String apnStr = "<unknown>"; 1957 if (apnContext.getWaitingApns() != null 1958 && !apnContext.getWaitingApns().isEmpty()){ 1959 apnStr = apnContext.getWaitingApns().get(0).apn; 1960 } 1961 log("onDataSetupComplete: success apn=" + apnStr); 1962 } 1963 ApnSetting apn = apnContext.getApnSetting(); 1964 if (apn.proxy != null && apn.proxy.length() != 0) { 1965 try { 1966 String port = apn.port; 1967 if (TextUtils.isEmpty(port)) port = "8080"; 1968 ProxyProperties proxy = new ProxyProperties(apn.proxy, 1969 Integer.parseInt(port), null); 1970 dcac.setLinkPropertiesHttpProxySync(proxy); 1971 } catch (NumberFormatException e) { 1972 loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" + 1973 apn.port + "): " + e); 1974 } 1975 } 1976 1977 // everything is setup 1978 if(TextUtils.equals(apnContext.getApnType(),Phone.APN_TYPE_DEFAULT)) { 1979 SystemProperties.set("gsm.defaultpdpcontext.active", "true"); 1980 if (canSetPreferApn && mPreferredApn == null) { 1981 if (DBG) log("onDataSetupComplete: PREFERED APN is null"); 1982 mPreferredApn = apnContext.getApnSetting(); 1983 if (mPreferredApn != null) { 1984 setPreferredApn(mPreferredApn.id); 1985 } 1986 } 1987 } else { 1988 SystemProperties.set("gsm.defaultpdpcontext.active", "false"); 1989 } 1990 notifyDefaultData(apnContext); 1991 } 1992 } else { 1993 String apnString; 1994 1995 cause = (DataConnection.FailCause) (ar.result); 1996 if (DBG) { 1997 try { 1998 apnString = apnContext.getWaitingApns().get(0).apn; 1999 } catch (Exception e) { 2000 apnString = "<unknown>"; 2001 } 2002 log(String.format("onDataSetupComplete: error apn=%s cause=%s", apnString, cause)); 2003 } 2004 if (cause.isEventLoggable()) { 2005 // Log this failure to the Event Logs. 2006 int cid = getCellLocationId(); 2007 EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL, 2008 cause.ordinal(), cid, TelephonyManager.getDefault().getNetworkType()); 2009 } 2010 2011 // Count permanent failures and remove the APN we just tried 2012 if (cause.isPermanentFail()) apnContext.decWaitingApnsPermFailCount(); 2013 2014 apnContext.removeNextWaitingApn(); 2015 if (DBG) { 2016 log(String.format("onDataSetupComplete: WaitingApns.size=%d" + 2017 " WaitingApnsPermFailureCountDown=%d", 2018 apnContext.getWaitingApns().size(), 2019 apnContext.getWaitingApnsPermFailCount())); 2020 } 2021 handleError = true; 2022 } 2023 2024 if (handleError) { 2025 // See if there are more APN's to try 2026 if (apnContext.getWaitingApns().isEmpty()) { 2027 if (apnContext.getWaitingApnsPermFailCount() == 0) { 2028 if (DBG) { 2029 log("onDataSetupComplete: All APN's had permanent failures, stop retrying"); 2030 } 2031 apnContext.setState(State.FAILED); 2032 mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType()); 2033 2034 apnContext.setDataConnection(null); 2035 apnContext.setDataConnectionAc(null); 2036 } else { 2037 if (DBG) log("onDataSetupComplete: Not all permanent failures, retry"); 2038 // check to see if retry should be overridden for this failure. 2039 int retryOverride = -1; 2040 if (ar.exception instanceof DataConnection.CallSetupException) { 2041 retryOverride = 2042 ((DataConnection.CallSetupException)ar.exception).getRetryOverride(); 2043 } 2044 if (retryOverride == RILConstants.MAX_INT) { 2045 if (DBG) log("No retry is suggested."); 2046 } else { 2047 startDelayedRetry(cause, apnContext, retryOverride); 2048 } 2049 } 2050 } else { 2051 if (DBG) log("onDataSetupComplete: Try next APN"); 2052 apnContext.setState(State.SCANNING); 2053 // Wait a bit before trying the next APN, so that 2054 // we're not tying up the RIL command channel 2055 startAlarmForReconnect(APN_DELAY_MILLIS, apnContext); 2056 } 2057 } 2058 } 2059 2060 /** 2061 * Called when EVENT_DISCONNECT_DONE is received. 2062 */ 2063 @Override 2064 protected void onDisconnectDone(int connId, AsyncResult ar) { 2065 ApnContext apnContext = null; 2066 2067 if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE connId=" + connId); 2068 if (ar.userObj instanceof ApnContext) { 2069 apnContext = (ApnContext) ar.userObj; 2070 } else { 2071 loge("Invalid ar in onDisconnectDone"); 2072 return; 2073 } 2074 2075 apnContext.setState(State.IDLE); 2076 2077 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType()); 2078 2079 // if all data connection are gone, check whether Airplane mode request was 2080 // pending. 2081 if (isDisconnected()) { 2082 if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) { 2083 // Radio will be turned off. No need to retry data setup 2084 apnContext.setApnSetting(null); 2085 apnContext.setDataConnection(null); 2086 apnContext.setDataConnectionAc(null); 2087 return; 2088 } 2089 } 2090 2091 // If APN is still enabled, try to bring it back up automatically 2092 if (apnContext.isReady() && retryAfterDisconnected(apnContext.getReason())) { 2093 SystemProperties.set("gsm.defaultpdpcontext.active", "false"); // TODO - what the heck? This shoudld go 2094 // Wait a bit before trying the next APN, so that 2095 // we're not tying up the RIL command channel. 2096 // This also helps in any external dependency to turn off the context. 2097 startAlarmForReconnect(APN_DELAY_MILLIS, apnContext); 2098 } else { 2099 apnContext.setApnSetting(null); 2100 apnContext.setDataConnection(null); 2101 apnContext.setDataConnectionAc(null); 2102 } 2103 } 2104 2105 protected void onPollPdp() { 2106 if (getOverallState() == State.CONNECTED) { 2107 // only poll when connected 2108 mPhone.mCM.getDataCallList(this.obtainMessage(EVENT_DATA_STATE_CHANGED)); 2109 sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS); 2110 } 2111 } 2112 2113 @Override 2114 protected void onVoiceCallStarted() { 2115 if (DBG) log("onVoiceCallStarted"); 2116 if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) { 2117 if (DBG) log("onVoiceCallStarted stop polling"); 2118 stopNetStatPoll(); 2119 stopDataStallAlarm(); 2120 notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED); 2121 } 2122 } 2123 2124 @Override 2125 protected void onVoiceCallEnded() { 2126 if (DBG) log("onVoiceCallEnded"); 2127 if (isConnected()) { 2128 if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) { 2129 startNetStatPoll(); 2130 startDataStallAlarm(DATA_STALL_NOT_SUSPECTED); 2131 notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED); 2132 } else { 2133 // clean slate after call end. 2134 resetPollStats(); 2135 } 2136 } else { 2137 // reset reconnect timer 2138 setupDataOnReadyApns(Phone.REASON_VOICE_CALL_ENDED); 2139 } 2140 } 2141 2142 @Override 2143 protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) { 2144 if (DBG) log("onCleanUpConnection"); 2145 ApnContext apnContext = mApnContexts.get(apnIdToType(apnId)); 2146 if (apnContext != null) { 2147 apnContext.setReason(reason); 2148 cleanUpConnection(tearDown, apnContext); 2149 } 2150 } 2151 2152 protected boolean isConnected() { 2153 for (ApnContext apnContext : mApnContexts.values()) { 2154 if (apnContext.getState() == State.CONNECTED) { 2155 // At least one context is connected, return true 2156 return true; 2157 } 2158 } 2159 // There are not any contexts connected, return false 2160 return false; 2161 } 2162 2163 @Override 2164 public boolean isDisconnected() { 2165 for (ApnContext apnContext : mApnContexts.values()) { 2166 if (!apnContext.isDisconnected()) { 2167 // At least one context was not disconnected return false 2168 return false; 2169 } 2170 } 2171 // All contexts were disconnected so return true 2172 return true; 2173 } 2174 2175 @Override 2176 protected void notifyDataConnection(String reason) { 2177 if (DBG) log("notifyDataConnection: reason=" + reason); 2178 for (ApnContext apnContext : mApnContexts.values()) { 2179 if (apnContext.isReady()) { 2180 if (DBG) log("notifyDataConnection: type:"+apnContext.getApnType()); 2181 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(), 2182 apnContext.getApnType()); 2183 } 2184 } 2185 notifyOffApnsOfAvailability(reason); 2186 } 2187 2188 /** 2189 * Based on the sim operator numeric, create a list for all possible 2190 * Data Connections and setup the preferredApn. 2191 */ 2192 private void createAllApnList() { 2193 mAllApns = new ArrayList<ApnSetting>(); 2194 String operator = mPhone.mIccRecords.getOperatorNumeric(); 2195 if (operator != null) { 2196 String selection = "numeric = '" + operator + "'"; 2197 // query only enabled apn. 2198 // carrier_enabled : 1 means enabled apn, 0 disabled apn. 2199 selection += " and carrier_enabled = 1"; 2200 if (DBG) log("createAllApnList: selection=" + selection); 2201 2202 Cursor cursor = mPhone.getContext().getContentResolver().query( 2203 Telephony.Carriers.CONTENT_URI, null, selection, null, null); 2204 2205 if (cursor != null) { 2206 if (cursor.getCount() > 0) { 2207 mAllApns = createApnList(cursor); 2208 } 2209 cursor.close(); 2210 } 2211 } 2212 2213 if (mAllApns.isEmpty()) { 2214 if (DBG) log("createAllApnList: No APN found for carrier: " + operator); 2215 mPreferredApn = null; 2216 // TODO: What is the right behaviour? 2217 //notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN); 2218 } else { 2219 mPreferredApn = getPreferredApn(); 2220 if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) { 2221 mPreferredApn = null; 2222 setPreferredApn(-1); 2223 } 2224 if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn); 2225 } 2226 if (DBG) log("createAllApnList: X mAllApns=" + mAllApns); 2227 } 2228 2229 /** Return the id for a new data connection */ 2230 private GsmDataConnection createDataConnection() { 2231 if (DBG) log("createDataConnection E"); 2232 2233 RetryManager rm = new RetryManager(); 2234 int id = mUniqueIdGenerator.getAndIncrement(); 2235 GsmDataConnection conn = GsmDataConnection.makeDataConnection(mPhone, id, rm, this); 2236 mDataConnections.put(id, conn); 2237 DataConnectionAc dcac = new DataConnectionAc(conn, LOG_TAG); 2238 int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler()); 2239 if (status == AsyncChannel.STATUS_SUCCESSFUL) { 2240 mDataConnectionAsyncChannels.put(dcac.dataConnection.getDataConnectionId(), dcac); 2241 } else { 2242 loge("createDataConnection: Could not connect to dcac.mDc=" + dcac.dataConnection + 2243 " status=" + status); 2244 } 2245 2246 // install reconnect intent filter for this data connection. 2247 IntentFilter filter = new IntentFilter(); 2248 filter.addAction(INTENT_RECONNECT_ALARM + '.' + id); 2249 mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone); 2250 2251 if (DBG) log("createDataConnection() X id=" + id); 2252 return conn; 2253 } 2254 2255 private void configureRetry(DataConnection dc, boolean forDefault) { 2256 if (dc == null) return; 2257 2258 if (!dc.configureRetry(getReryConfig(forDefault))) { 2259 if (forDefault) { 2260 if (!dc.configureRetry(DEFAULT_DATA_RETRY_CONFIG)) { 2261 // Should never happen, log an error and default to a simple linear sequence. 2262 loge("configureRetry: Could not configure using " + 2263 "DEFAULT_DATA_RETRY_CONFIG=" + DEFAULT_DATA_RETRY_CONFIG); 2264 dc.configureRetry(20, 2000, 1000); 2265 } 2266 } else { 2267 if (!dc.configureRetry(SECONDARY_DATA_RETRY_CONFIG)) { 2268 // Should never happen, log an error and default to a simple sequence. 2269 loge("configureRetry: Could note configure using " + 2270 "SECONDARY_DATA_RETRY_CONFIG=" + SECONDARY_DATA_RETRY_CONFIG); 2271 dc.configureRetry("max_retries=3, 333, 333, 333"); 2272 } 2273 } 2274 } 2275 } 2276 2277 private void destroyDataConnections() { 2278 if(mDataConnections != null) { 2279 if (DBG) log("destroyDataConnections: clear mDataConnectionList"); 2280 mDataConnections.clear(); 2281 } else { 2282 if (DBG) log("destroyDataConnections: mDataConnecitonList is empty, ignore"); 2283 } 2284 } 2285 2286 /** 2287 * Build a list of APNs to be used to create PDP's. 2288 * 2289 * @param requestedApnType 2290 * @return waitingApns list to be used to create PDP 2291 * error when waitingApns.isEmpty() 2292 */ 2293 private ArrayList<ApnSetting> buildWaitingApns(String requestedApnType) { 2294 ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>(); 2295 2296 if (requestedApnType.equals(Phone.APN_TYPE_DUN)) { 2297 ApnSetting dun = fetchDunApn(); 2298 if (dun != null) { 2299 apnList.add(dun); 2300 if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList); 2301 return apnList; 2302 } 2303 } 2304 2305 String operator = mPhone.mIccRecords.getOperatorNumeric(); 2306 int radioTech = mPhone.getServiceState().getRadioTechnology(); 2307 2308 if (requestedApnType.equals(Phone.APN_TYPE_DEFAULT)) { 2309 if (canSetPreferApn && mPreferredApn != null) { 2310 if (DBG) { 2311 log("buildWaitingApns: Preferred APN:" + operator + ":" 2312 + mPreferredApn.numeric + ":" + mPreferredApn); 2313 } 2314 if (mPreferredApn.numeric.equals(operator)) { 2315 if (mPreferredApn.bearer == 0 || mPreferredApn.bearer == radioTech) { 2316 apnList.add(mPreferredApn); 2317 if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList); 2318 return apnList; 2319 } else { 2320 if (DBG) log("buildWaitingApns: no preferred APN"); 2321 setPreferredApn(-1); 2322 mPreferredApn = null; 2323 } 2324 } else { 2325 if (DBG) log("buildWaitingApns: no preferred APN"); 2326 setPreferredApn(-1); 2327 mPreferredApn = null; 2328 } 2329 } 2330 } 2331 if (mAllApns != null) { 2332 for (ApnSetting apn : mAllApns) { 2333 if (apn.canHandleType(requestedApnType)) { 2334 if (apn.bearer == 0 || apn.bearer == radioTech) { 2335 if (DBG) log("apn info : " +apn.toString()); 2336 apnList.add(apn); 2337 } 2338 } 2339 } 2340 } else { 2341 loge("mAllApns is empty!"); 2342 } 2343 if (DBG) log("buildWaitingApns: X apnList=" + apnList); 2344 return apnList; 2345 } 2346 2347 private String apnListToString (ArrayList<ApnSetting> apns) { 2348 StringBuilder result = new StringBuilder(); 2349 for (int i = 0, size = apns.size(); i < size; i++) { 2350 result.append('[') 2351 .append(apns.get(i).toString()) 2352 .append(']'); 2353 } 2354 return result.toString(); 2355 } 2356 2357 private void startDelayedRetry(GsmDataConnection.FailCause cause, 2358 ApnContext apnContext, int retryOverride) { 2359 notifyNoData(cause, apnContext); 2360 reconnectAfterFail(cause, apnContext, retryOverride); 2361 } 2362 2363 private void setPreferredApn(int pos) { 2364 if (!canSetPreferApn) { 2365 log("setPreferredApn: X !canSEtPreferApn"); 2366 return; 2367 } 2368 2369 log("setPreferredApn: delete"); 2370 ContentResolver resolver = mPhone.getContext().getContentResolver(); 2371 resolver.delete(PREFERAPN_NO_UPDATE_URI, null, null); 2372 2373 if (pos >= 0) { 2374 log("setPreferredApn: insert"); 2375 ContentValues values = new ContentValues(); 2376 values.put(APN_ID, pos); 2377 resolver.insert(PREFERAPN_NO_UPDATE_URI, values); 2378 } 2379 } 2380 2381 private ApnSetting getPreferredApn() { 2382 if (mAllApns.isEmpty()) { 2383 log("getPreferredApn: X not found mAllApns.isEmpty"); 2384 return null; 2385 } 2386 2387 Cursor cursor = mPhone.getContext().getContentResolver().query( 2388 PREFERAPN_NO_UPDATE_URI, new String[] { "_id", "name", "apn" }, 2389 null, null, Telephony.Carriers.DEFAULT_SORT_ORDER); 2390 2391 if (cursor != null) { 2392 canSetPreferApn = true; 2393 } else { 2394 canSetPreferApn = false; 2395 } 2396 2397 if (canSetPreferApn && cursor.getCount() > 0) { 2398 int pos; 2399 cursor.moveToFirst(); 2400 pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)); 2401 for(ApnSetting p:mAllApns) { 2402 if (p.id == pos && p.canHandleType(mRequestedApnType)) { 2403 log("getPreferredApn: X found apnSetting" + p); 2404 cursor.close(); 2405 return p; 2406 } 2407 } 2408 } 2409 2410 if (cursor != null) { 2411 cursor.close(); 2412 } 2413 2414 log("getPreferredApn: X not found"); 2415 return null; 2416 } 2417 2418 @Override 2419 public void handleMessage (Message msg) { 2420 if (DBG) log("handleMessage msg=" + msg); 2421 2422 if (!mPhone.mIsTheCurrentActivePhone || mIsDisposed) { 2423 loge("handleMessage: Ignore GSM msgs since GSM phone is inactive"); 2424 return; 2425 } 2426 2427 switch (msg.what) { 2428 case EVENT_RECORDS_LOADED: 2429 onRecordsLoaded(); 2430 break; 2431 2432 case EVENT_DATA_CONNECTION_DETACHED: 2433 onDataConnectionDetached(); 2434 break; 2435 2436 case EVENT_DATA_CONNECTION_ATTACHED: 2437 onDataConnectionAttached(); 2438 break; 2439 2440 case EVENT_DATA_STATE_CHANGED: 2441 onDataStateChanged((AsyncResult) msg.obj); 2442 break; 2443 2444 case EVENT_POLL_PDP: 2445 onPollPdp(); 2446 break; 2447 2448 case EVENT_DO_RECOVERY: 2449 doRecovery(); 2450 break; 2451 2452 case EVENT_APN_CHANGED: 2453 onApnChanged(); 2454 break; 2455 2456 case EVENT_PS_RESTRICT_ENABLED: 2457 /** 2458 * We don't need to explicitly to tear down the PDP context 2459 * when PS restricted is enabled. The base band will deactive 2460 * PDP context and notify us with PDP_CONTEXT_CHANGED. 2461 * But we should stop the network polling and prevent reset PDP. 2462 */ 2463 if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted); 2464 stopNetStatPoll(); 2465 stopDataStallAlarm(); 2466 mIsPsRestricted = true; 2467 break; 2468 2469 case EVENT_PS_RESTRICT_DISABLED: 2470 /** 2471 * When PS restrict is removed, we need setup PDP connection if 2472 * PDP connection is down. 2473 */ 2474 if (DBG) log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted); 2475 mIsPsRestricted = false; 2476 if (isConnected()) { 2477 startNetStatPoll(); 2478 startDataStallAlarm(DATA_STALL_NOT_SUSPECTED); 2479 } else { 2480 // TODO: Should all PDN states be checked to fail? 2481 if (mState == State.FAILED) { 2482 cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED); 2483 resetAllRetryCounts(); 2484 mReregisterOnReconnectFailure = false; 2485 } 2486 trySetupData(Phone.REASON_PS_RESTRICT_ENABLED, Phone.APN_TYPE_DEFAULT); 2487 } 2488 break; 2489 case EVENT_TRY_SETUP_DATA: 2490 if (msg.obj instanceof ApnContext) { 2491 onTrySetupData((ApnContext)msg.obj); 2492 } else if (msg.obj instanceof String) { 2493 onTrySetupData((String)msg.obj); 2494 } else { 2495 loge("EVENT_TRY_SETUP request w/o apnContext or String"); 2496 } 2497 break; 2498 2499 case EVENT_CLEAN_UP_CONNECTION: 2500 boolean tearDown = (msg.arg1 == 0) ? false : true; 2501 if (DBG) log("EVENT_CLEAN_UP_CONNECTION tearDown=" + tearDown); 2502 if (msg.obj instanceof ApnContext) { 2503 cleanUpConnection(tearDown, (ApnContext)msg.obj); 2504 } else { 2505 loge("EVENT_CLEAN_UP_CONNECTION request w/o apn context"); 2506 } 2507 break; 2508 default: 2509 // handle the message in the super class DataConnectionTracker 2510 super.handleMessage(msg); 2511 break; 2512 } 2513 } 2514 2515 protected int getApnProfileID(String apnType) { 2516 if (TextUtils.equals(apnType, Phone.APN_TYPE_IMS)) { 2517 return RILConstants.DATA_PROFILE_IMS; 2518 } else if (TextUtils.equals(apnType, Phone.APN_TYPE_FOTA)) { 2519 return RILConstants.DATA_PROFILE_FOTA; 2520 } else if (TextUtils.equals(apnType, Phone.APN_TYPE_CBS)) { 2521 return RILConstants.DATA_PROFILE_CBS; 2522 } else { 2523 return RILConstants.DATA_PROFILE_DEFAULT; 2524 } 2525 } 2526 2527 private int getCellLocationId() { 2528 int cid = -1; 2529 CellLocation loc = mPhone.getCellLocation(); 2530 2531 if (loc != null) { 2532 if (loc instanceof GsmCellLocation) { 2533 cid = ((GsmCellLocation)loc).getCid(); 2534 } else if (loc instanceof CdmaCellLocation) { 2535 cid = ((CdmaCellLocation)loc).getBaseStationId(); 2536 } 2537 } 2538 return cid; 2539 } 2540 2541 @Override 2542 protected void log(String s) { 2543 Log.d(LOG_TAG, "[GsmDCT] "+ s); 2544 } 2545 2546 @Override 2547 protected void loge(String s) { 2548 Log.e(LOG_TAG, "[GsmDCT] " + s); 2549 } 2550 } 2551