2 * Copyright (C) 2006 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.internal.telephony.gsm;
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.LinkProperties.CompareResult;
31 import android.net.NetworkUtils;
32 import android.net.ProxyProperties;
33 import android.net.TrafficStats;
34 import android.net.Uri;
35 import android.net.LinkCapabilities;
36 import android.net.LinkProperties;
37 import android.net.NetworkConfig;
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 import android.preference.PreferenceManager;
54 import com.android.internal.R;
55 import com.android.internal.telephony.ApnContext;
56 import com.android.internal.telephony.ApnSetting;
57 import com.android.internal.telephony.DataCallState;
58 import com.android.internal.telephony.DataConnection;
59 import com.android.internal.telephony.DataConnection.UpdateLinkPropertyResult;
60 import com.android.internal.telephony.DataConnectionAc;
61 import com.android.internal.telephony.DataConnectionTracker;
62 import com.android.internal.telephony.Phone;
63 import com.android.internal.telephony.PhoneBase;
64 import com.android.internal.telephony.RetryManager;
65 import com.android.internal.telephony.EventLogTags;
66 import com.android.internal.telephony.DataConnection.FailCause;
67 import com.android.internal.telephony.RILConstants;
68 import com.android.internal.util.AsyncChannel;
70 import java.io.IOException;
71 import java.net.InetAddress;
72 import java.net.InetSocketAddress;
73 import java.net.UnknownHostException;
74 import java.util.ArrayList;
75 import java.util.Collection;
76 import java.util.concurrent.ConcurrentHashMap;
77 import java.util.List;
79 import java.util.HashMap;
84 public final class GsmDataConnectionTracker extends DataConnectionTracker {
85 protected final String LOG_TAG = "GSM";
88 * Handles changes to the APN db.
90 private class ApnChangeObserver extends ContentObserver {
91 public ApnChangeObserver () {
92 super(mDataConnectionTracker);
96 public void onChange(boolean selfChange) {
97 sendMessage(obtainMessage(EVENT_APN_CHANGED));
101 //***** Instance Variables
103 private boolean mReregisterOnReconnectFailure = false;
104 private ContentResolver mResolver;
106 // Count of PDP reset attempts; reset when we see incoming,
107 // call reRegisterNetwork, or pingTest succeeds.
108 private int mPdpResetCount = 0;
110 // Recovery action taken in case of data stall
111 enum RecoveryAction {REREGISTER, RADIO_RESTART, RADIO_RESET};
112 private RecoveryAction mRecoveryAction = RecoveryAction.REREGISTER;
117 private static final int POLL_PDP_MILLIS = 5 * 1000;
119 private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect";
120 private static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "type";
122 static final Uri PREFERAPN_URI = Uri.parse("content://telephony/carriers/preferapn");
123 static final String APN_ID = "apn_id";
124 private boolean canSetPreferApn = false;
125 private boolean mRadioAvailable = false;
128 protected void onActionIntentReconnectAlarm(Intent intent) {
129 if (DBG) log("GPRS reconnect alarm. Previous state was " + mState);
131 String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
132 int connectionId = intent.getIntExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, -1);
134 DataConnectionAc dcac= mDataConnectionAsyncChannels.get(connectionId);
137 for (ApnContext apnContext : dcac.getApnListSync()) {
138 apnContext.setReason(reason);
139 if (apnContext.getState() == State.FAILED) {
140 apnContext.setState(State.IDLE);
142 sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext));
144 // Alram had expired. Clear pending intent recorded on the DataConnection.
145 dcac.setReconnectIntentSync(null);
149 /** Watches for changes to the APN db. */
150 private ApnChangeObserver mApnObserver;
154 public GsmDataConnectionTracker(PhoneBase p) {
157 p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null);
158 p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
159 p.mIccRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
160 p.mCM.registerForDataNetworkStateChanged (this, EVENT_DATA_STATE_CHANGED, null);
161 p.getCallTracker().registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null);
162 p.getCallTracker().registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null);
163 p.getServiceStateTracker().registerForDataConnectionAttached(this,
164 EVENT_DATA_CONNECTION_ATTACHED, null);
165 p.getServiceStateTracker().registerForDataConnectionDetached(this,
166 EVENT_DATA_CONNECTION_DETACHED, null);
167 p.getServiceStateTracker().registerForRoamingOn(this, EVENT_ROAMING_ON, null);
168 p.getServiceStateTracker().registerForRoamingOff(this, EVENT_ROAMING_OFF, null);
169 p.getServiceStateTracker().registerForPsRestrictedEnabled(this,
170 EVENT_PS_RESTRICT_ENABLED, null);
171 p.getServiceStateTracker().registerForPsRestrictedDisabled(this,
172 EVENT_PS_RESTRICT_DISABLED, null);
174 mDataConnectionTracker = this;
175 mResolver = mPhone.getContext().getContentResolver();
177 mApnObserver = new ApnChangeObserver();
178 p.getContext().getContentResolver().registerContentObserver(
179 Telephony.Carriers.CONTENT_URI, true, mApnObserver);
181 mApnContexts = new ConcurrentHashMap<String, ApnContext>();
182 initApnContextsAndDataConnection();
183 broadcastMessenger();
187 public void dispose() {
188 cleanUpAllConnections(false, null);
192 //Unregister for all events
193 mPhone.mCM.unregisterForAvailable(this);
194 mPhone.mCM.unregisterForOffOrNotAvailable(this);
195 mPhone.mIccRecords.unregisterForRecordsLoaded(this);
196 mPhone.mCM.unregisterForDataNetworkStateChanged(this);
197 mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
198 mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
199 mPhone.getServiceStateTracker().unregisterForDataConnectionAttached(this);
200 mPhone.getServiceStateTracker().unregisterForDataConnectionDetached(this);
201 mPhone.getServiceStateTracker().unregisterForRoamingOn(this);
202 mPhone.getServiceStateTracker().unregisterForRoamingOff(this);
203 mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this);
204 mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this);
206 mPhone.getContext().getContentResolver().unregisterContentObserver(this.mApnObserver);
207 mApnContexts.clear();
209 destroyDataConnections();
213 public boolean isApnTypeActive(String type) {
214 ApnContext apnContext = mApnContexts.get(type);
215 if (apnContext == null) return false;
217 return (apnContext.getDataConnection() != null);
221 protected boolean isDataPossible(String apnType) {
222 ApnContext apnContext = mApnContexts.get(apnType);
223 if (apnContext == null) {
226 boolean apnContextIsEnabled = apnContext.isEnabled();
227 State apnContextState = apnContext.getState();
228 boolean apnTypePossible = !(apnContextIsEnabled &&
229 (apnContextState == State.FAILED));
230 boolean dataAllowed = isDataAllowed();
231 boolean possible = dataAllowed && apnTypePossible;
234 log(String.format("isDataPossible(%s): possible=%b isDataAllowed=%b " +
235 "apnTypePossible=%b apnContextisEnabled=%b apnContextState()=%s",
236 apnType, possible, dataAllowed, apnTypePossible,
237 apnContextIsEnabled, apnContextState));
243 protected void finalize() {
244 if(DBG) log("finalize");
248 protected String getActionIntentReconnectAlarm() {
249 return INTENT_RECONNECT_ALARM;
252 private ApnContext addApnContext(String type) {
253 ApnContext apnContext = new ApnContext(type, LOG_TAG);
254 apnContext.setDependencyMet(false);
255 mApnContexts.put(type, apnContext);
259 protected void initApnContextsAndDataConnection() {
260 boolean defaultEnabled = SystemProperties.getBoolean(DEFALUT_DATA_ON_BOOT_PROP, true);
261 // Load device network attributes from resources
262 String[] networkConfigStrings = mPhone.getContext().getResources().getStringArray(
263 com.android.internal.R.array.networkAttributes);
264 for (String networkConfigString : networkConfigStrings) {
265 NetworkConfig networkConfig = new NetworkConfig(networkConfigString);
266 ApnContext apnContext = null;
268 switch (networkConfig.type) {
269 case ConnectivityManager.TYPE_MOBILE:
270 apnContext = addApnContext(Phone.APN_TYPE_DEFAULT);
271 apnContext.setEnabled(defaultEnabled);
273 case ConnectivityManager.TYPE_MOBILE_MMS:
274 apnContext = addApnContext(Phone.APN_TYPE_MMS);
276 case ConnectivityManager.TYPE_MOBILE_SUPL:
277 apnContext = addApnContext(Phone.APN_TYPE_SUPL);
279 case ConnectivityManager.TYPE_MOBILE_DUN:
280 apnContext = addApnContext(Phone.APN_TYPE_DUN);
282 case ConnectivityManager.TYPE_MOBILE_HIPRI:
283 apnContext = addApnContext(Phone.APN_TYPE_HIPRI);
285 case ConnectivityManager.TYPE_MOBILE_FOTA:
286 apnContext = addApnContext(Phone.APN_TYPE_FOTA);
288 case ConnectivityManager.TYPE_MOBILE_IMS:
289 apnContext = addApnContext(Phone.APN_TYPE_IMS);
291 case ConnectivityManager.TYPE_MOBILE_CBS:
292 apnContext = addApnContext(Phone.APN_TYPE_CBS);
295 // skip unknown types
298 if (apnContext != null) {
299 // set the prop, but also apply the newly set enabled and dependency values
300 onSetDependencyMet(apnContext.getApnType(), networkConfig.dependencyMet);
306 protected LinkProperties getLinkProperties(String apnType) {
307 ApnContext apnContext = mApnContexts.get(apnType);
308 if (apnContext != null) {
309 DataConnectionAc dcac = apnContext.getDataConnectionAc();
311 if (DBG) log("return link properites for " + apnType);
312 return dcac.getLinkPropertiesSync();
315 if (DBG) log("return new LinkProperties");
316 return new LinkProperties();
320 protected LinkCapabilities getLinkCapabilities(String apnType) {
321 ApnContext apnContext = mApnContexts.get(apnType);
322 if (apnContext!=null) {
323 DataConnectionAc dataConnectionAc = apnContext.getDataConnectionAc();
324 if (dataConnectionAc != null) {
325 if (DBG) log("get active pdp is not null, return link Capabilities for " + apnType);
326 return dataConnectionAc.getLinkCapabilitiesSync();
329 if (DBG) log("return new LinkCapabilities");
330 return new LinkCapabilities();
334 // Return all active apn types
335 public String[] getActiveApnTypes() {
336 if (DBG) log("get all active apn types");
337 ArrayList<String> result = new ArrayList<String>();
339 for (ApnContext apnContext : mApnContexts.values()) {
340 if (apnContext.isReady()) {
341 result.add(apnContext.getApnType());
345 return (String[])result.toArray(new String[0]);
349 // Return active apn of specific apn type
350 public String getActiveApnString(String apnType) {
351 if (DBG) log( "get active apn string for type:" + apnType);
352 ApnContext apnContext = mApnContexts.get(apnType);
353 if (apnContext != null) {
354 ApnSetting apnSetting = apnContext.getApnSetting();
355 if (apnSetting != null) {
356 return apnSetting.apn;
363 public boolean isApnTypeEnabled(String apnType) {
364 ApnContext apnContext = mApnContexts.get(apnType);
365 if (apnContext == null) {
368 return apnContext.isEnabled();
372 protected void setState(State s) {
373 if (DBG) log("setState should not be used in GSM" + s);
376 // Return state of specific apn type
378 public State getState(String apnType) {
379 ApnContext apnContext = mApnContexts.get(apnType);
380 if (apnContext != null) {
381 return apnContext.getState();
386 // Return state of overall
387 public State getOverallState() {
388 boolean isConnecting = false;
389 boolean isFailed = true; // All enabled Apns should be FAILED.
390 boolean isAnyEnabled = false;
392 for (ApnContext apnContext : mApnContexts.values()) {
393 if (apnContext.isEnabled()) {
395 switch (apnContext.getState()) {
398 if (DBG) log("overall state is CONNECTED");
399 return State.CONNECTED;
413 if (!isAnyEnabled) { // Nothing enabled. return IDLE.
414 if (DBG) log( "overall state is IDLE");
419 if (DBG) log( "overall state is CONNECTING");
420 return State.CONNECTING;
421 } else if (!isFailed) {
422 if (DBG) log( "overall state is IDLE");
425 if (DBG) log( "overall state is FAILED");
431 * Ensure that we are connected to an APN of the specified type.
433 * @param type the APN type
434 * @return Success is indicated by {@code Phone.APN_ALREADY_ACTIVE} or
435 * {@code Phone.APN_REQUEST_STARTED}. In the latter case, a
436 * broadcast will be sent by the ConnectivityManager when a
437 * connection to the APN has been established.
440 public synchronized int enableApnType(String apnType) {
441 ApnContext apnContext = mApnContexts.get(apnType);
442 if (apnContext == null || !isApnTypeAvailable(apnType)) {
443 if (DBG) log("enableApnType: " + apnType + " is type not available");
444 return Phone.APN_TYPE_NOT_AVAILABLE;
447 // If already active, return
448 if (DBG) log("enableApnType: " + apnType + " mState(" + apnContext.getState() + ")");
450 if (apnContext.getState() == State.CONNECTED) {
451 if (DBG) log("enableApnType: return APN_ALREADY_ACTIVE");
452 return Phone.APN_ALREADY_ACTIVE;
454 setEnabled(apnTypeToId(apnType), true);
456 log("enableApnType: new apn request for type " + apnType +
457 " return APN_REQUEST_STARTED");
459 return Phone.APN_REQUEST_STARTED;
462 // A new APN has gone active and needs to send events to catch up with the
464 private void notifyApnIdUpToCurrent(String reason, ApnContext apnContext, String type) {
465 switch (apnContext.getState()) {
471 mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTING);
475 mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTING);
476 mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTED);
482 public synchronized int disableApnType(String type) {
483 if (DBG) log("disableApnType:" + type);
484 ApnContext apnContext = mApnContexts.get(type);
486 if (apnContext != null) {
487 setEnabled(apnTypeToId(type), false);
488 if (apnContext.getState() != State.IDLE && apnContext.getState() != State.FAILED) {
489 if (DBG) log("diableApnType: return APN_REQUEST_STARTED");
490 return Phone.APN_REQUEST_STARTED;
492 if (DBG) log("disableApnType: return APN_ALREADY_INACTIVE");
493 return Phone.APN_ALREADY_INACTIVE;
498 log("disableApnType: no apn context was found, return APN_REQUEST_FAILED");
500 return Phone.APN_REQUEST_FAILED;
505 protected boolean isApnTypeAvailable(String type) {
506 if (type.equals(Phone.APN_TYPE_DUN) && fetchDunApn() != null) {
510 if (mAllApns != null) {
511 for (ApnSetting apn : mAllApns) {
512 if (apn.canHandleType(type)) {
521 * Report on whether data connectivity is enabled for any APN.
522 * @return {@code false} if data connectivity has been explicitly disabled,
523 * {@code true} otherwise.
526 public synchronized boolean getAnyDataEnabled() {
527 if (!(mInternalDataEnabled && mDataEnabled)) return false;
528 for (ApnContext apnContext : mApnContexts.values()) {
529 // Make sure we dont have a context that going down
530 // and is explicitly disabled.
531 if (isDataAllowed(apnContext)) {
538 private boolean isDataAllowed(ApnContext apnContext) {
539 return apnContext.isReady() && isDataAllowed();
542 //****** Called from ServiceStateTracker
544 * Invoked when ServiceStateTracker observes a transition from GPRS
547 protected void onDataConnectionDetached() {
549 * We presently believe it is unnecessary to tear down the PDP context
550 * when GPRS detaches, but we should stop the network polling.
552 if (DBG) log ("onDataConnectionDetached: stop polling and notify detached");
554 notifyDataConnection(Phone.REASON_DATA_DETACHED);
557 private void onDataConnectionAttached() {
558 if (DBG) log("onDataConnectionAttached");
559 if (getOverallState() == State.CONNECTED) {
560 if (DBG) log("onDataConnectionAttached: start polling notify attached");
562 notifyDataConnection(Phone.REASON_DATA_ATTACHED);
564 // update APN availability so that APN can be enabled.
565 notifyDataAvailability(Phone.REASON_DATA_ATTACHED);
568 setupDataOnReadyApns(Phone.REASON_DATA_ATTACHED);
572 protected boolean isDataAllowed() {
573 int gprsState = mPhone.getServiceStateTracker().getCurrentDataConnectionState();
574 boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
577 (gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) &&
578 mPhone.mIccRecords.getRecordsLoaded() &&
579 mPhone.mIccRecords.isProvisioned() &&
580 mPhone.getState() == Phone.State.IDLE &&
581 mInternalDataEnabled &&
582 (!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) &&
585 if (!allowed && DBG) {
587 if (!((gprsState == ServiceState.STATE_IN_SERVICE) || mAutoAttachOnCreation)) {
588 reason += " - gprs= " + gprsState;
590 if (!mPhone.mIccRecords.getRecordsLoaded()) reason += " - SIM not loaded";
591 if (!mPhone.mIccRecords.isProvisioned()) reason += " - SIM not provisioned";
592 if (mPhone.getState() != Phone.State.IDLE) {
593 reason += " - PhoneState= " + mPhone.getState();
595 if (!mInternalDataEnabled) reason += " - mInternalDataEnabled= false";
596 if (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled()) {
597 reason += " - Roaming and data roaming not enabled";
599 if (mIsPsRestricted) reason += " - mIsPsRestricted= true";
600 if (!desiredPowerState) reason += " - desiredPowerState= false";
601 if (DBG) log("isDataAllowed: not allowed due to" + reason);
606 private void setupDataOnReadyApns(String reason) {
607 // Stop reconnect alarms on all data connections pending
608 // retry. Reset ApnContext state to IDLE.
609 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
610 if (dcac.getReconnectIntentSync() != null) {
611 cancelReconnectAlarm(dcac);
613 // update retry config for existing calls to match up
614 // ones for the new RAT.
615 if (dcac.dataConnection != null) {
616 Collection<ApnContext> apns = dcac.getApnListSync();
618 boolean hasDefault = false;
619 for (ApnContext apnContext : apns) {
620 if (apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)) {
625 configureRetry(dcac.dataConnection, hasDefault);
629 // Only check for default APN state
630 for (ApnContext apnContext : mApnContexts.values()) {
631 if (apnContext.getState() == State.FAILED) {
632 // By this time, alarms for all failed Apns
633 // should be stopped if any.
634 // Make sure to set the state back to IDLE
635 // so that setup data can happen.
636 apnContext.setState(State.IDLE);
638 if (apnContext.isReady()) {
639 if (apnContext.getState() == State.IDLE) {
640 apnContext.setReason(reason);
641 trySetupData(apnContext);
647 private boolean trySetupData(String reason, String type) {
649 log("trySetupData: " + type + " due to " + (reason == null ? "(unspecified)" : reason)
650 + " isPsRestricted=" + mIsPsRestricted);
654 type = Phone.APN_TYPE_DEFAULT;
657 ApnContext apnContext = mApnContexts.get(type);
659 if (apnContext == null ){
660 if (DBG) log("trySetupData new apn context for type:" + type);
661 apnContext = new ApnContext(type, LOG_TAG);
662 mApnContexts.put(type, apnContext);
664 apnContext.setReason(reason);
666 return trySetupData(apnContext);
669 private boolean trySetupData(ApnContext apnContext) {
671 log("trySetupData for type:" + apnContext.getApnType() +
672 " due to " + apnContext.getReason());
673 log("trySetupData with mIsPsRestricted=" + mIsPsRestricted);
676 if (mPhone.getSimulatedRadioControl() != null) {
677 // Assume data is connected on the simulator
678 // FIXME this can be improved
679 apnContext.setState(State.CONNECTED);
680 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
682 log("trySetupData: (fix?) We're on the simulator; assuming data is connected");
686 boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
688 if ((apnContext.getState() == State.IDLE || apnContext.getState() == State.SCANNING) &&
689 isDataAllowed(apnContext) && getAnyDataEnabled()) {
691 if (apnContext.getState() == State.IDLE) {
692 ArrayList<ApnSetting> waitingApns = buildWaitingApns(apnContext.getApnType());
693 if (waitingApns.isEmpty()) {
694 if (DBG) log("trySetupData: No APN found");
695 notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN, apnContext);
696 notifyOffApnsOfAvailability(apnContext.getReason(), false);
699 apnContext.setWaitingApns(waitingApns);
701 log ("trySetupData: Create from mAllApns : " + apnListToString(mAllApns));
707 log ("Setup watingApns : " + apnListToString(apnContext.getWaitingApns()));
709 // apnContext.setReason(apnContext.getReason());
710 boolean retValue = setupData(apnContext);
711 notifyOffApnsOfAvailability(apnContext.getReason(), retValue);
714 // TODO: check the condition.
715 if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)
716 && (apnContext.getState() == State.IDLE
717 || apnContext.getState() == State.SCANNING))
718 mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
719 notifyOffApnsOfAvailability(apnContext.getReason(), false);
725 // Disabled apn's still need avail/unavail notificiations - send them out
726 protected void notifyOffApnsOfAvailability(String reason, boolean availability) {
727 if (mAvailability == availability) return;
728 mAvailability = availability;
730 for (ApnContext apnContext : mApnContexts.values()) {
731 if (!apnContext.isReady()) {
732 if (DBG) log("notifyOffApnOfAvailability type:" + apnContext.getApnType());
733 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
734 apnContext.getApnType(),
735 Phone.DataState.DISCONNECTED);
741 * If tearDown is true, this only tears down a CONNECTED session. Presently,
742 * there is no mechanism for abandoning an INITING/CONNECTING session,
743 * but would likely involve cancelling pending async requests or
744 * setting a flag or new state to ignore them when they came in
745 * @param tearDown true if the underlying GsmDataConnection should be
747 * @param reason reason for the clean up.
749 protected void cleanUpAllConnections(boolean tearDown, String reason) {
750 if (DBG) log("cleanUpAllConnections: tearDown=" + tearDown + " reason=" + reason);
752 for (ApnContext apnContext : mApnContexts.values()) {
753 apnContext.setReason(reason);
754 cleanUpConnection(tearDown, apnContext);
758 // TODO: Do we need mRequestedApnType?
759 mRequestedApnType = Phone.APN_TYPE_DEFAULT;
763 * Cleanup all connections.
765 * TODO: Cleanup only a specified connection passed as a parameter.
766 * Also, make sure when you clean up a conn, if it is last apply
767 * logic as though it is cleanupAllConnections
769 * @param tearDown true if the underlying DataConnection should be disconnected.
770 * @param reason for the clean up.
774 protected void onCleanUpAllConnections(String cause) {
775 cleanUpAllConnections(true, cause);
778 private void cleanUpConnection(boolean tearDown, ApnContext apnContext) {
780 if (apnContext == null) {
781 if (DBG) log("cleanUpConnection: apn context is null");
786 log("cleanUpConnection: tearDown=" + tearDown + " reason=" + apnContext.getReason());
788 DataConnectionAc dcac = apnContext.getDataConnectionAc();
790 boolean isConnected = (apnContext.getState() != State.IDLE
791 && apnContext.getState() != State.FAILED);
793 // The request is tearDown and but ApnContext is not connected.
794 // If apnContext is not enabled anymore, break the linkage to the DCAC/DC.
795 apnContext.setState(State.IDLE);
796 if (!apnContext.isReady()) {
797 apnContext.setDataConnection(null);
798 apnContext.setDataConnectionAc(null);
801 // Connection is still there. Try to clean up.
803 if (apnContext.getState() != State.DISCONNECTING) {
804 if (DBG) log("cleanUpConnection: tearing down");
805 Message msg = obtainMessage(EVENT_DISCONNECT_DONE, apnContext);
806 apnContext.getDataConnection().tearDown(apnContext.getReason(), msg);
807 apnContext.setState(State.DISCONNECTING);
810 // apn is connected but no reference to dcac.
811 // Should not be happen, but reset the state in case.
812 apnContext.setState(State.IDLE);
813 mPhone.notifyDataConnection(apnContext.getReason(),
814 apnContext.getApnType());
818 // force clean up the data connection.
819 if (dcac != null) dcac.resetSync();
820 apnContext.setState(State.IDLE);
821 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
822 apnContext.setDataConnection(null);
823 apnContext.setDataConnectionAc(null);
826 // make sure reconnection alarm is cleaned up if there is no ApnContext
827 // associated to the connection.
829 Collection<ApnContext> apnList = dcac.getApnListSync();
830 if (apnList.isEmpty()) {
831 cancelReconnectAlarm(dcac);
837 * Cancels the alarm associated with DCAC.
839 * @param DataConnectionAc on which the alarm should be stopped.
841 private void cancelReconnectAlarm(DataConnectionAc dcac) {
842 if (dcac == null) return;
844 PendingIntent intent = dcac.getReconnectIntentSync();
846 if (intent != null) {
848 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
850 dcac.setReconnectIntentSync(null);
855 * @param types comma delimited list of APN types
856 * @return array of APN types
858 private String[] parseTypes(String types) {
860 // If unset, set to DEFAULT.
861 if (types == null || types.equals("")) {
862 result = new String[1];
863 result[0] = Phone.APN_TYPE_ALL;
865 result = types.split(",");
870 private ArrayList<ApnSetting> createApnList(Cursor cursor) {
871 ArrayList<ApnSetting> result = new ArrayList<ApnSetting>();
872 if (cursor.moveToFirst()) {
874 String[] types = parseTypes(
875 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
876 ApnSetting apn = new ApnSetting(
877 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
878 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
879 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
880 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
881 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY)),
882 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)),
883 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC)),
884 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY)),
885 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)),
886 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
887 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
888 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
890 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
891 cursor.getString(cursor.getColumnIndexOrThrow(
892 Telephony.Carriers.ROAMING_PROTOCOL)));
894 } while (cursor.moveToNext());
896 if (DBG) log("createApnList: X result=" + result);
900 private boolean dataConnectionNotInUse(DataConnectionAc dcac) {
901 for (ApnContext apnContext : mApnContexts.values()) {
902 if (apnContext.getDataConnectionAc() == dcac) return false;
907 private GsmDataConnection findFreeDataConnection() {
908 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
909 if (dcac.isInactiveSync() && dataConnectionNotInUse(dcac)) {
910 log("findFreeDataConnection: found free GsmDataConnection");
911 return (GsmDataConnection) dcac.dataConnection;
914 log("findFreeDataConnection: NO free GsmDataConnection");
918 protected GsmDataConnection findReadyDataConnection(ApnSetting apn) {
920 log("findReadyDataConnection: apn string <" +
921 (apn!=null?(apn.toString()):"null") +">");
925 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
926 ApnSetting apnSetting = dcac.getApnSettingSync();
928 log("findReadyDataConnection: dc apn string <" +
929 (apnSetting != null ? (apnSetting.toString()) : "null") + ">");
931 if ((apnSetting != null) && TextUtils.equals(apnSetting.toString(), apn.toString())) {
932 return (GsmDataConnection) dcac.dataConnection;
939 private boolean setupData(ApnContext apnContext) {
940 if (DBG) log("setupData: apnContext=" + apnContext);
942 GsmDataConnection dc;
944 int profileId = getApnProfileID(apnContext.getApnType());
945 apn = apnContext.getNextWaitingApn();
947 if (DBG) log("setupData: return for no apn found!");
951 // First, check to see if ApnContext already has DC.
952 // This could happen if the retries are currently engaged.
953 dc = (GsmDataConnection)apnContext.getDataConnection();
957 dc = (GsmDataConnection) checkForConnectionForApnContext(apnContext);
960 dc = findReadyDataConnection(apn);
964 if (DBG) log("setupData: No ready GsmDataConnection found!");
965 // TODO: When allocating you are mapping type to id. If more than 1 free,
966 // then could findFreeDataConnection get the wrong one??
967 dc = findFreeDataConnection();
971 dc = createDataConnection();
975 if (DBG) log("setupData: No free GsmDataConnection found!");
979 DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId());
980 dc.setProfileId( profileId );
981 dc.setActiveApnType(apnContext.getApnType());
982 int refCount = dcac.getRefCountSync();
983 if (DBG) log("setupData: init dc and apnContext refCount=" + refCount);
985 // configure retry count if no other Apn is using the same connection.
987 configureRetry(dc, apn.canHandleType(Phone.APN_TYPE_DEFAULT));
989 apnContext.setDataConnectionAc(dcac);
990 apnContext.setDataConnection(dc);
993 apnContext.setApnSetting(apn);
994 apnContext.setState(State.INITING);
995 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
996 // If reconnect alarm is active on this DataConnection, wait for the alarm being
997 // fired so that we don't disruppt data retry pattern engaged.
998 if (apnContext.getDataConnectionAc().getReconnectIntentSync() != null) {
999 if (DBG) log("setupData: data reconnection pending");
1000 apnContext.setState(State.FAILED);
1001 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1005 Message msg = obtainMessage();
1006 msg.what = EVENT_DATA_SETUP_COMPLETE;
1007 msg.obj = apnContext;
1008 dc.bringUp(msg, apn);
1010 if (DBG) log("setupData: initing!");
1015 * Handles changes to the APN database.
1017 private void onApnChanged() {
1018 // TODO: How to handle when multiple APNs are active?
1019 boolean isConnected;
1021 ApnContext defaultApnContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT);
1022 isConnected = (defaultApnContext.getState() != State.IDLE
1023 && defaultApnContext.getState() != State.FAILED);
1025 if (mPhone instanceof GSMPhone) {
1026 // The "current" may no longer be valid. MMS depends on this to send properly. TBD
1027 ((GSMPhone)mPhone).updateCurrentCarrierInProvider();
1030 // TODO: It'd be nice to only do this if the changed entrie(s)
1031 // match the current operator.
1032 if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections");
1034 cleanUpAllConnections(isConnected, Phone.REASON_APN_CHANGED);
1036 setupDataOnReadyApns(Phone.REASON_APN_CHANGED);
1041 * @param cid Connection id provided from RIL.
1042 * @return DataConnectionAc associated with specified cid.
1044 private DataConnectionAc findDataConnectionAcByCid(int cid) {
1045 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
1046 if (dcac.getCidSync() == cid) {
1054 * @param dcacs Collection of DataConnectionAc reported from RIL.
1055 * @return List of ApnContext which is connected, but is not present in
1056 * data connection list reported from RIL.
1058 private List<ApnContext> findApnContextToClean(Collection<DataConnectionAc> dcacs) {
1059 if (dcacs == null) return null;
1061 ArrayList<ApnContext> list = new ArrayList<ApnContext>();
1062 for (ApnContext apnContext : mApnContexts.values()) {
1063 if (apnContext.getState() == State.CONNECTED) {
1064 boolean found = false;
1065 for (DataConnectionAc dcac : dcacs) {
1066 if (dcac == apnContext.getDataConnectionAc()) {
1067 // ApnContext holds the ref to dcac present in data call list.
1073 // ApnContext does not have dcac reported in data call list.
1074 // Fetch all the ApnContexts that map to this dcac which are in
1075 // INITING state too.
1076 if (DBG) log("onDataStateChanged(ar): Connected apn not found in the list (" +
1077 apnContext.toString() + ")");
1078 if (apnContext.getDataConnectionAc() != null) {
1079 list.addAll(apnContext.getDataConnectionAc().getApnListSync());
1081 list.add(apnContext);
1090 * @param ar is the result of RIL_REQUEST_DATA_CALL_LIST
1091 * or RIL_UNSOL_DATA_CALL_LIST_CHANGED
1093 private void onDataStateChanged (AsyncResult ar) {
1094 ArrayList<DataCallState> dataCallStates;
1096 if (DBG) log("onDataStateChanged(ar): E");
1097 dataCallStates = (ArrayList<DataCallState>)(ar.result);
1099 if (ar.exception != null) {
1100 // This is probably "radio not available" or something
1101 // of that sort. If so, the whole connection is going
1102 // to come down soon anyway
1103 if (DBG) log("onDataStateChanged(ar): exception; likely radio not available, ignore");
1106 if (DBG) log("onDataStateChanged(ar): DataCallState size=" + dataCallStates.size());
1108 // Create a hash map to store the dataCallState of each DataConnectionAc
1109 HashMap<DataCallState, DataConnectionAc> dataCallStateToDcac;
1110 dataCallStateToDcac = new HashMap<DataCallState, DataConnectionAc>();
1111 for (DataCallState dataCallState : dataCallStates) {
1112 DataConnectionAc dcac = findDataConnectionAcByCid(dataCallState.cid);
1114 if (dcac != null) dataCallStateToDcac.put(dataCallState, dcac);
1117 // A list of apns to cleanup, those that aren't in the list we know we have to cleanup
1118 List<ApnContext> apnsToCleanup = findApnContextToClean(dataCallStateToDcac.values());
1120 // Find which connections have changed state and send a notification or cleanup
1121 for (DataCallState newState : dataCallStates) {
1122 DataConnectionAc dcac = dataCallStateToDcac.get(newState);
1125 loge("onDataStateChanged(ar): No associated DataConnection ignore");
1129 // The list of apn's associated with this DataConnection
1130 Collection<ApnContext> apns = dcac.getApnListSync();
1132 // Find which ApnContexts of this DC are in the "Connected/Connecting" state.
1133 ArrayList<ApnContext> connectedApns = new ArrayList<ApnContext>();
1134 for (ApnContext apnContext : apns) {
1135 if (apnContext.getState() == State.CONNECTED ||
1136 apnContext.getState() == State.CONNECTING ||
1137 apnContext.getState() == State.INITING) {
1138 connectedApns.add(apnContext);
1141 if (connectedApns.size() == 0) {
1142 if (DBG) log("onDataStateChanged(ar): no connected apns");
1144 // Determine if the connection/apnContext should be cleaned up
1145 // or just a notification should be sent out.
1146 if (DBG) log("onDataStateChanged(ar): Found ConnId=" + newState.cid
1147 + " newState=" + newState.toString());
1148 if (newState.active == 0) {
1150 log("onDataStateChanged(ar): inactive, cleanup apns=" + connectedApns);
1152 apnsToCleanup.addAll(connectedApns);
1154 // Its active so update the DataConnections link properties
1155 UpdateLinkPropertyResult result =
1156 dcac.updateLinkPropertiesDataCallStateSync(newState);
1157 if (result.oldLp.equals(result.newLp)) {
1158 if (DBG) log("onDataStateChanged(ar): no change");
1160 if (result.oldLp.isIdenticalInterfaceName(result.newLp)) {
1161 if (! result.oldLp.isIdenticalDnses(result.newLp) ||
1162 ! result.oldLp.isIdenticalRoutes(result.newLp) ||
1163 ! result.oldLp.isIdenticalHttpProxy(result.newLp) ||
1164 ! result.oldLp.isIdenticalAddresses(result.newLp)) {
1165 // If the same address type was removed and added we need to cleanup
1166 CompareResult<LinkAddress> car =
1167 result.oldLp.compareAddresses(result.newLp);
1168 boolean needToClean = false;
1169 for (LinkAddress added : car.added) {
1170 for (LinkAddress removed : car.removed) {
1171 if (NetworkUtils.addressTypeMatches(removed.getAddress(),
1172 added.getAddress())) {
1180 log("onDataStateChanged(ar): addr change, cleanup apns=" +
1183 apnsToCleanup.addAll(connectedApns);
1185 if (DBG) log("onDataStateChanged(ar): simple change");
1186 for (ApnContext apnContext : connectedApns) {
1187 mPhone.notifyDataConnection(
1188 Phone.REASON_LINK_PROPERTIES_CHANGED,
1189 apnContext.getApnType());
1194 log("onDataStateChanged(ar): no changes");
1199 log("onDataStateChanged(ar): interface change, cleanup apns="
1202 apnsToCleanup.addAll(connectedApns);
1209 if (apnsToCleanup.size() != 0) {
1210 // Add an event log when the network drops PDP
1211 int cid = getCellLocationId();
1212 EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid,
1213 TelephonyManager.getDefault().getNetworkType());
1216 // Cleanup those dropped connections
1217 for (ApnContext apnContext : apnsToCleanup) {
1218 cleanUpConnection(true, apnContext);
1221 if (DBG) log("onDataStateChanged(ar): X");
1224 private void notifyDefaultData(ApnContext apnContext) {
1226 log("notifyDefaultData: type=" + apnContext.getApnType()
1227 + ", reason:" + apnContext.getReason());
1229 apnContext.setState(State.CONNECTED);
1230 // setState(State.CONNECTED);
1231 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1233 // reset reconnect timer
1234 apnContext.getDataConnection().resetRetryCount();
1237 // TODO: For multiple Active APNs not exactly sure how to do this.
1238 protected void gotoIdleAndNotifyDataConnection(String reason) {
1239 if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
1240 notifyDataConnection(reason);
1244 private void resetPollStats() {
1247 mSentSinceLastRecv = 0;
1248 mNetStatPollPeriod = POLL_NETSTAT_MILLIS;
1249 mNoRecvPollCount = 0;
1252 private void doRecovery() {
1253 if (getOverallState() == State.CONNECTED) {
1254 int maxPdpReset = Settings.Secure.getInt(mResolver,
1255 Settings.Secure.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT,
1256 DEFAULT_MAX_PDP_RESET_FAIL);
1257 if (mPdpResetCount < maxPdpReset) {
1259 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, mSentSinceLastRecv);
1260 if (DBG) log("doRecovery() cleanup all connections mPdpResetCount < max");
1261 cleanUpAllConnections(true, Phone.REASON_PDP_RESET);
1264 switch (mRecoveryAction) {
1266 EventLog.writeEvent(EventLogTags.PDP_REREGISTER_NETWORK, mSentSinceLastRecv);
1267 if (DBG) log("doRecovery() re-register getting preferred network type");
1268 mPhone.getServiceStateTracker().reRegisterNetwork(null);
1269 mRecoveryAction = RecoveryAction.RADIO_RESTART;
1272 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, mSentSinceLastRecv);
1273 if (DBG) log("restarting radio");
1274 mRecoveryAction = RecoveryAction.RADIO_RESET;
1278 // This is in case radio restart has not recovered the data.
1279 // It will set an additional "gsm.radioreset" property to tell
1280 // RIL or system to take further action.
1281 // The implementation of hard reset recovery action is up to OEM product.
1282 // Once gsm.radioreset property is consumed, it is expected to set back
1284 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, -1);
1285 if (DBG) log("restarting radio with reset indication");
1286 SystemProperties.set("gsm.radioreset", "true");
1287 // give 1 sec so property change can be notified.
1290 } catch (InterruptedException e) {}
1294 throw new RuntimeException("doRecovery: Invalid mRecoveryAction " +
1299 if (DBG) log("doRecovery(): ignore, we're not connected");
1304 protected void startNetStatPoll() {
1305 if (getOverallState() == State.CONNECTED && mNetStatPollEnabled == false) {
1306 if (DBG) log("startNetStatPoll");
1308 mNetStatPollEnabled = true;
1314 protected void stopNetStatPoll() {
1315 mNetStatPollEnabled = false;
1316 removeCallbacks(mPollNetStat);
1317 if (DBG) log("stopNetStatPoll");
1321 protected void restartRadio() {
1322 if (DBG) log("restartRadio: ************TURN OFF RADIO**************");
1323 cleanUpAllConnections(true, Phone.REASON_RADIO_TURNED_OFF);
1324 mPhone.getServiceStateTracker().powerOffRadioSafely(this);
1325 /* Note: no need to call setRadioPower(true). Assuming the desired
1326 * radio power state is still ON (as tracked by ServiceStateTracker),
1327 * ServiceStateTracker will call setRadioPower when it receives the
1328 * RADIO_STATE_CHANGED notification for the power off. And if the
1329 * desired power state has changed in the interim, we don't want to
1330 * override it with an unconditional power on.
1333 int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0"));
1334 SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1));
1337 private Runnable mPollNetStat = new Runnable()
1341 long sent, received;
1342 long preTxPkts = -1, preRxPkts = -1;
1344 Activity newActivity;
1346 preTxPkts = mTxPkts;
1347 preRxPkts = mRxPkts;
1349 long txSum = 0, rxSum = 0;
1350 for (ApnContext apnContext : mApnContexts.values()) {
1351 if (apnContext.getState() == State.CONNECTED) {
1352 DataConnectionAc dcac = apnContext.getDataConnectionAc();
1353 if (dcac == null) continue;
1355 LinkProperties linkProp = dcac.getLinkPropertiesSync();
1356 if (linkProp == null) continue;
1358 String iface = linkProp.getInterfaceName();
1360 if (iface != null) {
1361 long stats = TrafficStats.getTxPackets(iface);
1362 if (stats > 0) txSum += stats;
1363 stats = TrafficStats.getRxPackets(iface);
1364 if (stats > 0) rxSum += stats;
1372 // log("tx " + mTxPkts + " rx " + mRxPkts);
1374 if (mNetStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) {
1375 sent = mTxPkts - preTxPkts;
1376 received = mRxPkts - preRxPkts;
1378 if ( sent > 0 && received > 0 ) {
1379 mSentSinceLastRecv = 0;
1380 newActivity = Activity.DATAINANDOUT;
1382 mRecoveryAction = RecoveryAction.REREGISTER;
1383 } else if (sent > 0 && received == 0) {
1384 if (mPhone.getState() == Phone.State.IDLE) {
1385 mSentSinceLastRecv += sent;
1387 mSentSinceLastRecv = 0;
1389 newActivity = Activity.DATAOUT;
1390 } else if (sent == 0 && received > 0) {
1391 mSentSinceLastRecv = 0;
1392 newActivity = Activity.DATAIN;
1394 mRecoveryAction = RecoveryAction.REREGISTER;
1395 } else if (sent == 0 && received == 0) {
1396 newActivity = Activity.NONE;
1398 mSentSinceLastRecv = 0;
1399 newActivity = Activity.NONE;
1402 if (mActivity != newActivity && mIsScreenOn) {
1403 mActivity = newActivity;
1404 mPhone.notifyDataActivity();
1408 int watchdogTrigger = Settings.Secure.getInt(mResolver,
1409 Settings.Secure.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
1410 NUMBER_SENT_PACKETS_OF_HANG);
1412 if (mSentSinceLastRecv >= watchdogTrigger) {
1413 // we already have NUMBER_SENT_PACKETS sent without ack
1414 if (mNoRecvPollCount == 0) {
1415 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET_COUNTDOWN_TRIGGERED,
1416 mSentSinceLastRecv);
1419 int noRecvPollLimit = Settings.Secure.getInt(mResolver,
1420 Settings.Secure.PDP_WATCHDOG_ERROR_POLL_COUNT, NO_RECV_POLL_LIMIT);
1422 if (mNoRecvPollCount < noRecvPollLimit) {
1423 // It's possible the PDP context went down and we weren't notified.
1424 // Start polling the context list in an attempt to recover.
1425 if (DBG) log("Polling: no DATAIN in a while; polling PDP");
1426 mPhone.mCM.getDataCallList(obtainMessage(EVENT_DATA_STATE_CHANGED));
1430 // Slow down the poll interval to let things happen
1431 mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
1432 Settings.Secure.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS,
1433 POLL_NETSTAT_SLOW_MILLIS);
1435 if (DBG) log("Polling: Sent " + String.valueOf(mSentSinceLastRecv) +
1436 " pkts since last received start recovery process");
1437 mNoRecvPollCount = 0;
1438 sendMessage(obtainMessage(EVENT_START_RECOVERY));
1441 mNoRecvPollCount = 0;
1443 mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
1444 Settings.Secure.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS);
1446 mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
1447 Settings.Secure.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS,
1448 POLL_NETSTAT_SCREEN_OFF_MILLIS);
1452 if (mNetStatPollEnabled) {
1453 mDataConnectionTracker.postDelayed(this, mNetStatPollPeriod);
1459 * Returns true if the last fail cause is something that
1460 * seems like it deserves an error notification.
1461 * Transient errors are ignored
1463 private boolean shouldPostNotification(GsmDataConnection.FailCause cause) {
1464 return (cause != GsmDataConnection.FailCause.UNKNOWN);
1468 * Return true if data connection need to be setup after disconnected due to
1471 * @param reason the reason why data is disconnected
1472 * @return true if try setup data connection is need for this reason
1474 private boolean retryAfterDisconnected(String reason) {
1475 boolean retry = true;
1477 if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) {
1483 private void reconnectAfterFail(FailCause lastFailCauseCode,
1484 ApnContext apnContext, int retryOverride) {
1485 if (apnContext == null) {
1486 loge("reconnectAfterFail: apnContext == null, impossible");
1489 if ((apnContext.getState() == State.FAILED) &&
1490 (apnContext.getDataConnection() != null)) {
1491 if (!apnContext.getDataConnection().isRetryNeeded()) {
1492 if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)) {
1493 mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
1496 if (mReregisterOnReconnectFailure) {
1497 // We've re-registerd once now just retry forever.
1498 apnContext.getDataConnection().retryForeverUsingLastTimeout();
1500 // Try to Re-register to the network.
1501 if (DBG) log("reconnectAfterFail: activate failed, Reregistering to network");
1502 mReregisterOnReconnectFailure = true;
1503 mPhone.getServiceStateTracker().reRegisterNetwork(null);
1504 apnContext.getDataConnection().resetRetryCount();
1509 // If retry needs to be backed off for specific case (determined by RIL/Modem)
1510 // use the specified timer instead of pre-configured retry pattern.
1511 int nextReconnectDelay = retryOverride;
1512 if (nextReconnectDelay < 0) {
1513 nextReconnectDelay = apnContext.getDataConnection().getRetryTimer();
1514 apnContext.getDataConnection().increaseRetryCount();
1516 startAlarmForReconnect(nextReconnectDelay, apnContext);
1518 if (!shouldPostNotification(lastFailCauseCode)) {
1520 log("reconnectAfterFail: NOT Posting GPRS Unavailable notification "
1521 + "-- likely transient error");
1524 notifyNoData(lastFailCauseCode, apnContext);
1529 private void startAlarmForReconnect(int delay, ApnContext apnContext) {
1532 log("Schedule alarm for reconnect: activate failed. Scheduling next attempt for "
1533 + (delay / 1000) + "s");
1536 DataConnectionAc dcac = apnContext.getDataConnectionAc();
1538 if ((dcac == null) || (dcac.dataConnection == null)) {
1539 // should not happen, but just in case.
1540 loge("null dcac or dc.");
1545 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
1547 Intent intent = new Intent(INTENT_RECONNECT_ALARM + '.' +
1548 dcac.dataConnection.getDataConnectionId());
1549 intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason());
1550 intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE,
1551 dcac.dataConnection.getDataConnectionId());
1553 PendingIntent alarmIntent = PendingIntent.getBroadcast (mPhone.getContext(), 0,
1555 dcac.setReconnectIntentSync(alarmIntent);
1556 am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1557 SystemClock.elapsedRealtime() + delay, alarmIntent);
1561 private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode,
1562 ApnContext apnContext) {
1563 if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
1564 apnContext.setState(State.FAILED);
1565 if (lastFailCauseCode.isPermanentFail()
1566 && (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT))) {
1567 mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
1571 private void onRecordsLoaded() {
1572 if (DBG) log("onRecordsLoaded: createAllApnList");
1574 if (mRadioAvailable) {
1575 if (DBG) log("onRecordsLoaded: notifying data availability");
1576 notifyDataAvailability(Phone.REASON_SIM_LOADED);
1578 setupDataOnReadyApns(Phone.REASON_SIM_LOADED);
1582 protected void onSetDependencyMet(String apnType, boolean met) {
1583 // don't allow users to tweak hipri to work around default dependency not met
1584 if (Phone.APN_TYPE_HIPRI.equals(apnType)) return;
1586 ApnContext apnContext = mApnContexts.get(apnType);
1587 if (apnContext == null) {
1588 loge("onSetDependencyMet: ApnContext not found in onSetDependencyMet(" +
1589 apnType + ", " + met + ")");
1592 applyNewState(apnContext, apnContext.isEnabled(), met);
1593 if (Phone.APN_TYPE_DEFAULT.equals(apnType)) {
1594 // tie actions on default to similar actions on HIPRI regarding dependencyMet
1595 apnContext = mApnContexts.get(Phone.APN_TYPE_HIPRI);
1596 if (apnContext != null) applyNewState(apnContext, apnContext.isEnabled(), met);
1600 private void applyNewState(ApnContext apnContext, boolean enabled, boolean met) {
1601 boolean cleanup = false;
1602 boolean trySetup = false;
1604 log("applyNewState(" + apnContext.getApnType() + ", " + enabled +
1605 "(" + apnContext.isEnabled() + "), " + met + "(" +
1606 apnContext.getDependencyMet() +"))");
1608 if (apnContext.isReady()) {
1609 if (enabled && met) return;
1611 apnContext.setReason(Phone.REASON_DATA_DISABLED);
1613 apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET);
1617 if (enabled && met) {
1618 if (apnContext.isEnabled()) {
1619 apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_MET);
1621 apnContext.setReason(Phone.REASON_DATA_ENABLED);
1623 if (apnContext.getState() == State.FAILED) {
1624 apnContext.setState(State.IDLE);
1629 apnContext.setEnabled(enabled);
1630 apnContext.setDependencyMet(met);
1631 if (cleanup) cleanUpConnection(true, apnContext);
1632 if (trySetup) trySetupData(apnContext);
1635 private DataConnection checkForConnectionForApnContext(ApnContext apnContext) {
1636 // Loop through all apnContexts looking for one with a conn that satisfies this apnType
1637 String apnType = apnContext.getApnType();
1638 for (ApnContext c : mApnContexts.values()) {
1639 DataConnection conn = c.getDataConnection();
1641 ApnSetting apnSetting = c.getApnSetting();
1642 if (apnSetting != null && apnSetting.canHandleType(apnType)) {
1644 log("checkForConnectionForApnContext: apnContext=" + apnContext +
1645 " found conn=" + conn);
1651 if (DBG) log("checkForConnectionForApnContext: apnContext=" + apnContext + " NO conn");
1656 protected void onEnableApn(int apnId, int enabled) {
1657 ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
1658 if (apnContext == null) {
1659 loge("onEnableApn(" + apnId + ", " + enabled + "): NO ApnContext");
1662 // TODO change our retry manager to use the appropriate numbers for the new APN
1663 if (DBG) log("onEnableApn: apnContext=" + apnContext + " call applyNewState");
1664 applyNewState(apnContext, enabled == ENABLED, apnContext.getDependencyMet());
1668 // TODO: We shouldnt need this.
1669 protected boolean onTrySetupData(String reason) {
1670 if (DBG) log("onTrySetupData: reason=" + reason);
1671 setupDataOnReadyApns(reason);
1675 protected boolean onTrySetupData(ApnContext apnContext) {
1676 if (DBG) log("onTrySetupData: apnContext=" + apnContext);
1677 return trySetupData(apnContext);
1681 protected void onRoamingOff() {
1682 if (DBG) log("onRoamingOff");
1683 // Notify data availability so APN can be enabled.
1684 notifyDataAvailability(Phone.REASON_ROAMING_OFF);
1686 setupDataOnReadyApns(Phone.REASON_ROAMING_OFF);
1690 protected void onRoamingOn() {
1691 // Notify data availability so APN can be enabled.
1692 notifyDataAvailability(Phone.REASON_ROAMING_ON);
1694 if (getDataOnRoamingEnabled()) {
1695 if (DBG) log("onRoamingOn: setup data on roaming");
1696 setupDataOnReadyApns(Phone.REASON_ROAMING_ON);
1698 if (DBG) log("onRoamingOn: Tear down data connection on roaming.");
1699 cleanUpAllConnections(true, Phone.REASON_ROAMING_ON);
1704 protected void onRadioAvailable() {
1705 if (DBG) log("onRadioAvailable");
1706 mRadioAvailable = true;
1707 if (mPhone.getSimulatedRadioControl() != null) {
1708 // Assume data is connected on the simulator
1709 // FIXME this can be improved
1710 // setState(State.CONNECTED);
1711 notifyDataConnection(null);
1713 log("onRadioAvailable: We're on the simulator; assuming data is connected");
1716 if (mPhone.mIccRecords.getRecordsLoaded()) {
1717 notifyDataAvailability(null);
1720 if (getOverallState() != State.IDLE) {
1721 cleanUpConnection(true, null);
1726 protected void onRadioOffOrNotAvailable() {
1727 // Make sure our reconnect delay starts at the initial value
1728 // next time the radio comes on
1730 for (DataConnection dc : mDataConnections.values()) {
1731 dc.resetRetryCount();
1733 mReregisterOnReconnectFailure = false;
1734 mRadioAvailable = false;
1736 if (mPhone.getSimulatedRadioControl() != null) {
1737 // Assume data is connected on the simulator
1738 // FIXME this can be improved
1739 log("We're on the simulator; assuming radio off is meaningless");
1741 if (DBG) log("onRadioOffOrNotAvailable: is off and clean up all connections");
1742 cleanUpAllConnections(false, Phone.REASON_RADIO_TURNED_OFF);
1744 notifyDataAvailability(null);
1748 protected void onDataSetupComplete(AsyncResult ar) {
1750 ApnContext apnContext = null;
1752 if(ar.userObj instanceof ApnContext){
1753 apnContext = (ApnContext)ar.userObj;
1755 throw new RuntimeException("onDataSetupComplete: No apnContext");
1758 if (isDataSetupCompleteOk(ar)) {
1759 DataConnectionAc dcac = apnContext.getDataConnectionAc();
1761 throw new RuntimeException("onDataSetupCompete: No dcac");
1763 DataConnection dc = apnContext.getDataConnection();
1766 log(String.format("onDataSetupComplete: success apn=%s",
1767 apnContext.getWaitingApns().get(0).apn));
1769 ApnSetting apn = apnContext.getApnSetting();
1770 if (apn.proxy != null && apn.proxy.length() != 0) {
1772 ProxyProperties proxy = new ProxyProperties(apn.proxy,
1773 Integer.parseInt(apn.port), null);
1774 dcac.setLinkPropertiesHttpProxySync(proxy);
1775 } catch (NumberFormatException e) {
1776 loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" +
1777 apn.port + "): " + e);
1781 // everything is setup
1782 if(TextUtils.equals(apnContext.getApnType(),Phone.APN_TYPE_DEFAULT)) {
1783 SystemProperties.set("gsm.defaultpdpcontext.active", "true");
1784 if (canSetPreferApn && mPreferredApn == null) {
1785 if (DBG) log("onDataSetupComplete: PREFERED APN is null");
1786 mPreferredApn = apnContext.getApnSetting();
1787 if (mPreferredApn != null) {
1788 setPreferredApn(mPreferredApn.id);
1792 SystemProperties.set("gsm.defaultpdpcontext.active", "false");
1794 notifyDefaultData(apnContext);
1797 DataConnection.FailCause cause;
1799 cause = (DataConnection.FailCause) (ar.result);
1802 apnString = apnContext.getWaitingApns().get(0).apn;
1803 } catch (Exception e) {
1804 apnString = "<unknown>";
1806 log(String.format("onDataSetupComplete: error apn=%s cause=%s", apnString, cause));
1808 if (cause.isEventLoggable()) {
1809 // Log this failure to the Event Logs.
1810 int cid = getCellLocationId();
1811 EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL,
1812 cause.ordinal(), cid, TelephonyManager.getDefault().getNetworkType());
1815 // Count permanent failures and remove the APN we just tried
1816 if (cause.isPermanentFail()) apnContext.decWaitingApnsPermFailCount();
1818 apnContext.removeNextWaitingApn();
1820 log(String.format("onDataSetupComplete: WaitingApns.size=%d" +
1821 " WaitingApnsPermFailureCountDown=%d",
1822 apnContext.getWaitingApns().size(),
1823 apnContext.getWaitingApnsPermFailCount()));
1826 // See if there are more APN's to try
1827 if (apnContext.getWaitingApns().isEmpty()) {
1828 if (apnContext.getWaitingApnsPermFailCount() == 0) {
1830 log("onDataSetupComplete: All APN's had permanent failures, stop retrying");
1832 apnContext.setState(State.FAILED);
1833 mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
1835 apnContext.setDataConnection(null);
1836 apnContext.setDataConnectionAc(null);
1838 log("onDataSetupComplete: permanent error apn=%s" + apnString );
1841 if (DBG) log("onDataSetupComplete: Not all permanent failures, retry");
1842 // check to see if retry should be overridden for this failure.
1843 int retryOverride = -1;
1844 if (ar.exception instanceof DataConnection.CallSetupException) {
1846 ((DataConnection.CallSetupException)ar.exception).getRetryOverride();
1848 if (retryOverride == RILConstants.MAX_INT) {
1849 if (DBG) log("No retry is suggested.");
1851 startDelayedRetry(cause, apnContext, retryOverride);
1855 if (DBG) log("onDataSetupComplete: Try next APN");
1856 apnContext.setState(State.SCANNING);
1857 // Wait a bit before trying the next APN, so that
1858 // we're not tying up the RIL command channel
1859 startAlarmForReconnect(APN_DELAY_MILLIS, apnContext);
1865 * Called when EVENT_DISCONNECT_DONE is received.
1868 protected void onDisconnectDone(int connId, AsyncResult ar) {
1869 ApnContext apnContext = null;
1871 if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE connId=" + connId);
1872 if (ar.userObj instanceof ApnContext) {
1873 apnContext = (ApnContext) ar.userObj;
1875 loge("Invalid ar in onDisconnectDone");
1879 apnContext.setState(State.IDLE);
1881 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1883 // if all data connection are gone, check whether Airplane mode request was
1885 if (!isConnected()) {
1886 if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
1887 // Radio will be turned off. No need to retry data setup
1888 apnContext.setApnSetting(null);
1889 apnContext.setDataConnection(null);
1890 apnContext.setDataConnectionAc(null);
1895 // If APN is still enabled, try to bring it back up automatically
1896 if (apnContext.isReady() && retryAfterDisconnected(apnContext.getReason())) {
1897 SystemProperties.set("gsm.defaultpdpcontext.active", "false"); // TODO - what the heck? This shoudld go
1898 // Wait a bit before trying the next APN, so that
1899 // we're not tying up the RIL command channel.
1900 // This also helps in any external dependency to turn off the context.
1901 startAlarmForReconnect(APN_DELAY_MILLIS, apnContext);
1903 apnContext.setApnSetting(null);
1904 apnContext.setDataConnection(null);
1905 apnContext.setDataConnectionAc(null);
1909 protected void onPollPdp() {
1910 if (getOverallState() == State.CONNECTED) {
1911 // only poll when connected
1912 mPhone.mCM.getDataCallList(this.obtainMessage(EVENT_DATA_STATE_CHANGED));
1913 sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS);
1918 protected void onVoiceCallStarted() {
1919 if (DBG) log("onVoiceCallStarted");
1920 if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
1921 if (DBG) log("onVoiceCallStarted stop polling");
1923 notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
1928 protected void onVoiceCallEnded() {
1929 if (DBG) log("onVoiceCallEnded");
1930 if (isConnected()) {
1931 if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
1933 notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
1935 // clean slate after call end.
1939 // reset reconnect timer
1940 setupDataOnReadyApns(Phone.REASON_VOICE_CALL_ENDED);
1945 protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
1946 if (DBG) log("onCleanUpConnection");
1947 ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
1948 if (apnContext != null) {
1949 apnContext.setReason(reason);
1950 cleanUpConnection(tearDown, apnContext);
1954 protected boolean isConnected() {
1955 for (ApnContext apnContext : mApnContexts.values()) {
1956 if (apnContext.getState() == State.CONNECTED) {
1964 protected void notifyDataConnection(String reason) {
1965 if (DBG) log("notifyDataConnection: reason=" + reason);
1966 for (ApnContext apnContext : mApnContexts.values()) {
1967 if (apnContext.isReady()) {
1968 if (DBG) log("notifyDataConnection: type:"+apnContext.getApnType());
1969 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
1970 apnContext.getApnType());
1973 notifyDataAvailability(reason);
1977 * Based on the sim operator numeric, create a list for all possible
1978 * Data Connections and setup the preferredApn.
1980 private void createAllApnList() {
1981 mAllApns = new ArrayList<ApnSetting>();
1982 String operator = mPhone.mIccRecords.getOperatorNumeric();
1983 if (operator != null) {
1984 String selection = "numeric = '" + operator + "'";
1985 if (DBG) log("createAllApnList: selection=" + selection);
1987 Cursor cursor = mPhone.getContext().getContentResolver().query(
1988 Telephony.Carriers.CONTENT_URI, null, selection, null, null);
1990 if (cursor != null) {
1991 if (cursor.getCount() > 0) {
1992 mAllApns = createApnList(cursor);
1998 if (mAllApns.isEmpty()) {
1999 if (DBG) log("createAllApnList: No APN found for carrier: " + operator);
2000 mPreferredApn = null;
2001 // TODO: What is the right behaviour?
2002 //notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN);
2004 mPreferredApn = getPreferredApn();
2005 if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
2006 mPreferredApn = null;
2007 setPreferredApn(-1);
2009 if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn);
2011 if (DBG) log("createAllApnList: X mAllApns=" + mAllApns);
2014 /** Return the id for a new data connection */
2015 private GsmDataConnection createDataConnection() {
2016 if (DBG) log("createDataConnection E");
2018 RetryManager rm = new RetryManager();
2019 int id = mUniqueIdGenerator.getAndIncrement();
2020 GsmDataConnection conn = GsmDataConnection.makeDataConnection(mPhone, id, rm);
2021 mDataConnections.put(id, conn);
2022 DataConnectionAc dcac = new DataConnectionAc(conn, LOG_TAG);
2023 int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler());
2024 if (status == AsyncChannel.STATUS_SUCCESSFUL) {
2025 mDataConnectionAsyncChannels.put(dcac.dataConnection.getDataConnectionId(), dcac);
2027 loge("createDataConnection: Could not connect to dcac.mDc=" + dcac.dataConnection +
2028 " status=" + status);
2031 // install reconnect intent filter for this data connection.
2032 IntentFilter filter = new IntentFilter();
2033 filter.addAction(INTENT_RECONNECT_ALARM + '.' + id);
2034 mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
2036 if (DBG) log("createDataConnection() X id=" + id);
2040 private void configureRetry(DataConnection dc, boolean forDefault) {
2041 if (dc == null) return;
2043 if (!dc.configureRetry(getReryConfig(forDefault))) {
2045 if (!dc.configureRetry(DEFAULT_DATA_RETRY_CONFIG)) {
2046 // Should never happen, log an error and default to a simple linear sequence.
2047 loge("configureRetry: Could not configure using " +
2048 "DEFAULT_DATA_RETRY_CONFIG=" + DEFAULT_DATA_RETRY_CONFIG);
2049 dc.configureRetry(20, 2000, 1000);
2052 if (!dc.configureRetry(SECONDARY_DATA_RETRY_CONFIG)) {
2053 // Should never happen, log an error and default to a simple sequence.
2054 loge("configureRetry: Could note configure using " +
2055 "SECONDARY_DATA_RETRY_CONFIG=" + SECONDARY_DATA_RETRY_CONFIG);
2056 dc.configureRetry("max_retries=3, 333, 333, 333");
2062 private void destroyDataConnections() {
2063 if(mDataConnections != null) {
2064 if (DBG) log("destroyDataConnections: clear mDataConnectionList");
2065 mDataConnections.clear();
2067 if (DBG) log("destroyDataConnections: mDataConnecitonList is empty, ignore");
2072 * Build a list of APNs to be used to create PDP's.
2074 * @param requestedApnType
2075 * @return waitingApns list to be used to create PDP
2076 * error when waitingApns.isEmpty()
2078 private ArrayList<ApnSetting> buildWaitingApns(String requestedApnType) {
2079 ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
2081 if (requestedApnType.equals(Phone.APN_TYPE_DUN)) {
2082 ApnSetting dun = fetchDunApn();
2085 if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
2090 String operator = mPhone.mIccRecords.getOperatorNumeric();
2091 if (requestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
2092 if (canSetPreferApn && mPreferredApn != null) {
2094 log("buildWaitingApns: Preferred APN:" + operator + ":"
2095 + mPreferredApn.numeric + ":" + mPreferredApn);
2097 if (mPreferredApn.numeric.equals(operator)) {
2098 apnList.add(mPreferredApn);
2099 if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
2102 if (DBG) log("buildWaitingApns: no preferred APN");
2103 setPreferredApn(-1);
2104 mPreferredApn = null;
2108 if (mAllApns != null) {
2109 for (ApnSetting apn : mAllApns) {
2110 if (apn.canHandleType(requestedApnType)) {
2115 loge("mAllApns is empty!");
2117 if (DBG) log("buildWaitingApns: X apnList=" + apnList);
2121 private String apnListToString (ArrayList<ApnSetting> apns) {
2122 StringBuilder result = new StringBuilder();
2123 for (int i = 0, size = apns.size(); i < size; i++) {
2125 .append(apns.get(i).toString())
2128 return result.toString();
2131 private void startDelayedRetry(GsmDataConnection.FailCause cause,
2132 ApnContext apnContext, int retryOverride) {
2133 notifyNoData(cause, apnContext);
2134 reconnectAfterFail(cause, apnContext, retryOverride);
2137 private void setPreferredApn(int pos) {
2138 if (!canSetPreferApn) {
2142 ContentResolver resolver = mPhone.getContext().getContentResolver();
2143 resolver.delete(PREFERAPN_URI, null, null);
2146 ContentValues values = new ContentValues();
2147 values.put(APN_ID, pos);
2148 resolver.insert(PREFERAPN_URI, values);
2152 private ApnSetting getPreferredApn() {
2153 if (mAllApns.isEmpty()) {
2157 Cursor cursor = mPhone.getContext().getContentResolver().query(
2158 PREFERAPN_URI, new String[] { "_id", "name", "apn" },
2159 null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
2161 if (cursor != null) {
2162 canSetPreferApn = true;
2164 canSetPreferApn = false;
2167 if (canSetPreferApn && cursor.getCount() > 0) {
2169 cursor.moveToFirst();
2170 pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
2171 for(ApnSetting p:mAllApns) {
2172 if (p.id == pos && p.canHandleType(mRequestedApnType)) {
2179 if (cursor != null) {
2187 public void handleMessage (Message msg) {
2188 if (DBG) log("handleMessage msg=" + msg);
2190 if (!mPhone.mIsTheCurrentActivePhone || mIsDisposed) {
2191 loge("handleMessage: Ignore GSM msgs since GSM phone is inactive");
2196 case EVENT_RECORDS_LOADED:
2200 case EVENT_DATA_CONNECTION_DETACHED:
2201 onDataConnectionDetached();
2204 case EVENT_DATA_CONNECTION_ATTACHED:
2205 onDataConnectionAttached();
2208 case EVENT_DATA_STATE_CHANGED:
2209 onDataStateChanged((AsyncResult) msg.obj);
2212 case EVENT_POLL_PDP:
2216 case EVENT_START_NETSTAT_POLL:
2220 case EVENT_START_RECOVERY:
2224 case EVENT_APN_CHANGED:
2228 case EVENT_PS_RESTRICT_ENABLED:
2230 * We don't need to explicitly to tear down the PDP context
2231 * when PS restricted is enabled. The base band will deactive
2232 * PDP context and notify us with PDP_CONTEXT_CHANGED.
2233 * But we should stop the network polling and prevent reset PDP.
2235 if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
2237 mIsPsRestricted = true;
2240 case EVENT_PS_RESTRICT_DISABLED:
2242 * When PS restrict is removed, we need setup PDP connection if
2243 * PDP connection is down.
2245 if (DBG) log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
2246 mIsPsRestricted = false;
2247 if (isConnected()) {
2250 // TODO: Should all PDN states be checked to fail?
2251 if (mState == State.FAILED) {
2252 cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED);
2253 resetAllRetryCounts();
2254 mReregisterOnReconnectFailure = false;
2256 trySetupData(Phone.REASON_PS_RESTRICT_ENABLED, Phone.APN_TYPE_DEFAULT);
2259 case EVENT_TRY_SETUP_DATA:
2260 if (msg.obj instanceof ApnContext) {
2261 onTrySetupData((ApnContext)msg.obj);
2262 } else if (msg.obj instanceof String) {
2263 onTrySetupData((String)msg.obj);
2265 loge("EVENT_TRY_SETUP request w/o apnContext or String");
2269 case EVENT_CLEAN_UP_CONNECTION:
2270 boolean tearDown = (msg.arg1 == 0) ? false : true;
2271 if (DBG) log("EVENT_CLEAN_UP_CONNECTION tearDown=" + tearDown);
2272 if (msg.obj instanceof ApnContext) {
2273 cleanUpConnection(tearDown, (ApnContext)msg.obj);
2275 loge("EVENT_CLEAN_UP_CONNECTION request w/o apn context");
2279 // handle the message in the super class DataConnectionTracker
2280 super.handleMessage(msg);
2285 protected int getApnProfileID(String apnType) {
2286 if (TextUtils.equals(apnType, Phone.APN_TYPE_IMS)) {
2287 return RILConstants.DATA_PROFILE_IMS;
2288 } else if (TextUtils.equals(apnType, Phone.APN_TYPE_FOTA)) {
2289 return RILConstants.DATA_PROFILE_FOTA;
2290 } else if (TextUtils.equals(apnType, Phone.APN_TYPE_CBS)) {
2291 return RILConstants.DATA_PROFILE_CBS;
2293 return RILConstants.DATA_PROFILE_DEFAULT;
2297 private int getCellLocationId() {
2299 CellLocation loc = mPhone.getCellLocation();
2302 if (loc instanceof GsmCellLocation) {
2303 cid = ((GsmCellLocation)loc).getCid();
2304 } else if (loc instanceof CdmaCellLocation) {
2305 cid = ((CdmaCellLocation)loc).getBaseStationId();
2312 public boolean isAnyActiveDataConnections() {
2313 return isConnected();
2317 protected void log(String s) {
2318 Log.d(LOG_TAG, "[GsmDCT] "+ s);
2322 protected void loge(String s) {
2323 Log.e(LOG_TAG, "[GsmDCT] " + s);