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.content.res.Resources;
27 import android.database.ContentObserver;
28 import android.database.Cursor;
29 import android.net.ConnectivityManager;
30 import android.net.LinkAddress;
31 import android.net.LinkCapabilities;
32 import android.net.LinkProperties;
33 import android.net.LinkProperties.CompareResult;
34 import android.net.NetworkConfig;
35 import android.net.NetworkUtils;
36 import android.net.ProxyProperties;
37 import android.net.TrafficStats;
38 import android.net.Uri;
39 import android.os.AsyncResult;
40 import android.os.Message;
41 import android.os.SystemClock;
42 import android.os.SystemProperties;
43 import android.provider.Settings;
44 import android.provider.Telephony;
45 import android.telephony.CellLocation;
46 import android.telephony.ServiceState;
47 import android.telephony.TelephonyManager;
48 import android.telephony.cdma.CdmaCellLocation;
49 import android.telephony.gsm.GsmCellLocation;
50 import android.text.TextUtils;
51 import android.util.EventLog;
52 import android.telephony.Rlog;
54 import com.android.internal.telephony.ApnContext;
55 import com.android.internal.telephony.ApnSetting;
56 import com.android.internal.telephony.DataCallState;
57 import com.android.internal.telephony.DataConnection;
58 import com.android.internal.telephony.DataConnection.FailCause;
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.DctConstants;
63 import com.android.internal.telephony.EventLogTags;
64 import com.android.internal.telephony.IccCard;
65 import com.android.internal.telephony.Phone;
66 import com.android.internal.telephony.PhoneBase;
67 import com.android.internal.telephony.PhoneConstants;
68 import com.android.internal.telephony.RILConstants;
69 import com.android.internal.telephony.RetryManager;
70 import com.android.internal.telephony.uicc.IccRecords;
71 import com.android.internal.telephony.uicc.UiccCard;
72 import com.android.internal.telephony.uicc.UiccController;
73 import com.android.internal.util.AsyncChannel;
75 import java.io.FileDescriptor;
76 import java.io.PrintWriter;
77 import java.util.ArrayList;
78 import java.util.Collection;
79 import java.util.HashMap;
80 import java.util.List;
81 import java.util.concurrent.ConcurrentHashMap;
86 public final class GsmDataConnectionTracker extends DataConnectionTracker {
87 protected final String LOG_TAG = "GSM";
90 * Handles changes to the APN db.
92 private class ApnChangeObserver extends ContentObserver {
93 public ApnChangeObserver () {
94 super(mDataConnectionTracker);
98 public void onChange(boolean selfChange) {
99 sendMessage(obtainMessage(DctConstants.EVENT_APN_CHANGED));
103 //***** Instance Variables
105 private boolean mReregisterOnReconnectFailure = false;
110 private static final int POLL_PDP_MILLIS = 5 * 1000;
112 private static final String INTENT_RECONNECT_ALARM =
113 "com.android.internal.telephony.gprs-reconnect";
114 private static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "reconnect_alarm_extra_type";
115 private static final String INTENT_RECONNECT_ALARM_EXTRA_RETRY_COUNT =
116 "reconnect_alaram_extra_retry_count";
118 private static final String INTENT_DATA_STALL_ALARM =
119 "com.android.internal.telephony.gprs-data-stall";
121 static final Uri PREFERAPN_NO_UPDATE_URI =
122 Uri.parse("content://telephony/carriers/preferapn_no_update");
123 static final String APN_ID = "apn_id";
124 private boolean canSetPreferApn = false;
126 private static final boolean DATA_STALL_SUSPECTED = true;
127 private static final boolean DATA_STALL_NOT_SUSPECTED = false;
130 protected void onActionIntentReconnectAlarm(Intent intent) {
131 String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
132 int connectionId = intent.getIntExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, -1);
133 int retryCount = intent.getIntExtra(INTENT_RECONNECT_ALARM_EXTRA_RETRY_COUNT, 0);
135 DataConnectionAc dcac= mDataConnectionAsyncChannels.get(connectionId);
138 log("onActionIntentReconnectAlarm: mState=" + mState + " reason=" + reason +
139 " connectionId=" + connectionId + " retryCount=" + retryCount + " dcac=" + dcac
140 + " mDataConnectionAsyncChannels=" + mDataConnectionAsyncChannels);
144 for (ApnContext apnContext : dcac.getApnListSync()) {
145 apnContext.setReason(reason);
146 apnContext.setRetryCount(retryCount);
147 DctConstants.State apnContextState = apnContext.getState();
149 log("onActionIntentReconnectAlarm: apnContext state=" + apnContextState);
151 if ((apnContextState == DctConstants.State.FAILED)
152 || (apnContextState == DctConstants.State.IDLE)) {
154 log("onActionIntentReconnectAlarm: state is FAILED|IDLE, disassociate");
156 apnContext.setDataConnectionAc(null);
157 apnContext.setDataConnection(null);
158 apnContext.setState(DctConstants.State.IDLE);
161 log("onActionIntentReconnectAlarm: keep associated");
164 sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, apnContext));
166 // Alram had expired. Clear pending intent recorded on the DataConnection.
167 dcac.setReconnectIntentSync(null);
171 /** Watches for changes to the APN db. */
172 private ApnChangeObserver mApnObserver;
176 public GsmDataConnectionTracker(PhoneBase p) {
178 if (DBG) log("GsmDCT.constructor");
179 p.mCM.registerForAvailable (this, DctConstants.EVENT_RADIO_AVAILABLE, null);
180 p.mCM.registerForOffOrNotAvailable(this, DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE,
182 p.mCM.registerForDataNetworkStateChanged (this, DctConstants.EVENT_DATA_STATE_CHANGED,
184 p.getCallTracker().registerForVoiceCallEnded (this, DctConstants.EVENT_VOICE_CALL_ENDED,
186 p.getCallTracker().registerForVoiceCallStarted (this, DctConstants.EVENT_VOICE_CALL_STARTED,
188 p.getServiceStateTracker().registerForDataConnectionAttached(this,
189 DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null);
190 p.getServiceStateTracker().registerForDataConnectionDetached(this,
191 DctConstants.EVENT_DATA_CONNECTION_DETACHED, null);
192 p.getServiceStateTracker().registerForRoamingOn(this, DctConstants.EVENT_ROAMING_ON, null);
193 p.getServiceStateTracker().registerForRoamingOff(this, DctConstants.EVENT_ROAMING_OFF,
195 p.getServiceStateTracker().registerForPsRestrictedEnabled(this,
196 DctConstants.EVENT_PS_RESTRICT_ENABLED, null);
197 p.getServiceStateTracker().registerForPsRestrictedDisabled(this,
198 DctConstants.EVENT_PS_RESTRICT_DISABLED, null);
200 mDataConnectionTracker = this;
202 mApnObserver = new ApnChangeObserver();
203 p.getContext().getContentResolver().registerContentObserver(
204 Telephony.Carriers.CONTENT_URI, true, mApnObserver);
206 initApnContextsAndDataConnection();
207 broadcastMessenger();
211 public void dispose() {
212 if (DBG) log("GsmDCT.dispose");
213 cleanUpAllConnections(true, null);
217 //Unregister for all events
218 mPhone.mCM.unregisterForAvailable(this);
219 mPhone.mCM.unregisterForOffOrNotAvailable(this);
220 IccRecords r = mIccRecords.get();
221 if (r != null) { r.unregisterForRecordsLoaded(this);}
222 mPhone.mCM.unregisterForDataNetworkStateChanged(this);
223 mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
224 mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
225 mPhone.getServiceStateTracker().unregisterForDataConnectionAttached(this);
226 mPhone.getServiceStateTracker().unregisterForDataConnectionDetached(this);
227 mPhone.getServiceStateTracker().unregisterForRoamingOn(this);
228 mPhone.getServiceStateTracker().unregisterForRoamingOff(this);
229 mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this);
230 mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this);
232 mPhone.getContext().getContentResolver().unregisterContentObserver(this.mApnObserver);
233 mApnContexts.clear();
235 destroyDataConnections();
239 public boolean isApnTypeActive(String type) {
240 ApnContext apnContext = mApnContexts.get(type);
241 if (apnContext == null) return false;
243 return (apnContext.getDataConnection() != null);
247 protected boolean isDataPossible(String apnType) {
248 ApnContext apnContext = mApnContexts.get(apnType);
249 if (apnContext == null) {
252 boolean apnContextIsEnabled = apnContext.isEnabled();
253 DctConstants.State apnContextState = apnContext.getState();
254 boolean apnTypePossible = !(apnContextIsEnabled &&
255 (apnContextState == DctConstants.State.FAILED));
256 boolean dataAllowed = isDataAllowed();
257 boolean possible = dataAllowed && apnTypePossible;
260 log(String.format("isDataPossible(%s): possible=%b isDataAllowed=%b " +
261 "apnTypePossible=%b apnContextisEnabled=%b apnContextState()=%s",
262 apnType, possible, dataAllowed, apnTypePossible,
263 apnContextIsEnabled, apnContextState));
269 protected void finalize() {
270 if(DBG) log("finalize");
274 protected String getActionIntentReconnectAlarm() {
275 return INTENT_RECONNECT_ALARM;
279 protected String getActionIntentDataStallAlarm() {
280 return INTENT_DATA_STALL_ALARM;
283 private ApnContext addApnContext(String type) {
284 ApnContext apnContext = new ApnContext(type, LOG_TAG);
285 apnContext.setDependencyMet(false);
286 mApnContexts.put(type, apnContext);
290 protected void initApnContextsAndDataConnection() {
291 boolean defaultEnabled = SystemProperties.getBoolean(DEFALUT_DATA_ON_BOOT_PROP, true);
292 // Load device network attributes from resources
293 String[] networkConfigStrings = mPhone.getContext().getResources().getStringArray(
294 com.android.internal.R.array.networkAttributes);
295 for (String networkConfigString : networkConfigStrings) {
296 NetworkConfig networkConfig = new NetworkConfig(networkConfigString);
297 ApnContext apnContext = null;
299 switch (networkConfig.type) {
300 case ConnectivityManager.TYPE_MOBILE:
301 apnContext = addApnContext(PhoneConstants.APN_TYPE_DEFAULT);
302 apnContext.setEnabled(defaultEnabled);
304 case ConnectivityManager.TYPE_MOBILE_MMS:
305 apnContext = addApnContext(PhoneConstants.APN_TYPE_MMS);
307 case ConnectivityManager.TYPE_MOBILE_SUPL:
308 apnContext = addApnContext(PhoneConstants.APN_TYPE_SUPL);
310 case ConnectivityManager.TYPE_MOBILE_DUN:
311 apnContext = addApnContext(PhoneConstants.APN_TYPE_DUN);
313 case ConnectivityManager.TYPE_MOBILE_HIPRI:
314 apnContext = addApnContext(PhoneConstants.APN_TYPE_HIPRI);
315 ApnContext defaultContext = mApnContexts.get(PhoneConstants.APN_TYPE_DEFAULT);
316 if (defaultContext != null) {
317 applyNewState(apnContext, apnContext.isEnabled(),
318 defaultContext.getDependencyMet());
320 // the default will set the hipri dep-met when it is created
323 case ConnectivityManager.TYPE_MOBILE_FOTA:
324 apnContext = addApnContext(PhoneConstants.APN_TYPE_FOTA);
326 case ConnectivityManager.TYPE_MOBILE_IMS:
327 apnContext = addApnContext(PhoneConstants.APN_TYPE_IMS);
329 case ConnectivityManager.TYPE_MOBILE_CBS:
330 apnContext = addApnContext(PhoneConstants.APN_TYPE_CBS);
333 // skip unknown types
336 if (apnContext != null) {
337 // set the prop, but also apply the newly set enabled and dependency values
338 onSetDependencyMet(apnContext.getApnType(), networkConfig.dependencyMet);
344 protected LinkProperties getLinkProperties(String apnType) {
345 ApnContext apnContext = mApnContexts.get(apnType);
346 if (apnContext != null) {
347 DataConnectionAc dcac = apnContext.getDataConnectionAc();
349 if (DBG) log("return link properites for " + apnType);
350 return dcac.getLinkPropertiesSync();
353 if (DBG) log("return new LinkProperties");
354 return new LinkProperties();
358 protected LinkCapabilities getLinkCapabilities(String apnType) {
359 ApnContext apnContext = mApnContexts.get(apnType);
360 if (apnContext!=null) {
361 DataConnectionAc dataConnectionAc = apnContext.getDataConnectionAc();
362 if (dataConnectionAc != null) {
363 if (DBG) log("get active pdp is not null, return link Capabilities for " + apnType);
364 return dataConnectionAc.getLinkCapabilitiesSync();
367 if (DBG) log("return new LinkCapabilities");
368 return new LinkCapabilities();
372 // Return all active apn types
373 public String[] getActiveApnTypes() {
374 if (DBG) log("get all active apn types");
375 ArrayList<String> result = new ArrayList<String>();
377 for (ApnContext apnContext : mApnContexts.values()) {
378 if (apnContext.isReady()) {
379 result.add(apnContext.getApnType());
383 return (String[])result.toArray(new String[0]);
387 // Return active apn of specific apn type
388 public String getActiveApnString(String apnType) {
389 if (DBG) log( "get active apn string for type:" + apnType);
390 ApnContext apnContext = mApnContexts.get(apnType);
391 if (apnContext != null) {
392 ApnSetting apnSetting = apnContext.getApnSetting();
393 if (apnSetting != null) {
394 return apnSetting.apn;
401 public boolean isApnTypeEnabled(String apnType) {
402 ApnContext apnContext = mApnContexts.get(apnType);
403 if (apnContext == null) {
406 return apnContext.isEnabled();
410 protected void setState(DctConstants.State s) {
411 if (DBG) log("setState should not be used in GSM" + s);
414 // Return state of specific apn type
416 public DctConstants.State getState(String apnType) {
417 ApnContext apnContext = mApnContexts.get(apnType);
418 if (apnContext != null) {
419 return apnContext.getState();
421 return DctConstants.State.FAILED;
424 // Return state of overall
425 public DctConstants.State getOverallState() {
426 boolean isConnecting = false;
427 boolean isFailed = true; // All enabled Apns should be FAILED.
428 boolean isAnyEnabled = false;
430 for (ApnContext apnContext : mApnContexts.values()) {
431 if (apnContext.isEnabled()) {
433 switch (apnContext.getState()) {
436 if (DBG) log("overall state is CONNECTED");
437 return DctConstants.State.CONNECTED;
451 if (!isAnyEnabled) { // Nothing enabled. return IDLE.
452 if (DBG) log( "overall state is IDLE");
453 return DctConstants.State.IDLE;
457 if (DBG) log( "overall state is CONNECTING");
458 return DctConstants.State.CONNECTING;
459 } else if (!isFailed) {
460 if (DBG) log( "overall state is IDLE");
461 return DctConstants.State.IDLE;
463 if (DBG) log( "overall state is FAILED");
464 return DctConstants.State.FAILED;
469 * Ensure that we are connected to an APN of the specified type.
471 * @param type the APN type
472 * @return Success is indicated by {@code PhoneConstants.APN_ALREADY_ACTIVE} or
473 * {@code PhoneConstants.APN_REQUEST_STARTED}. In the latter case, a
474 * broadcast will be sent by the ConnectivityManager when a
475 * connection to the APN has been established.
478 public synchronized int enableApnType(String apnType) {
479 ApnContext apnContext = mApnContexts.get(apnType);
480 if (apnContext == null || !isApnTypeAvailable(apnType)) {
481 if (DBG) log("enableApnType: " + apnType + " is type not available");
482 return PhoneConstants.APN_TYPE_NOT_AVAILABLE;
485 // If already active, return
486 if (DBG) log("enableApnType: " + apnType + " mState(" + apnContext.getState() + ")");
488 if (apnContext.getState() == DctConstants.State.CONNECTED) {
489 if (DBG) log("enableApnType: return APN_ALREADY_ACTIVE");
490 return PhoneConstants.APN_ALREADY_ACTIVE;
492 setEnabled(apnTypeToId(apnType), true);
494 log("enableApnType: new apn request for type " + apnType +
495 " return APN_REQUEST_STARTED");
497 return PhoneConstants.APN_REQUEST_STARTED;
500 // A new APN has gone active and needs to send events to catch up with the
502 private void notifyApnIdUpToCurrent(String reason, ApnContext apnContext, String type) {
503 switch (apnContext.getState()) {
509 mPhone.notifyDataConnection(reason, type, PhoneConstants.DataState.CONNECTING);
513 mPhone.notifyDataConnection(reason, type, PhoneConstants.DataState.CONNECTING);
514 mPhone.notifyDataConnection(reason, type, PhoneConstants.DataState.CONNECTED);
520 public synchronized int disableApnType(String type) {
521 if (DBG) log("disableApnType:" + type);
522 ApnContext apnContext = mApnContexts.get(type);
524 if (apnContext != null) {
525 setEnabled(apnTypeToId(type), false);
526 if (apnContext.getState() != DctConstants.State.IDLE && apnContext.getState()
527 != DctConstants.State.FAILED) {
528 if (DBG) log("diableApnType: return APN_REQUEST_STARTED");
529 return PhoneConstants.APN_REQUEST_STARTED;
531 if (DBG) log("disableApnType: return APN_ALREADY_INACTIVE");
532 return PhoneConstants.APN_ALREADY_INACTIVE;
537 log("disableApnType: no apn context was found, return APN_REQUEST_FAILED");
539 return PhoneConstants.APN_REQUEST_FAILED;
544 protected boolean isApnTypeAvailable(String type) {
545 if (type.equals(PhoneConstants.APN_TYPE_DUN) && fetchDunApn() != null) {
549 if (mAllApns != null) {
550 for (ApnSetting apn : mAllApns) {
551 if (apn.canHandleType(type)) {
560 * Report on whether data connectivity is enabled for any APN.
561 * @return {@code false} if data connectivity has been explicitly disabled,
562 * {@code true} otherwise.
565 public boolean getAnyDataEnabled() {
566 synchronized (mDataEnabledLock) {
567 if (!(mInternalDataEnabled && mUserDataEnabled && sPolicyDataEnabled)) return false;
568 for (ApnContext apnContext : mApnContexts.values()) {
569 // Make sure we dont have a context that going down
570 // and is explicitly disabled.
571 if (isDataAllowed(apnContext)) {
579 private boolean isDataAllowed(ApnContext apnContext) {
580 return apnContext.isReady() && isDataAllowed();
583 //****** Called from ServiceStateTracker
585 * Invoked when ServiceStateTracker observes a transition from GPRS
588 protected void onDataConnectionDetached() {
590 * We presently believe it is unnecessary to tear down the PDP context
591 * when GPRS detaches, but we should stop the network polling.
593 if (DBG) log ("onDataConnectionDetached: stop polling and notify detached");
595 stopDataStallAlarm();
596 notifyDataConnection(Phone.REASON_DATA_DETACHED);
599 private void onDataConnectionAttached() {
600 if (DBG) log("onDataConnectionAttached");
601 if (getOverallState() == DctConstants.State.CONNECTED) {
602 if (DBG) log("onDataConnectionAttached: start polling notify attached");
604 startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
605 notifyDataConnection(Phone.REASON_DATA_ATTACHED);
607 // update APN availability so that APN can be enabled.
608 notifyOffApnsOfAvailability(Phone.REASON_DATA_ATTACHED);
610 mAutoAttachOnCreation = true;
611 setupDataOnReadyApns(Phone.REASON_DATA_ATTACHED);
615 protected boolean isDataAllowed() {
616 final boolean internalDataEnabled;
617 synchronized (mDataEnabledLock) {
618 internalDataEnabled = mInternalDataEnabled;
621 int gprsState = mPhone.getServiceStateTracker().getCurrentDataConnectionState();
622 boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
623 IccRecords r = mIccRecords.get();
624 boolean recordsLoaded = (r != null) ? r.getRecordsLoaded() : false;
627 (gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) &&
629 (mPhone.getState() == PhoneConstants.State.IDLE ||
630 mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) &&
631 internalDataEnabled &&
632 (!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) &&
635 if (!allowed && DBG) {
637 if (!((gprsState == ServiceState.STATE_IN_SERVICE) || mAutoAttachOnCreation)) {
638 reason += " - gprs= " + gprsState;
640 if (!recordsLoaded) reason += " - SIM not loaded";
641 if (mPhone.getState() != PhoneConstants.State.IDLE &&
642 !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
643 reason += " - PhoneState= " + mPhone.getState();
644 reason += " - Concurrent voice and data not allowed";
646 if (!internalDataEnabled) reason += " - mInternalDataEnabled= false";
647 if (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled()) {
648 reason += " - Roaming and data roaming not enabled";
650 if (mIsPsRestricted) reason += " - mIsPsRestricted= true";
651 if (!desiredPowerState) reason += " - desiredPowerState= false";
652 if (DBG) log("isDataAllowed: not allowed due to" + reason);
657 private void setupDataOnReadyApns(String reason) {
658 // Stop reconnect alarms on all data connections pending
659 // retry. Reset ApnContext state to IDLE.
660 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
661 if (dcac.getReconnectIntentSync() != null) {
662 cancelReconnectAlarm(dcac);
664 // update retry config for existing calls to match up
665 // ones for the new RAT.
666 if (dcac.dataConnection != null) {
667 Collection<ApnContext> apns = dcac.getApnListSync();
669 boolean hasDefault = false;
670 for (ApnContext apnContext : apns) {
671 if (apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)) {
676 configureRetry(dcac.dataConnection, hasDefault, 0);
680 // Be sure retry counts for Apncontexts and DC's are sync'd.
681 // When DCT/ApnContexts are refactored and we cleanup retrying
682 // this won't be needed.
683 resetAllRetryCounts();
685 // Only check for default APN state
686 for (ApnContext apnContext : mApnContexts.values()) {
687 if (apnContext.getState() == DctConstants.State.FAILED) {
688 // By this time, alarms for all failed Apns
689 // should be stopped if any.
690 // Make sure to set the state back to IDLE
691 // so that setup data can happen.
692 apnContext.setState(DctConstants.State.IDLE);
694 if (apnContext.isReady()) {
695 if (apnContext.getState() == DctConstants.State.IDLE ||
696 apnContext.getState() == DctConstants.State.SCANNING) {
697 apnContext.setReason(reason);
698 trySetupData(apnContext);
704 private boolean trySetupData(String reason, String type) {
706 log("trySetupData: " + type + " due to " + (reason == null ? "(unspecified)" : reason)
707 + " isPsRestricted=" + mIsPsRestricted);
711 type = PhoneConstants.APN_TYPE_DEFAULT;
714 ApnContext apnContext = mApnContexts.get(type);
716 if (apnContext == null ){
717 if (DBG) log("trySetupData new apn context for type:" + type);
718 apnContext = new ApnContext(type, LOG_TAG);
719 mApnContexts.put(type, apnContext);
721 apnContext.setReason(reason);
723 return trySetupData(apnContext);
726 private boolean trySetupData(ApnContext apnContext) {
728 log("trySetupData for type:" + apnContext.getApnType() +
729 " due to " + apnContext.getReason());
730 log("trySetupData with mIsPsRestricted=" + mIsPsRestricted);
733 if (mPhone.getSimulatedRadioControl() != null) {
734 // Assume data is connected on the simulator
735 // FIXME this can be improved
736 apnContext.setState(DctConstants.State.CONNECTED);
737 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
739 log("trySetupData: (fix?) We're on the simulator; assuming data is connected");
743 boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
745 if ((apnContext.getState() == DctConstants.State.IDLE ||
746 apnContext.getState() == DctConstants.State.SCANNING) &&
747 isDataAllowed(apnContext) && getAnyDataEnabled() && !isEmergency()) {
749 if (apnContext.getState() == DctConstants.State.IDLE) {
750 ArrayList<ApnSetting> waitingApns = buildWaitingApns(apnContext.getApnType());
751 if (waitingApns.isEmpty()) {
752 if (DBG) log("trySetupData: No APN found");
753 notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN, apnContext);
754 notifyOffApnsOfAvailability(apnContext.getReason());
757 apnContext.setWaitingApns(waitingApns);
759 log ("trySetupData: Create from mAllApns : " + apnListToString(mAllApns));
765 log ("Setup watingApns : " + apnListToString(apnContext.getWaitingApns()));
767 // apnContext.setReason(apnContext.getReason());
768 boolean retValue = setupData(apnContext);
769 notifyOffApnsOfAvailability(apnContext.getReason());
772 // TODO: check the condition.
773 if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
774 && (apnContext.getState() == DctConstants.State.IDLE
775 || apnContext.getState() == DctConstants.State.SCANNING))
776 mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
777 notifyOffApnsOfAvailability(apnContext.getReason());
783 // Disabled apn's still need avail/unavail notificiations - send them out
784 protected void notifyOffApnsOfAvailability(String reason) {
785 for (ApnContext apnContext : mApnContexts.values()) {
786 if (!apnContext.isReady()) {
787 if (DBG) log("notifyOffApnOfAvailability type:" + apnContext.getApnType());
788 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
789 apnContext.getApnType(),
790 PhoneConstants.DataState.DISCONNECTED);
793 log("notifyOffApnsOfAvailability skipped apn due to isReady==false: " +
794 apnContext.toString());
801 * If tearDown is true, this only tears down a CONNECTED session. Presently,
802 * there is no mechanism for abandoning an INITING/CONNECTING session,
803 * but would likely involve cancelling pending async requests or
804 * setting a flag or new state to ignore them when they came in
805 * @param tearDown true if the underlying GsmDataConnection should be
807 * @param reason reason for the clean up.
809 protected void cleanUpAllConnections(boolean tearDown, String reason) {
810 if (DBG) log("cleanUpAllConnections: tearDown=" + tearDown + " reason=" + reason);
812 for (ApnContext apnContext : mApnContexts.values()) {
813 apnContext.setReason(reason);
814 cleanUpConnection(tearDown, apnContext);
818 stopDataStallAlarm();
820 // TODO: Do we need mRequestedApnType?
821 mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
825 * Cleanup all connections.
827 * TODO: Cleanup only a specified connection passed as a parameter.
828 * Also, make sure when you clean up a conn, if it is last apply
829 * logic as though it is cleanupAllConnections
831 * @param tearDown true if the underlying DataConnection should be disconnected.
832 * @param reason for the clean up.
836 protected void onCleanUpAllConnections(String cause) {
837 cleanUpAllConnections(true, cause);
840 private void cleanUpConnection(boolean tearDown, ApnContext apnContext) {
842 if (apnContext == null) {
843 if (DBG) log("cleanUpConnection: apn context is null");
847 DataConnectionAc dcac = apnContext.getDataConnectionAc();
849 log("cleanUpConnection: E tearDown=" + tearDown + " reason=" + apnContext.getReason() +
850 " apnContext=" + apnContext);
853 if (apnContext.isDisconnected()) {
854 // The request is tearDown and but ApnContext is not connected.
855 // If apnContext is not enabled anymore, break the linkage to the DCAC/DC.
856 apnContext.setState(DctConstants.State.IDLE);
857 if (!apnContext.isReady()) {
858 apnContext.setDataConnection(null);
859 apnContext.setDataConnectionAc(null);
862 // Connection is still there. Try to clean up.
864 if (apnContext.getState() != DctConstants.State.DISCONNECTING) {
865 boolean disconnectAll = false;
866 if (PhoneConstants.APN_TYPE_DUN.equals(apnContext.getApnType())) {
867 ApnSetting dunSetting = fetchDunApn();
868 if (dunSetting != null &&
869 dunSetting.equals(apnContext.getApnSetting())) {
870 if (DBG) log("tearing down dedicated DUN connection");
871 // we need to tear it down - we brought it up just for dun and
872 // other people are camped on it and now dun is done. We need
873 // to stop using it and let the normal apn list get used to find
874 // connections for the remaining desired connections
875 disconnectAll = true;
879 log("cleanUpConnection: tearing down" + (disconnectAll ? " all" :""));
881 Message msg = obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, apnContext);
883 apnContext.getDataConnection().tearDownAll(apnContext.getReason(), msg);
885 apnContext.getDataConnection().tearDown(apnContext.getReason(), msg);
887 apnContext.setState(DctConstants.State.DISCONNECTING);
890 // apn is connected but no reference to dcac.
891 // Should not be happen, but reset the state in case.
892 apnContext.setState(DctConstants.State.IDLE);
893 mPhone.notifyDataConnection(apnContext.getReason(),
894 apnContext.getApnType());
898 // force clean up the data connection.
899 if (dcac != null) dcac.resetSync();
900 apnContext.setState(DctConstants.State.IDLE);
901 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
902 apnContext.setDataConnection(null);
903 apnContext.setDataConnectionAc(null);
906 // make sure reconnection alarm is cleaned up if there is no ApnContext
907 // associated to the connection.
909 Collection<ApnContext> apnList = dcac.getApnListSync();
910 if (apnList.isEmpty()) {
911 cancelReconnectAlarm(dcac);
915 log("cleanUpConnection: X tearDown=" + tearDown + " reason=" + apnContext.getReason() +
916 " apnContext=" + apnContext + " dc=" + apnContext.getDataConnection());
921 * Cancels the alarm associated with DCAC.
923 * @param DataConnectionAc on which the alarm should be stopped.
925 private void cancelReconnectAlarm(DataConnectionAc dcac) {
926 if (dcac == null) return;
928 PendingIntent intent = dcac.getReconnectIntentSync();
930 if (intent != null) {
932 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
934 dcac.setReconnectIntentSync(null);
939 * @param types comma delimited list of APN types
940 * @return array of APN types
942 private String[] parseTypes(String types) {
944 // If unset, set to DEFAULT.
945 if (types == null || types.equals("")) {
946 result = new String[1];
947 result[0] = PhoneConstants.APN_TYPE_ALL;
949 result = types.split(",");
954 private ArrayList<ApnSetting> createApnList(Cursor cursor) {
955 ArrayList<ApnSetting> result = new ArrayList<ApnSetting>();
956 if (cursor.moveToFirst()) {
958 String[] types = parseTypes(
959 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
960 ApnSetting apn = new ApnSetting(
961 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
962 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
963 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
964 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
965 NetworkUtils.trimV4AddrZeros(
967 cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
968 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)),
969 NetworkUtils.trimV4AddrZeros(
971 cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
972 NetworkUtils.trimV4AddrZeros(
974 cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
975 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)),
976 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
977 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
978 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
980 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
981 cursor.getString(cursor.getColumnIndexOrThrow(
982 Telephony.Carriers.ROAMING_PROTOCOL)),
983 cursor.getInt(cursor.getColumnIndexOrThrow(
984 Telephony.Carriers.CARRIER_ENABLED)) == 1,
985 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER)));
987 } while (cursor.moveToNext());
989 if (DBG) log("createApnList: X result=" + result);
993 private boolean dataConnectionNotInUse(DataConnectionAc dcac) {
994 if (DBG) log("dataConnectionNotInUse: check if dcac is inuse dc=" + dcac.dataConnection);
995 for (ApnContext apnContext : mApnContexts.values()) {
996 if (apnContext.getDataConnectionAc() == dcac) {
997 if (DBG) log("dataConnectionNotInUse: in use by apnContext=" + apnContext);
1001 // TODO: Fix retry handling so free DataConnections have empty apnlists.
1002 // Probably move retry handling into DataConnections and reduce complexity
1004 for (ApnContext apnContext : dcac.getApnListSync()) {
1006 log("dataConnectionNotInUse: removing apnContext=" + apnContext);
1008 dcac.removeApnContextSync(apnContext);
1010 if (DBG) log("dataConnectionNotInUse: not in use return true");
1014 private GsmDataConnection findFreeDataConnection() {
1015 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
1016 if (dcac.isInactiveSync() && dataConnectionNotInUse(dcac)) {
1017 DataConnection dc = dcac.dataConnection;
1019 log("findFreeDataConnection: found free GsmDataConnection=" +
1020 " dcac=" + dcac + " dc=" + dc);
1022 return (GsmDataConnection) dc;
1025 log("findFreeDataConnection: NO free GsmDataConnection");
1029 protected GsmDataConnection findReadyDataConnection(ApnSetting apn) {
1034 log("findReadyDataConnection: apn string <" + apn + ">" +
1035 " dcacs.size=" + mDataConnectionAsyncChannels.size());
1037 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
1038 ApnSetting apnSetting = dcac.getApnSettingSync();
1040 log("findReadyDataConnection: dc apn string <" +
1041 (apnSetting != null ? (apnSetting.toString()) : "null") + ">");
1043 if ((apnSetting != null) && TextUtils.equals(apnSetting.toString(), apn.toString())) {
1044 DataConnection dc = dcac.dataConnection;
1046 log("findReadyDataConnection: found ready GsmDataConnection=" +
1047 " dcac=" + dcac + " dc=" + dc);
1049 return (GsmDataConnection) dc;
1056 private boolean setupData(ApnContext apnContext) {
1057 if (DBG) log("setupData: apnContext=" + apnContext);
1059 GsmDataConnection dc;
1061 int profileId = getApnProfileID(apnContext.getApnType());
1062 apn = apnContext.getNextWaitingApn();
1064 if (DBG) log("setupData: return for no apn found!");
1069 dc = (GsmDataConnection) checkForConnectionForApnContext(apnContext);
1072 dc = findReadyDataConnection(apn);
1075 if (DBG) log("setupData: No ready GsmDataConnection found!");
1076 // TODO: When allocating you are mapping type to id. If more than 1 free,
1077 // then could findFreeDataConnection get the wrong one??
1078 dc = findFreeDataConnection();
1082 dc = createDataConnection();
1086 if (DBG) log("setupData: No free GsmDataConnection found!");
1090 apn = mDataConnectionAsyncChannels.get(dc.getDataConnectionId()).getApnSettingSync();
1093 DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId());
1094 dc.setProfileId( profileId ); // assumed no connection sharing on profiled types
1096 int refCount = dcac.getRefCountSync();
1097 if (DBG) log("setupData: init dc and apnContext refCount=" + refCount);
1099 // configure retry count if no other Apn is using the same connection.
1100 if (refCount == 0) {
1101 configureRetry(dc, apn.canHandleType(PhoneConstants.APN_TYPE_DEFAULT),
1102 apnContext.getRetryCount());
1104 apnContext.setDataConnectionAc(dcac);
1105 apnContext.setDataConnection(dc);
1107 apnContext.setApnSetting(apn);
1108 apnContext.setState(DctConstants.State.INITING);
1109 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1110 // If reconnect alarm is active on this DataConnection, wait for the alarm being
1111 // fired so that we don't disruppt data retry pattern engaged.
1112 if (apnContext.getDataConnectionAc().getReconnectIntentSync() != null) {
1113 if (DBG) log("setupData: data reconnection pending");
1114 apnContext.setState(DctConstants.State.FAILED);
1115 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1119 Message msg = obtainMessage();
1120 msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
1121 msg.obj = apnContext;
1122 dc.bringUp(msg, apn);
1124 if (DBG) log("setupData: initing!");
1129 * Handles changes to the APN database.
1131 private void onApnChanged() {
1132 DctConstants.State overallState = getOverallState();
1133 boolean isDisconnected = (overallState == DctConstants.State.IDLE ||
1134 overallState == DctConstants.State.FAILED);
1136 if (mPhone instanceof GSMPhone) {
1137 // The "current" may no longer be valid. MMS depends on this to send properly. TBD
1138 ((GSMPhone)mPhone).updateCurrentCarrierInProvider();
1141 // TODO: It'd be nice to only do this if the changed entrie(s)
1142 // match the current operator.
1143 if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections");
1145 cleanUpAllConnections(!isDisconnected, Phone.REASON_APN_CHANGED);
1146 if (isDisconnected) {
1147 setupDataOnReadyApns(Phone.REASON_APN_CHANGED);
1152 * @param cid Connection id provided from RIL.
1153 * @return DataConnectionAc associated with specified cid.
1155 private DataConnectionAc findDataConnectionAcByCid(int cid) {
1156 for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
1157 if (dcac.getCidSync() == cid) {
1165 * @param dcacs Collection of DataConnectionAc reported from RIL.
1166 * @return List of ApnContext which is connected, but is not present in
1167 * data connection list reported from RIL.
1169 private List<ApnContext> findApnContextToClean(Collection<DataConnectionAc> dcacs) {
1170 if (dcacs == null) return null;
1172 if (DBG) log("findApnContextToClean(ar): E dcacs=" + dcacs);
1174 ArrayList<ApnContext> list = new ArrayList<ApnContext>();
1175 for (ApnContext apnContext : mApnContexts.values()) {
1176 if (apnContext.getState() == DctConstants.State.CONNECTED) {
1177 boolean found = false;
1178 for (DataConnectionAc dcac : dcacs) {
1179 if (dcac == apnContext.getDataConnectionAc()) {
1180 // ApnContext holds the ref to dcac present in data call list.
1186 // ApnContext does not have dcac reported in data call list.
1187 // Fetch all the ApnContexts that map to this dcac which are in
1188 // INITING state too.
1189 if (DBG) log("findApnContextToClean(ar): Connected apn not found in the list (" +
1190 apnContext.toString() + ")");
1191 if (apnContext.getDataConnectionAc() != null) {
1192 list.addAll(apnContext.getDataConnectionAc().getApnListSync());
1194 list.add(apnContext);
1199 if (DBG) log("findApnContextToClean(ar): X list=" + list);
1204 * @param ar is the result of RIL_REQUEST_DATA_CALL_LIST
1205 * or RIL_UNSOL_DATA_CALL_LIST_CHANGED
1207 private void onDataStateChanged (AsyncResult ar) {
1208 ArrayList<DataCallState> dataCallStates;
1210 if (DBG) log("onDataStateChanged(ar): E");
1211 dataCallStates = (ArrayList<DataCallState>)(ar.result);
1213 if (ar.exception != null) {
1214 // This is probably "radio not available" or something
1215 // of that sort. If so, the whole connection is going
1216 // to come down soon anyway
1217 if (DBG) log("onDataStateChanged(ar): exception; likely radio not available, ignore");
1220 if (DBG) log("onDataStateChanged(ar): DataCallState size=" + dataCallStates.size());
1222 boolean isAnyDataCallDormant = false;
1223 boolean isAnyDataCallActive = false;
1225 // Create a hash map to store the dataCallState of each DataConnectionAc
1226 HashMap<DataCallState, DataConnectionAc> dataCallStateToDcac;
1227 dataCallStateToDcac = new HashMap<DataCallState, DataConnectionAc>();
1228 for (DataCallState dataCallState : dataCallStates) {
1229 DataConnectionAc dcac = findDataConnectionAcByCid(dataCallState.cid);
1231 if (dcac != null) dataCallStateToDcac.put(dataCallState, dcac);
1234 // A list of apns to cleanup, those that aren't in the list we know we have to cleanup
1235 List<ApnContext> apnsToCleanup = findApnContextToClean(dataCallStateToDcac.values());
1237 // Find which connections have changed state and send a notification or cleanup
1238 for (DataCallState newState : dataCallStates) {
1239 DataConnectionAc dcac = dataCallStateToDcac.get(newState);
1242 loge("onDataStateChanged(ar): No associated DataConnection ignore");
1246 if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_UP) isAnyDataCallActive = true;
1247 if (newState.active == DATA_CONNECTION_ACTIVE_PH_LINK_DOWN) isAnyDataCallDormant = true;
1249 // The list of apn's associated with this DataConnection
1250 Collection<ApnContext> apns = dcac.getApnListSync();
1252 // Find which ApnContexts of this DC are in the "Connected/Connecting" state.
1253 ArrayList<ApnContext> connectedApns = new ArrayList<ApnContext>();
1254 for (ApnContext apnContext : apns) {
1255 if (apnContext.getState() == DctConstants.State.CONNECTED ||
1256 apnContext.getState() == DctConstants.State.CONNECTING ||
1257 apnContext.getState() == DctConstants.State.INITING) {
1258 connectedApns.add(apnContext);
1261 if (connectedApns.size() == 0) {
1262 if (DBG) log("onDataStateChanged(ar): no connected apns");
1264 // Determine if the connection/apnContext should be cleaned up
1265 // or just a notification should be sent out.
1266 if (DBG) log("onDataStateChanged(ar): Found ConnId=" + newState.cid
1267 + " newState=" + newState.toString());
1268 if (newState.active == 0) {
1270 log("onDataStateChanged(ar): inactive, cleanup apns=" + connectedApns);
1272 apnsToCleanup.addAll(connectedApns);
1274 // Its active so update the DataConnections link properties
1275 UpdateLinkPropertyResult result =
1276 dcac.updateLinkPropertiesDataCallStateSync(newState);
1277 if (result.oldLp.equals(result.newLp)) {
1278 if (DBG) log("onDataStateChanged(ar): no change");
1280 if (result.oldLp.isIdenticalInterfaceName(result.newLp)) {
1281 if (! result.oldLp.isIdenticalDnses(result.newLp) ||
1282 ! result.oldLp.isIdenticalRoutes(result.newLp) ||
1283 ! result.oldLp.isIdenticalHttpProxy(result.newLp) ||
1284 ! result.oldLp.isIdenticalAddresses(result.newLp)) {
1285 // If the same address type was removed and added we need to cleanup
1286 CompareResult<LinkAddress> car =
1287 result.oldLp.compareAddresses(result.newLp);
1289 log("onDataStateChanged: oldLp=" + result.oldLp +
1290 " newLp=" + result.newLp + " car=" + car);
1292 boolean needToClean = false;
1293 for (LinkAddress added : car.added) {
1294 for (LinkAddress removed : car.removed) {
1295 if (NetworkUtils.addressTypeMatches(removed.getAddress(),
1296 added.getAddress())) {
1304 log("onDataStateChanged(ar): addr change, cleanup apns=" +
1305 connectedApns + " oldLp=" + result.oldLp +
1306 " newLp=" + result.newLp);
1308 apnsToCleanup.addAll(connectedApns);
1310 if (DBG) log("onDataStateChanged(ar): simple change");
1311 for (ApnContext apnContext : connectedApns) {
1312 mPhone.notifyDataConnection(
1313 PhoneConstants.REASON_LINK_PROPERTIES_CHANGED,
1314 apnContext.getApnType());
1319 log("onDataStateChanged(ar): no changes");
1324 log("onDataStateChanged(ar): interface change, cleanup apns="
1327 apnsToCleanup.addAll(connectedApns);
1334 if (isAnyDataCallDormant && !isAnyDataCallActive) {
1335 // There is no way to indicate link activity per APN right now. So
1336 // Link Activity will be considered dormant only when all data calls
1338 // If a single data call is in dormant state and none of the data
1339 // calls are active broadcast overall link state as dormant.
1340 mActivity = DctConstants.Activity.DORMANT;
1342 log("onDataStateChanged: Data Activity updated to DORMANT. stopNetStatePoll");
1346 mActivity = DctConstants.Activity.NONE;
1348 log("onDataStateChanged: Data Activity updated to NONE. " +
1349 "isAnyDataCallActive = " + isAnyDataCallActive +
1350 " isAnyDataCallDormant = " + isAnyDataCallDormant);
1352 if (isAnyDataCallActive) startNetStatPoll();
1354 mPhone.notifyDataActivity();
1356 if (apnsToCleanup.size() != 0) {
1357 // Add an event log when the network drops PDP
1358 int cid = getCellLocationId();
1359 EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid,
1360 TelephonyManager.getDefault().getNetworkType());
1363 // Cleanup those dropped connections
1364 if (DBG) log("onDataStateChange(ar): apnsToCleanup=" + apnsToCleanup);
1365 for (ApnContext apnContext : apnsToCleanup) {
1366 cleanUpConnection(true, apnContext);
1369 if (DBG) log("onDataStateChanged(ar): X");
1372 private void notifyDefaultData(ApnContext apnContext) {
1374 log("notifyDefaultData: type=" + apnContext.getApnType()
1375 + ", reason:" + apnContext.getReason());
1377 apnContext.setState(DctConstants.State.CONNECTED);
1378 // setState(DctConstants.State.CONNECTED);
1379 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1381 startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
1382 // reset reconnect timer
1383 apnContext.setRetryCount(0);
1386 // TODO: For multiple Active APNs not exactly sure how to do this.
1387 protected void gotoIdleAndNotifyDataConnection(String reason) {
1388 if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
1389 notifyDataConnection(reason);
1394 protected void restartRadio() {
1395 if (DBG) log("restartRadio: ************TURN OFF RADIO**************");
1396 cleanUpAllConnections(true, Phone.REASON_RADIO_TURNED_OFF);
1397 mPhone.getServiceStateTracker().powerOffRadioSafely(this);
1398 /* Note: no need to call setRadioPower(true). Assuming the desired
1399 * radio power state is still ON (as tracked by ServiceStateTracker),
1400 * ServiceStateTracker will call setRadioPower when it receives the
1401 * RADIO_STATE_CHANGED notification for the power off. And if the
1402 * desired power state has changed in the interim, we don't want to
1403 * override it with an unconditional power on.
1406 int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0"));
1407 SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1));
1411 * Returns true if the last fail cause is something that
1412 * seems like it deserves an error notification.
1413 * Transient errors are ignored
1415 private boolean shouldPostNotification(GsmDataConnection.FailCause cause) {
1416 return (cause != GsmDataConnection.FailCause.UNKNOWN);
1420 * Return true if data connection need to be setup after disconnected due to
1423 * @param reason the reason why data is disconnected
1424 * @return true if try setup data connection is need for this reason
1426 private boolean retryAfterDisconnected(String reason) {
1427 boolean retry = true;
1429 if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) {
1435 private void reconnectAfterFail(FailCause lastFailCauseCode,
1436 ApnContext apnContext, int retryOverride) {
1437 if (apnContext == null) {
1438 loge("reconnectAfterFail: apnContext == null, impossible");
1442 log("reconnectAfterFail: lastFailCause=" + lastFailCauseCode +
1443 " retryOverride=" + retryOverride + " apnContext=" + apnContext);
1445 if ((apnContext.getState() == DctConstants.State.FAILED) &&
1446 (apnContext.getDataConnection() != null)) {
1447 if (!apnContext.getDataConnection().isRetryNeeded()) {
1448 if (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)) {
1449 mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
1452 if (mReregisterOnReconnectFailure) {
1453 // We've re-registerd once now just retry forever.
1454 apnContext.getDataConnection().retryForeverUsingLastTimeout();
1456 // Try to Re-register to the network.
1457 if (DBG) log("reconnectAfterFail: activate failed, Reregistering to network");
1458 mReregisterOnReconnectFailure = true;
1459 mPhone.getServiceStateTracker().reRegisterNetwork(null);
1460 apnContext.setRetryCount(0);
1465 // If retry needs to be backed off for specific case (determined by RIL/Modem)
1466 // use the specified timer instead of pre-configured retry pattern.
1467 int nextReconnectDelay = retryOverride;
1468 if (nextReconnectDelay < 0) {
1469 nextReconnectDelay = apnContext.getDataConnection().getRetryTimer();
1470 apnContext.getDataConnection().increaseRetryCount();
1472 log("reconnectAfterFail: increaseRetryCount=" +
1473 apnContext.getDataConnection().getRetryCount() +
1474 " nextReconnectDelay=" + nextReconnectDelay);
1477 startAlarmForReconnect(nextReconnectDelay, apnContext);
1479 if (!shouldPostNotification(lastFailCauseCode)) {
1481 log("reconnectAfterFail: NOT Posting GPRS Unavailable notification "
1482 + "-- likely transient error");
1485 notifyNoData(lastFailCauseCode, apnContext);
1490 private void startAlarmForReconnect(int delay, ApnContext apnContext) {
1492 DataConnectionAc dcac = apnContext.getDataConnectionAc();
1494 if ((dcac == null) || (dcac.dataConnection == null)) {
1495 // should not happen, but just in case.
1496 loge("startAlarmForReconnect: null dcac or dc.");
1501 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
1503 Intent intent = new Intent(INTENT_RECONNECT_ALARM + '.' +
1504 dcac.dataConnection.getDataConnectionId());
1505 String reason = apnContext.getReason();
1506 intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason);
1507 int connectionId = dcac.dataConnection.getDataConnectionId();
1508 intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, connectionId);
1510 // TODO: Until a real fix is created, which probably entails pushing
1511 // retires into the DC itself, this fix gets the retry count and
1512 // puts it in the reconnect alarm. When the reconnect alarm fires
1513 // onActionIntentReconnectAlarm is called which will use the value saved
1514 // here and save it in the ApnContext and send the EVENT_CONNECT message
1515 // which invokes setupData. Then setupData will use the value in the ApnContext
1516 // and to tell the DC to set the retry count in the retry manager.
1517 int retryCount = dcac.dataConnection.getRetryCount();
1518 intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_RETRY_COUNT, retryCount);
1521 log("startAlarmForReconnect: next attempt in " + (delay / 1000) + "s" +
1522 " reason='" + reason + "' connectionId=" + connectionId +
1523 " retryCount=" + retryCount);
1526 PendingIntent alarmIntent = PendingIntent.getBroadcast (mPhone.getContext(), 0,
1527 intent, PendingIntent.FLAG_UPDATE_CURRENT);
1528 dcac.setReconnectIntentSync(alarmIntent);
1529 am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1530 SystemClock.elapsedRealtime() + delay, alarmIntent);
1534 private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode,
1535 ApnContext apnContext) {
1536 if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
1537 apnContext.setState(DctConstants.State.FAILED);
1538 if (lastFailCauseCode.isPermanentFail()
1539 && (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT))) {
1540 mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
1544 private void onRecordsLoaded() {
1545 if (DBG) log("onRecordsLoaded: createAllApnList");
1547 if (mPhone.mCM.getRadioState().isOn()) {
1548 if (DBG) log("onRecordsLoaded: notifying data availability");
1549 notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);
1551 setupDataOnReadyApns(Phone.REASON_SIM_LOADED);
1555 protected void onSetDependencyMet(String apnType, boolean met) {
1556 // don't allow users to tweak hipri to work around default dependency not met
1557 if (PhoneConstants.APN_TYPE_HIPRI.equals(apnType)) return;
1559 ApnContext apnContext = mApnContexts.get(apnType);
1560 if (apnContext == null) {
1561 loge("onSetDependencyMet: ApnContext not found in onSetDependencyMet(" +
1562 apnType + ", " + met + ")");
1565 applyNewState(apnContext, apnContext.isEnabled(), met);
1566 if (PhoneConstants.APN_TYPE_DEFAULT.equals(apnType)) {
1567 // tie actions on default to similar actions on HIPRI regarding dependencyMet
1568 apnContext = mApnContexts.get(PhoneConstants.APN_TYPE_HIPRI);
1569 if (apnContext != null) applyNewState(apnContext, apnContext.isEnabled(), met);
1573 private void applyNewState(ApnContext apnContext, boolean enabled, boolean met) {
1574 boolean cleanup = false;
1575 boolean trySetup = false;
1577 log("applyNewState(" + apnContext.getApnType() + ", " + enabled +
1578 "(" + apnContext.isEnabled() + "), " + met + "(" +
1579 apnContext.getDependencyMet() +"))");
1581 if (apnContext.isReady()) {
1582 if (enabled && met) return;
1584 apnContext.setReason(Phone.REASON_DATA_DISABLED);
1586 apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET);
1590 if (enabled && met) {
1591 if (apnContext.isEnabled()) {
1592 apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_MET);
1594 apnContext.setReason(Phone.REASON_DATA_ENABLED);
1596 if (apnContext.getState() == DctConstants.State.FAILED) {
1597 apnContext.setState(DctConstants.State.IDLE);
1602 apnContext.setEnabled(enabled);
1603 apnContext.setDependencyMet(met);
1604 if (cleanup) cleanUpConnection(true, apnContext);
1605 if (trySetup) trySetupData(apnContext);
1608 private DataConnection checkForConnectionForApnContext(ApnContext apnContext) {
1609 // Loop through all apnContexts looking for one with a conn that satisfies this apnType
1610 String apnType = apnContext.getApnType();
1611 ApnSetting dunSetting = null;
1613 if (PhoneConstants.APN_TYPE_DUN.equals(apnType)) {
1614 dunSetting = fetchDunApn();
1617 DataConnection potential = null;
1618 for (ApnContext c : mApnContexts.values()) {
1619 DataConnection conn = c.getDataConnection();
1621 ApnSetting apnSetting = c.getApnSetting();
1622 if (dunSetting != null) {
1623 if (dunSetting.equals(apnSetting)) {
1624 switch (c.getState()) {
1627 log("checkForConnectionForApnContext: apnContext=" +
1628 apnContext + " found conn=" + conn);
1635 } else if (apnSetting != null && apnSetting.canHandleType(apnType)) {
1636 switch (c.getState()) {
1639 log("checkForConnectionForApnContext: apnContext=" + apnContext +
1640 " found conn=" + conn);
1649 if (potential != null) {
1651 log("checkForConnectionForApnContext: apnContext=" + apnContext +
1652 " found conn=" + potential);
1657 if (DBG) log("checkForConnectionForApnContext: apnContext=" + apnContext + " NO conn");
1662 protected void onEnableApn(int apnId, int enabled) {
1663 ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
1664 if (apnContext == null) {
1665 loge("onEnableApn(" + apnId + ", " + enabled + "): NO ApnContext");
1668 // TODO change our retry manager to use the appropriate numbers for the new APN
1669 if (DBG) log("onEnableApn: apnContext=" + apnContext + " call applyNewState");
1670 applyNewState(apnContext, enabled == DctConstants.ENABLED, apnContext.getDependencyMet());
1674 // TODO: We shouldnt need this.
1675 protected boolean onTrySetupData(String reason) {
1676 if (DBG) log("onTrySetupData: reason=" + reason);
1677 setupDataOnReadyApns(reason);
1681 protected boolean onTrySetupData(ApnContext apnContext) {
1682 if (DBG) log("onTrySetupData: apnContext=" + apnContext);
1683 return trySetupData(apnContext);
1687 protected void onRoamingOff() {
1688 if (DBG) log("onRoamingOff");
1690 if (mUserDataEnabled == false) return;
1692 if (getDataOnRoamingEnabled() == false) {
1693 notifyOffApnsOfAvailability(Phone.REASON_ROAMING_OFF);
1694 setupDataOnReadyApns(Phone.REASON_ROAMING_OFF);
1696 notifyDataConnection(Phone.REASON_ROAMING_OFF);
1701 protected void onRoamingOn() {
1702 if (mUserDataEnabled == false) return;
1704 if (getDataOnRoamingEnabled()) {
1705 if (DBG) log("onRoamingOn: setup data on roaming");
1706 setupDataOnReadyApns(Phone.REASON_ROAMING_ON);
1707 notifyDataConnection(Phone.REASON_ROAMING_ON);
1709 if (DBG) log("onRoamingOn: Tear down data connection on roaming.");
1710 cleanUpAllConnections(true, Phone.REASON_ROAMING_ON);
1711 notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
1716 protected void onRadioAvailable() {
1717 if (DBG) log("onRadioAvailable");
1718 if (mPhone.getSimulatedRadioControl() != null) {
1719 // Assume data is connected on the simulator
1720 // FIXME this can be improved
1721 // setState(DctConstants.State.CONNECTED);
1722 notifyDataConnection(null);
1724 log("onRadioAvailable: We're on the simulator; assuming data is connected");
1727 IccRecords r = mIccRecords.get();
1728 if (r != null && r.getRecordsLoaded()) {
1729 notifyOffApnsOfAvailability(null);
1732 if (getOverallState() != DctConstants.State.IDLE) {
1733 cleanUpConnection(true, null);
1738 protected void onRadioOffOrNotAvailable() {
1739 // Make sure our reconnect delay starts at the initial value
1740 // next time the radio comes on
1742 resetAllRetryCounts();
1743 mReregisterOnReconnectFailure = false;
1745 if (mPhone.getSimulatedRadioControl() != null) {
1746 // Assume data is connected on the simulator
1747 // FIXME this can be improved
1748 log("We're on the simulator; assuming radio off is meaningless");
1750 if (DBG) log("onRadioOffOrNotAvailable: is off and clean up all connections");
1751 cleanUpAllConnections(false, Phone.REASON_RADIO_TURNED_OFF);
1753 notifyOffApnsOfAvailability(null);
1757 protected void onDataSetupComplete(AsyncResult ar) {
1759 DataConnection.FailCause cause = DataConnection.FailCause.UNKNOWN;
1760 boolean handleError = false;
1761 ApnContext apnContext = null;
1763 if(ar.userObj instanceof ApnContext){
1764 apnContext = (ApnContext)ar.userObj;
1766 throw new RuntimeException("onDataSetupComplete: No apnContext");
1769 if (isDataSetupCompleteOk(ar)) {
1770 DataConnectionAc dcac = apnContext.getDataConnectionAc();
1773 // Note: To change radio.test.onDSC.null.dcac from command line you need to
1774 // adb root and adb remount and from the command line you can only change the
1775 // value to 1 once. To change it a second time you can reboot or execute
1776 // adb shell stop and then adb shell start. The command line to set the value is:
1777 // adb shell sqlite3 /data/data/com.android.providers.settings/databases/settings.db "insert into system (name,value) values ('radio.test.onDSC.null.dcac', '1');"
1778 ContentResolver cr = mPhone.getContext().getContentResolver();
1779 String radioTestProperty = "radio.test.onDSC.null.dcac";
1780 if (Settings.System.getInt(cr, radioTestProperty, 0) == 1) {
1781 log("onDataSetupComplete: " + radioTestProperty +
1782 " is true, set dcac to null and reset property to false");
1784 Settings.System.putInt(cr, radioTestProperty, 0);
1785 log("onDataSetupComplete: " + radioTestProperty + "=" +
1786 Settings.System.getInt(mPhone.getContext().getContentResolver(),
1787 radioTestProperty, -1));
1791 log("onDataSetupComplete: no connection to DC, handle as error");
1792 cause = DataConnection.FailCause.CONNECTION_TO_DATACONNECTIONAC_BROKEN;
1795 DataConnection dc = apnContext.getDataConnection();
1796 ApnSetting apn = apnContext.getApnSetting();
1798 log("onDataSetupComplete: success apn=" + (apn == null ? "unknown" : apn.apn));
1800 if (apn != null && apn.proxy != null && apn.proxy.length() != 0) {
1802 String port = apn.port;
1803 if (TextUtils.isEmpty(port)) port = "8080";
1804 ProxyProperties proxy = new ProxyProperties(apn.proxy,
1805 Integer.parseInt(port), null);
1806 dcac.setLinkPropertiesHttpProxySync(proxy);
1807 } catch (NumberFormatException e) {
1808 loge("onDataSetupComplete: NumberFormatException making ProxyProperties (" +
1809 apn.port + "): " + e);
1813 // everything is setup
1814 if(TextUtils.equals(apnContext.getApnType(),PhoneConstants.APN_TYPE_DEFAULT)) {
1815 SystemProperties.set("gsm.defaultpdpcontext.active", "true");
1816 if (canSetPreferApn && mPreferredApn == null) {
1817 if (DBG) log("onDataSetupComplete: PREFERED APN is null");
1818 mPreferredApn = apn;
1819 if (mPreferredApn != null) {
1820 setPreferredApn(mPreferredApn.id);
1824 SystemProperties.set("gsm.defaultpdpcontext.active", "false");
1826 notifyDefaultData(apnContext);
1829 cause = (DataConnection.FailCause) (ar.result);
1831 ApnSetting apn = apnContext.getApnSetting();
1832 log(String.format("onDataSetupComplete: error apn=%s cause=%s",
1833 (apn == null ? "unknown" : apn.apn), cause));
1835 if (cause.isEventLoggable()) {
1836 // Log this failure to the Event Logs.
1837 int cid = getCellLocationId();
1838 EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL,
1839 cause.ordinal(), cid, TelephonyManager.getDefault().getNetworkType());
1842 // Count permanent failures and remove the APN we just tried
1843 if (cause.isPermanentFail()) apnContext.decWaitingApnsPermFailCount();
1845 apnContext.removeWaitingApn(apnContext.getApnSetting());
1847 log(String.format("onDataSetupComplete: WaitingApns.size=%d" +
1848 " WaitingApnsPermFailureCountDown=%d",
1849 apnContext.getWaitingApns().size(),
1850 apnContext.getWaitingApnsPermFailCount()));
1856 // See if there are more APN's to try
1857 if (apnContext.getWaitingApns().isEmpty()) {
1858 if (apnContext.getWaitingApnsPermFailCount() == 0) {
1860 log("onDataSetupComplete: All APN's had permanent failures, stop retrying");
1862 apnContext.setState(DctConstants.State.FAILED);
1863 mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
1865 apnContext.setDataConnection(null);
1866 apnContext.setDataConnectionAc(null);
1868 if (DBG) log("onDataSetupComplete: Not all permanent failures, retry");
1869 // check to see if retry should be overridden for this failure.
1870 int retryOverride = -1;
1871 if (ar.exception instanceof DataConnection.CallSetupException) {
1873 ((DataConnection.CallSetupException)ar.exception).getRetryOverride();
1875 if (retryOverride == RILConstants.MAX_INT) {
1876 if (DBG) log("No retry is suggested.");
1878 startDelayedRetry(cause, apnContext, retryOverride);
1882 if (DBG) log("onDataSetupComplete: Try next APN");
1883 apnContext.setState(DctConstants.State.SCANNING);
1884 // Wait a bit before trying the next APN, so that
1885 // we're not tying up the RIL command channel
1886 startAlarmForReconnect(APN_DELAY_MILLIS, apnContext);
1892 * Called when EVENT_DISCONNECT_DONE is received.
1895 protected void onDisconnectDone(int connId, AsyncResult ar) {
1896 ApnContext apnContext = null;
1898 if (ar.userObj instanceof ApnContext) {
1899 apnContext = (ApnContext) ar.userObj;
1901 loge("onDisconnectDone: Invalid ar in onDisconnectDone, ignore");
1905 if(DBG) log("onDisconnectDone: EVENT_DISCONNECT_DONE apnContext=" + apnContext);
1906 apnContext.setState(DctConstants.State.IDLE);
1908 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1910 // if all data connection are gone, check whether Airplane mode request was
1912 if (isDisconnected()) {
1913 if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
1914 // Radio will be turned off. No need to retry data setup
1915 apnContext.setApnSetting(null);
1916 apnContext.setDataConnection(null);
1917 apnContext.setDataConnectionAc(null);
1922 // If APN is still enabled, try to bring it back up automatically
1923 if (apnContext.isReady() && retryAfterDisconnected(apnContext.getReason())) {
1924 SystemProperties.set("gsm.defaultpdpcontext.active", "false"); // TODO - what the heck? This shoudld go
1925 // Wait a bit before trying the next APN, so that
1926 // we're not tying up the RIL command channel.
1927 // This also helps in any external dependency to turn off the context.
1928 startAlarmForReconnect(APN_DELAY_MILLIS, apnContext);
1930 apnContext.setApnSetting(null);
1931 apnContext.setDataConnection(null);
1932 apnContext.setDataConnectionAc(null);
1936 protected void onPollPdp() {
1937 if (getOverallState() == DctConstants.State.CONNECTED) {
1938 // only poll when connected
1939 mPhone.mCM.getDataCallList(this.obtainMessage(DctConstants.EVENT_DATA_STATE_CHANGED));
1940 sendMessageDelayed(obtainMessage(DctConstants.EVENT_POLL_PDP), POLL_PDP_MILLIS);
1945 protected void onVoiceCallStarted() {
1946 if (DBG) log("onVoiceCallStarted");
1947 if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
1948 if (DBG) log("onVoiceCallStarted stop polling");
1950 stopDataStallAlarm();
1951 notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
1956 protected void onVoiceCallEnded() {
1957 if (DBG) log("onVoiceCallEnded");
1958 if (isConnected()) {
1959 if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
1961 startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
1962 notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
1964 // clean slate after call end.
1968 // reset reconnect timer
1969 setupDataOnReadyApns(Phone.REASON_VOICE_CALL_ENDED);
1973 protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
1974 if (DBG) log("onCleanUpConnection");
1975 ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
1976 if (apnContext != null) {
1977 apnContext.setReason(reason);
1978 cleanUpConnection(tearDown, apnContext);
1982 protected boolean isConnected() {
1983 for (ApnContext apnContext : mApnContexts.values()) {
1984 if (apnContext.getState() ==DctConstants.State.CONNECTED) {
1985 // At least one context is connected, return true
1989 // There are not any contexts connected, return false
1994 public boolean isDisconnected() {
1995 for (ApnContext apnContext : mApnContexts.values()) {
1996 if (!apnContext.isDisconnected()) {
1997 // At least one context was not disconnected return false
2001 // All contexts were disconnected so return true
2006 protected void notifyDataConnection(String reason) {
2007 if (DBG) log("notifyDataConnection: reason=" + reason);
2008 for (ApnContext apnContext : mApnContexts.values()) {
2009 if (apnContext.isReady()) {
2010 if (DBG) log("notifyDataConnection: type:"+apnContext.getApnType());
2011 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
2012 apnContext.getApnType());
2015 notifyOffApnsOfAvailability(reason);
2019 * Based on the sim operator numeric, create a list for all possible
2020 * Data Connections and setup the preferredApn.
2022 private void createAllApnList() {
2023 mAllApns = new ArrayList<ApnSetting>();
2024 IccRecords r = mIccRecords.get();
2025 String operator = (r != null) ? r.getOperatorNumeric() : "";
2026 if (operator != null) {
2027 String selection = "numeric = '" + operator + "'";
2028 // query only enabled apn.
2029 // carrier_enabled : 1 means enabled apn, 0 disabled apn.
2030 selection += " and carrier_enabled = 1";
2031 if (DBG) log("createAllApnList: selection=" + selection);
2033 Cursor cursor = mPhone.getContext().getContentResolver().query(
2034 Telephony.Carriers.CONTENT_URI, null, selection, null, null);
2036 if (cursor != null) {
2037 if (cursor.getCount() > 0) {
2038 mAllApns = createApnList(cursor);
2044 if (mAllApns.isEmpty()) {
2045 if (DBG) log("createAllApnList: No APN found for carrier: " + operator);
2046 mPreferredApn = null;
2047 // TODO: What is the right behaviour?
2048 //notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN);
2050 mPreferredApn = getPreferredApn();
2051 if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
2052 mPreferredApn = null;
2053 setPreferredApn(-1);
2055 if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn);
2057 if (DBG) log("createAllApnList: X mAllApns=" + mAllApns);
2060 /** Return the id for a new data connection */
2061 private GsmDataConnection createDataConnection() {
2062 if (DBG) log("createDataConnection E");
2064 RetryManager rm = new RetryManager();
2065 int id = mUniqueIdGenerator.getAndIncrement();
2066 GsmDataConnection conn = GsmDataConnection.makeDataConnection(mPhone, id, rm, this);
2067 mDataConnections.put(id, conn);
2068 DataConnectionAc dcac = new DataConnectionAc(conn, LOG_TAG);
2069 int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler());
2070 if (status == AsyncChannel.STATUS_SUCCESSFUL) {
2071 mDataConnectionAsyncChannels.put(dcac.dataConnection.getDataConnectionId(), dcac);
2073 loge("createDataConnection: Could not connect to dcac.mDc=" + dcac.dataConnection +
2074 " status=" + status);
2077 // install reconnect intent filter for this data connection.
2078 IntentFilter filter = new IntentFilter();
2079 filter.addAction(INTENT_RECONNECT_ALARM + '.' + id);
2080 mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
2082 if (DBG) log("createDataConnection() X id=" + id + " dc=" + conn);
2086 private void configureRetry(DataConnection dc, boolean forDefault, int retryCount) {
2088 log("configureRetry: forDefault=" + forDefault + " retryCount=" + retryCount +
2091 if (dc == null) return;
2093 if (!dc.configureRetry(getReryConfig(forDefault))) {
2095 if (!dc.configureRetry(DEFAULT_DATA_RETRY_CONFIG)) {
2096 // Should never happen, log an error and default to a simple linear sequence.
2097 loge("configureRetry: Could not configure using " +
2098 "DEFAULT_DATA_RETRY_CONFIG=" + DEFAULT_DATA_RETRY_CONFIG);
2099 dc.configureRetry(20, 2000, 1000);
2102 if (!dc.configureRetry(SECONDARY_DATA_RETRY_CONFIG)) {
2103 // Should never happen, log an error and default to a simple sequence.
2104 loge("configureRetry: Could note configure using " +
2105 "SECONDARY_DATA_RETRY_CONFIG=" + SECONDARY_DATA_RETRY_CONFIG);
2106 dc.configureRetry("max_retries=3, 333, 333, 333");
2110 dc.setRetryCount(retryCount);
2113 private void destroyDataConnections() {
2114 if(mDataConnections != null) {
2115 if (DBG) log("destroyDataConnections: clear mDataConnectionList");
2116 mDataConnections.clear();
2118 if (DBG) log("destroyDataConnections: mDataConnecitonList is empty, ignore");
2123 * Build a list of APNs to be used to create PDP's.
2125 * @param requestedApnType
2126 * @return waitingApns list to be used to create PDP
2127 * error when waitingApns.isEmpty()
2129 private ArrayList<ApnSetting> buildWaitingApns(String requestedApnType) {
2130 ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
2132 if (requestedApnType.equals(PhoneConstants.APN_TYPE_DUN)) {
2133 ApnSetting dun = fetchDunApn();
2136 if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
2141 IccRecords r = mIccRecords.get();
2142 String operator = (r != null) ? r.getOperatorNumeric() : "";
2143 int radioTech = mPhone.getServiceState().getRilRadioTechnology();
2145 // This is a workaround for a bug (7305641) where we don't failover to other
2146 // suitable APNs if our preferred APN fails. On prepaid ATT sims we need to
2147 // failover to a provisioning APN, but once we've used their default data
2148 // connection we are locked to it for life. This change allows ATT devices
2149 // to say they don't want to use preferred at all.
2150 boolean usePreferred = true;
2152 usePreferred = ! mPhone.getContext().getResources().getBoolean(com.android.
2153 internal.R.bool.config_dontPreferApn);
2154 } catch (Resources.NotFoundException e) {
2155 usePreferred = true;
2158 if (usePreferred && canSetPreferApn && mPreferredApn != null &&
2159 mPreferredApn.canHandleType(requestedApnType)) {
2161 log("buildWaitingApns: Preferred APN:" + operator + ":"
2162 + mPreferredApn.numeric + ":" + mPreferredApn);
2164 if (mPreferredApn.numeric.equals(operator)) {
2165 if (mPreferredApn.bearer == 0 || mPreferredApn.bearer == radioTech) {
2166 apnList.add(mPreferredApn);
2167 if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
2170 if (DBG) log("buildWaitingApns: no preferred APN");
2171 setPreferredApn(-1);
2172 mPreferredApn = null;
2175 if (DBG) log("buildWaitingApns: no preferred APN");
2176 setPreferredApn(-1);
2177 mPreferredApn = null;
2180 if (mAllApns != null) {
2181 for (ApnSetting apn : mAllApns) {
2182 if (apn.canHandleType(requestedApnType)) {
2183 if (apn.bearer == 0 || apn.bearer == radioTech) {
2184 if (DBG) log("apn info : " +apn.toString());
2190 loge("mAllApns is empty!");
2192 if (DBG) log("buildWaitingApns: X apnList=" + apnList);
2196 private String apnListToString (ArrayList<ApnSetting> apns) {
2197 StringBuilder result = new StringBuilder();
2198 for (int i = 0, size = apns.size(); i < size; i++) {
2200 .append(apns.get(i).toString())
2203 return result.toString();
2206 private void startDelayedRetry(GsmDataConnection.FailCause cause,
2207 ApnContext apnContext, int retryOverride) {
2208 notifyNoData(cause, apnContext);
2209 reconnectAfterFail(cause, apnContext, retryOverride);
2212 private void setPreferredApn(int pos) {
2213 if (!canSetPreferApn) {
2214 log("setPreferredApn: X !canSEtPreferApn");
2218 log("setPreferredApn: delete");
2219 ContentResolver resolver = mPhone.getContext().getContentResolver();
2220 resolver.delete(PREFERAPN_NO_UPDATE_URI, null, null);
2223 log("setPreferredApn: insert");
2224 ContentValues values = new ContentValues();
2225 values.put(APN_ID, pos);
2226 resolver.insert(PREFERAPN_NO_UPDATE_URI, values);
2230 private ApnSetting getPreferredApn() {
2231 if (mAllApns.isEmpty()) {
2232 log("getPreferredApn: X not found mAllApns.isEmpty");
2236 Cursor cursor = mPhone.getContext().getContentResolver().query(
2237 PREFERAPN_NO_UPDATE_URI, new String[] { "_id", "name", "apn" },
2238 null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
2240 if (cursor != null) {
2241 canSetPreferApn = true;
2243 canSetPreferApn = false;
2246 if (canSetPreferApn && cursor.getCount() > 0) {
2248 cursor.moveToFirst();
2249 pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
2250 for(ApnSetting p:mAllApns) {
2251 if (p.id == pos && p.canHandleType(mRequestedApnType)) {
2252 log("getPreferredApn: X found apnSetting" + p);
2259 if (cursor != null) {
2263 log("getPreferredApn: X not found");
2268 public void handleMessage (Message msg) {
2269 if (DBG) log("handleMessage msg=" + msg);
2271 if (!mPhone.mIsTheCurrentActivePhone || mIsDisposed) {
2272 loge("handleMessage: Ignore GSM msgs since GSM phone is inactive");
2277 case DctConstants.EVENT_RECORDS_LOADED:
2281 case DctConstants.EVENT_DATA_CONNECTION_DETACHED:
2282 onDataConnectionDetached();
2285 case DctConstants.EVENT_DATA_CONNECTION_ATTACHED:
2286 onDataConnectionAttached();
2289 case DctConstants.EVENT_DATA_STATE_CHANGED:
2290 onDataStateChanged((AsyncResult) msg.obj);
2293 case DctConstants.EVENT_POLL_PDP:
2297 case DctConstants.EVENT_DO_RECOVERY:
2301 case DctConstants.EVENT_APN_CHANGED:
2305 case DctConstants.EVENT_PS_RESTRICT_ENABLED:
2307 * We don't need to explicitly to tear down the PDP context
2308 * when PS restricted is enabled. The base band will deactive
2309 * PDP context and notify us with PDP_CONTEXT_CHANGED.
2310 * But we should stop the network polling and prevent reset PDP.
2312 if (DBG) log("EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
2314 stopDataStallAlarm();
2315 mIsPsRestricted = true;
2318 case DctConstants.EVENT_PS_RESTRICT_DISABLED:
2320 * When PS restrict is removed, we need setup PDP connection if
2321 * PDP connection is down.
2323 if (DBG) log("EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
2324 mIsPsRestricted = false;
2325 if (isConnected()) {
2327 startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
2329 // TODO: Should all PDN states be checked to fail?
2330 if (mState ==DctConstants.State.FAILED) {
2331 cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED);
2332 resetAllRetryCounts();
2333 mReregisterOnReconnectFailure = false;
2335 trySetupData(Phone.REASON_PS_RESTRICT_ENABLED, PhoneConstants.APN_TYPE_DEFAULT);
2338 case DctConstants.EVENT_TRY_SETUP_DATA:
2339 if (msg.obj instanceof ApnContext) {
2340 onTrySetupData((ApnContext)msg.obj);
2341 } else if (msg.obj instanceof String) {
2342 onTrySetupData((String)msg.obj);
2344 loge("EVENT_TRY_SETUP request w/o apnContext or String");
2348 case DctConstants.EVENT_CLEAN_UP_CONNECTION:
2349 boolean tearDown = (msg.arg1 == 0) ? false : true;
2350 if (DBG) log("EVENT_CLEAN_UP_CONNECTION tearDown=" + tearDown);
2351 if (msg.obj instanceof ApnContext) {
2352 cleanUpConnection(tearDown, (ApnContext)msg.obj);
2354 loge("EVENT_CLEAN_UP_CONNECTION request w/o apn context");
2358 // handle the message in the super class DataConnectionTracker
2359 super.handleMessage(msg);
2364 protected int getApnProfileID(String apnType) {
2365 if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_IMS)) {
2366 return RILConstants.DATA_PROFILE_IMS;
2367 } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_FOTA)) {
2368 return RILConstants.DATA_PROFILE_FOTA;
2369 } else if (TextUtils.equals(apnType, PhoneConstants.APN_TYPE_CBS)) {
2370 return RILConstants.DATA_PROFILE_CBS;
2372 return RILConstants.DATA_PROFILE_DEFAULT;
2376 private int getCellLocationId() {
2378 CellLocation loc = mPhone.getCellLocation();
2381 if (loc instanceof GsmCellLocation) {
2382 cid = ((GsmCellLocation)loc).getCid();
2383 } else if (loc instanceof CdmaCellLocation) {
2384 cid = ((CdmaCellLocation)loc).getBaseStationId();
2391 protected void onUpdateIcc() {
2392 if (mUiccController == null ) {
2396 IccRecords newIccRecords = mUiccController.getIccRecords(UiccController.APP_FAM_3GPP);
2398 IccRecords r = mIccRecords.get();
2399 if (r != newIccRecords) {
2401 log("Removing stale icc objects.");
2402 r.unregisterForRecordsLoaded(this);
2403 mIccRecords.set(null);
2405 if (newIccRecords != null) {
2406 log("New records found");
2407 mIccRecords.set(newIccRecords);
2408 newIccRecords.registerForRecordsLoaded(
2409 this, DctConstants.EVENT_RECORDS_LOADED, null);
2415 protected void log(String s) {
2416 Rlog.d(LOG_TAG, "[GsmDCT] "+ s);
2420 protected void loge(String s) {
2421 Rlog.e(LOG_TAG, "[GsmDCT] " + s);
2425 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2426 pw.println("GsmDataConnectionTracker extends:");
2427 super.dump(fd, pw, args);
2428 pw.println(" mReregisterOnReconnectFailure=" + mReregisterOnReconnectFailure);
2429 pw.println(" canSetPreferApn=" + canSetPreferApn);
2430 pw.println(" mApnObserver=" + mApnObserver);
2431 pw.println(" getOverallState=" + getOverallState());
2432 pw.println(" mDataConnectionAsyncChannels=%s\n" + mDataConnectionAsyncChannels);