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.SharedPreferences;
26 import android.database.ContentObserver;
27 import android.database.Cursor;
28 import android.net.ConnectivityManager;
29 import android.net.ProxyProperties;
30 import android.net.TrafficStats;
31 import android.net.Uri;
32 import android.net.LinkCapabilities;
33 import android.net.LinkProperties;
34 import android.net.NetworkConfig;
35 import android.os.AsyncResult;
36 import android.os.Message;
37 import android.os.SystemClock;
38 import android.os.SystemProperties;
39 import android.provider.Settings;
40 import android.provider.Telephony;
41 import android.telephony.ServiceState;
42 import android.telephony.TelephonyManager;
43 import android.telephony.gsm.GsmCellLocation;
44 import android.text.TextUtils;
45 import android.util.EventLog;
46 import android.util.Log;
47 import android.preference.PreferenceManager;
49 import com.android.internal.R;
50 import com.android.internal.telephony.ApnContext;
51 import com.android.internal.telephony.ApnSetting;
52 import com.android.internal.telephony.DataCallState;
53 import com.android.internal.telephony.DataConnection;
54 import com.android.internal.telephony.DataConnectionTracker;
55 import com.android.internal.telephony.Phone;
56 import com.android.internal.telephony.PhoneBase;
57 import com.android.internal.telephony.RetryManager;
58 import com.android.internal.telephony.EventLogTags;
59 import com.android.internal.telephony.DataConnection.FailCause;
60 import com.android.internal.telephony.RILConstants;
62 import java.io.IOException;
63 import java.net.InetAddress;
64 import java.net.InetSocketAddress;
65 import java.net.UnknownHostException;
66 import java.util.ArrayList;
67 import java.util.concurrent.ConcurrentHashMap;
68 import java.util.Iterator;
70 import java.util.HashMap;
75 public final class GsmDataConnectionTracker extends DataConnectionTracker {
76 protected final String LOG_TAG = "GSM";
79 * Handles changes to the APN db.
81 private class ApnChangeObserver extends ContentObserver {
82 public ApnChangeObserver () {
83 super(mDataConnectionTracker);
87 public void onChange(boolean selfChange) {
88 sendMessage(obtainMessage(EVENT_APN_CHANGED));
92 //***** Instance Variables
94 private boolean mReregisterOnReconnectFailure = false;
95 private ContentResolver mResolver;
97 // Count of PDP reset attempts; reset when we see incoming,
98 // call reRegisterNetwork, or pingTest succeeds.
99 private int mPdpResetCount = 0;
101 /** Delay between APN attempts */
102 protected static final int APN_DELAY_MILLIS = 5000;
104 //useful for debugging
105 boolean mFailNextConnect = false;
109 private static final int POLL_PDP_MILLIS = 5 * 1000;
111 private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect";
112 private static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "type";
114 static final Uri PREFERAPN_URI = Uri.parse("content://telephony/carriers/preferapn");
115 static final String APN_ID = "apn_id";
116 private boolean canSetPreferApn = false;
119 protected void onActionIntentReconnectAlarm(Intent intent) {
120 log("GPRS reconnect alarm. Previous state was " + mState);
122 String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
123 String type = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE);
124 ApnContext apnContext = mApnContexts.get(type);
125 if (apnContext != null) {
126 apnContext.setReason(reason);
127 if (apnContext.getState() == State.FAILED) {
128 Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
129 msg.arg1 = 0; // tearDown is false
130 msg.obj = (ApnContext)apnContext;
133 sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext));
137 /** Watches for changes to the APN db. */
138 private ApnChangeObserver mApnObserver;
142 public GsmDataConnectionTracker(PhoneBase p) {
145 p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null);
146 p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
147 p.mSIMRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
148 p.mCM.registerForDataNetworkStateChanged (this, EVENT_DATA_STATE_CHANGED, null);
149 p.getCallTracker().registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null);
150 p.getCallTracker().registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null);
151 p.getServiceStateTracker().registerForDataConnectionAttached(this,
152 EVENT_DATA_CONNECTION_ATTACHED, null);
153 p.getServiceStateTracker().registerForDataConnectionDetached(this,
154 EVENT_DATA_CONNECTION_DETACHED, null);
155 p.getServiceStateTracker().registerForRoamingOn(this, EVENT_ROAMING_ON, null);
156 p.getServiceStateTracker().registerForRoamingOff(this, EVENT_ROAMING_OFF, null);
157 p.getServiceStateTracker().registerForPsRestrictedEnabled(this,
158 EVENT_PS_RESTRICT_ENABLED, null);
159 p.getServiceStateTracker().registerForPsRestrictedDisabled(this,
160 EVENT_PS_RESTRICT_DISABLED, null);
162 mDataConnectionTracker = this;
163 mResolver = mPhone.getContext().getContentResolver();
165 mApnObserver = new ApnChangeObserver();
166 p.getContext().getContentResolver().registerContentObserver(
167 Telephony.Carriers.CONTENT_URI, true, mApnObserver);
169 mApnContexts = new ConcurrentHashMap<String, ApnContext>();
170 initApnContextsAndDataConnection();
171 broadcastMessenger();
175 public void dispose() {
176 cleanUpAllConnections(false, null);
180 //Unregister for all events
181 mPhone.mCM.unregisterForAvailable(this);
182 mPhone.mCM.unregisterForOffOrNotAvailable(this);
183 mPhone.mSIMRecords.unregisterForRecordsLoaded(this);
184 mPhone.mCM.unregisterForDataNetworkStateChanged(this);
185 mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
186 mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
187 mPhone.getServiceStateTracker().unregisterForDataConnectionAttached(this);
188 mPhone.getServiceStateTracker().unregisterForDataConnectionDetached(this);
189 mPhone.getServiceStateTracker().unregisterForRoamingOn(this);
190 mPhone.getServiceStateTracker().unregisterForRoamingOff(this);
191 mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this);
192 mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this);
194 mPhone.getContext().getContentResolver().unregisterContentObserver(this.mApnObserver);
195 mApnContexts.clear();
197 destroyDataConnections();
201 public boolean isApnTypeActive(String type) {
202 ApnContext apnContext = mApnContexts.get(type);
203 if (apnContext == null) return false;
205 return (apnContext.getDataConnection() != null);
209 * The only circumstances under which we report that data connectivity is not
212 * <li>Data is disallowed (roaming, power state, voice call, etc).</li>
213 * <li>The current data state is {@code DISCONNECTED} for a reason other than
214 * having explicitly disabled connectivity. In other words, data is not available
215 * because the phone is out of coverage or some like reason.</li>
217 * @return {@code true} if data connectivity is possible, {@code false} otherwise.
218 * TODO - do per-apn notifications of availability using dependencyMet values.
221 protected boolean isDataPossible() {
222 boolean possible = (isDataAllowed()
223 && getAnyDataEnabled() && (getOverallState() == State.CONNECTED));
224 if (!possible && DBG && isDataAllowed()) {
225 log("Data not possible. No coverage: dataState = " + getOverallState());
231 protected void finalize() {
232 if(DBG) log("finalize");
236 protected String getActionIntentReconnectAlarm() {
237 return INTENT_RECONNECT_ALARM;
240 private ApnContext addApnContext(String type) {
241 ApnContext apnContext = new ApnContext(type, LOG_TAG);
242 apnContext.setDependencyMet(false);
243 mApnContexts.put(type, apnContext);
247 protected void initApnContextsAndDataConnection() {
248 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
249 boolean defaultEnabled = !sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false);
250 // Load device network attributes from resources
251 String[] networkConfigStrings = mPhone.getContext().getResources().getStringArray(
252 com.android.internal.R.array.networkAttributes);
253 for (String networkConfigString : networkConfigStrings) {
254 NetworkConfig networkConfig = new NetworkConfig(networkConfigString);
255 ApnContext apnContext = null;
257 switch (networkConfig.type) {
258 case ConnectivityManager.TYPE_MOBILE:
259 apnContext = addApnContext(Phone.APN_TYPE_DEFAULT);
260 apnContext.setEnabled(defaultEnabled);
262 case ConnectivityManager.TYPE_MOBILE_MMS:
263 apnContext = addApnContext(Phone.APN_TYPE_MMS);
265 case ConnectivityManager.TYPE_MOBILE_SUPL:
266 apnContext = addApnContext(Phone.APN_TYPE_SUPL);
268 case ConnectivityManager.TYPE_MOBILE_DUN:
269 apnContext = addApnContext(Phone.APN_TYPE_DUN);
271 case ConnectivityManager.TYPE_MOBILE_HIPRI:
272 apnContext = addApnContext(Phone.APN_TYPE_HIPRI);
274 case ConnectivityManager.TYPE_MOBILE_FOTA:
275 apnContext = addApnContext(Phone.APN_TYPE_FOTA);
277 case ConnectivityManager.TYPE_MOBILE_IMS:
278 apnContext = addApnContext(Phone.APN_TYPE_IMS);
280 case ConnectivityManager.TYPE_MOBILE_CBS:
281 apnContext = addApnContext(Phone.APN_TYPE_CBS);
284 // skip unknown types
287 if (apnContext != null) {
288 // set the prop, but also apply the newly set enabled and dependency values
289 onSetDependencyMet(apnContext.getApnType(), networkConfig.dependencyMet);
295 protected LinkProperties getLinkProperties(String apnType) {
296 ApnContext apnContext = mApnContexts.get(apnType);
297 if (apnContext != null && apnContext.getDataConnection() != null) {
298 if (DBG) log("get active pdp is not null, return link properites for " + apnType);
299 return apnContext.getDataConnection().getLinkProperties();
301 if (DBG) log("return new LinkProperties");
302 return new LinkProperties();
307 protected LinkCapabilities getLinkCapabilities(String apnType) {
308 ApnContext apnContext = mApnContexts.get(apnType);
309 if (apnContext!=null && apnContext.getDataConnection() != null) {
310 if (DBG) log("get active pdp is not null, return link Capabilities for " + apnType);
311 return apnContext.getDataConnection().getLinkCapabilities();
313 if (DBG) log("return new LinkCapabilities");
314 return new LinkCapabilities();
319 // Return all active apn types
320 public String[] getActiveApnTypes() {
321 if (DBG) log("get all active apn types");
322 ArrayList<String> result = new ArrayList<String>();
324 Iterator<ApnContext> it = mApnContexts.values().iterator();
325 while (it.hasNext()) {
326 ApnContext apnContext = it.next();
327 if (apnContext.isReady()) {
328 result.add(apnContext.getApnType());
332 return (String[])result.toArray(new String[0]);
337 * Return DEFAULT APN due to the limit of the interface
339 public synchronized String getActiveApnString() {
340 if (DBG) log( "get default active apn string");
341 ApnContext defaultApnContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT);
342 if (defaultApnContext != null && defaultApnContext.getApnSetting() != null) {
343 return defaultApnContext.getApnSetting().apn;
348 // Return active apn of specific apn type
349 public synchronized String getActiveApnString(String apnType) {
350 if (DBG) log( "get active apn string for type:" + apnType);
351 ApnContext apnContext = mApnContexts.get(apnType);
352 if (apnContext != null && apnContext.getApnSetting() != null) {
353 return apnContext.getApnSetting().apn;
359 protected void setState(State s) {
360 if (DBG) log("setState should not be used in GSM" + s);
363 // Return state of specific apn type
365 public synchronized State getState(String apnType) {
366 ApnContext apnContext = mApnContexts.get(apnType);
367 if (apnContext != null) {
368 return apnContext.getState();
373 // Return state of overall
374 public State getOverallState() {
375 boolean isConnecting = false;
376 Iterator<ApnContext> it = mApnContexts.values().iterator();
377 while (it.hasNext()) {
378 ApnContext apnContext = it.next();
379 if (apnContext.getState() == State.CONNECTED ||
380 apnContext.getState() == State.DISCONNECTING) {
381 if (DBG) log("overall state is CONNECTED");
382 return State.CONNECTED;
384 else if (apnContext.getState() == State.CONNECTING
385 || apnContext.getState() == State.INITING) {
390 if (DBG) log( "overall state is CONNECTING");
391 return State.CONNECTING;
393 if (DBG) log( "overall state is IDLE");
399 * Ensure that we are connected to an APN of the specified type.
401 * @param type the APN type
402 * @return Success is indicated by {@code Phone.APN_ALREADY_ACTIVE} or
403 * {@code Phone.APN_REQUEST_STARTED}. In the latter case, a
404 * broadcast will be sent by the ConnectivityManager when a
405 * connection to the APN has been established.
408 public synchronized int enableApnType(String apnType) {
409 if (DBG) log("calling enableApnType with type:" + apnType);
411 ApnContext apnContext = mApnContexts.get(apnType);
412 if (apnContext == null || !isApnTypeAvailable(apnType)) {
413 if (DBG) log("type not available");
414 return Phone.APN_TYPE_NOT_AVAILABLE;
417 // If already active, return
418 log("enableApnType(" + apnType + ")" + ", mState(" + apnContext.getState() + ")");
420 if (apnContext.getState() == State.INITING) {
421 if (DBG) log("return APN_REQUEST_STARTED");
422 return Phone.APN_REQUEST_STARTED;
424 else if (apnContext.getState() == State.CONNECTED) {
425 if (DBG) log("return APN_ALREADY_ACTIVE");
426 return Phone.APN_ALREADY_ACTIVE;
428 else if (apnContext.getState() == State.DISCONNECTING) {
429 if (DBG) log("requested APN while disconnecting");
430 apnContext.setPendingAction(ApnContext.PENDING_ACTION_RECONNECT);
431 return Phone.APN_REQUEST_STARTED;
434 if (DBG) log("new apn request for type " + apnType + " is to be handled");
435 setEnabled(apnTypeToId(apnType), true);
436 if (DBG) log("return APN_REQUEST_STARTED");
437 return Phone.APN_REQUEST_STARTED;
440 // A new APN has gone active and needs to send events to catch up with the
442 private void notifyApnIdUpToCurrent(String reason, ApnContext apnContext, String type) {
443 switch (apnContext.getState()) {
449 mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTING);
453 mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTING);
454 mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTED);
460 public synchronized int disableApnType(String type) {
461 if (DBG) log("calling disableApnType with type:" + type);
462 ApnContext apnContext = mApnContexts.get(type);
464 if (apnContext != null) {
465 apnContext.setPendingAction(ApnContext.PENDING_ACTION_APN_DISABLE);
467 if (apnContext.getState() != State.IDLE && apnContext.getState() != State.FAILED) {
468 Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
469 msg.arg1 = 1; // tearDown is true;
470 // TODO - don't set things on apnContext from public functions.
471 // Maybe pass reason as arg2?
472 apnContext.setReason(Phone.REASON_DATA_DISABLED);
473 msg.obj = apnContext;
475 if (DBG) log("return APN_REQUEST_STARTED");
476 return Phone.APN_REQUEST_STARTED;
478 if (DBG) log("return APN_ALREADY_INACTIVE");
479 return Phone.APN_ALREADY_INACTIVE;
484 log("no apn context was found, return APN_REQUEST_FAILED");
485 return Phone.APN_REQUEST_FAILED;
490 protected boolean isApnTypeAvailable(String type) {
491 if (type.equals(Phone.APN_TYPE_DUN)) {
492 return (fetchDunApn() != null);
495 if (mAllApns != null) {
496 for (ApnSetting apn : mAllApns) {
497 if (apn.canHandleType(type)) {
505 protected boolean isEnabled(String apnType) {
506 ApnContext apnContext = mApnContexts.get(apnType);
507 if (apnContext == null) return false;
508 if (apnContext.getState() == State.DISCONNECTING
509 && apnContext.getPendingAction() == ApnContext.PENDING_ACTION_APN_DISABLE) {
516 * Report on whether data connectivity is enabled for any APN.
517 * @return {@code false} if data connectivity has been explicitly disabled,
518 * {@code true} otherwise.
521 public synchronized boolean getAnyDataEnabled() {
522 if (!(mInternalDataEnabled && mDataEnabled)) return false;
523 for (ApnContext apnContext : mApnContexts.values()) {
524 // Make sure we dont have a context that going down
525 // and is explicitly disabled.
526 if (isDataAllowed(apnContext)) {
533 private boolean isDataAllowed(ApnContext apnContext) {
534 if(apnContext.getState() == State.DISCONNECTING
535 && apnContext.getPendingAction() == ApnContext.PENDING_ACTION_APN_DISABLE) {
538 return apnContext.isReady() && isDataAllowed();
541 //****** Called from ServiceStateTracker
543 * Invoked when ServiceStateTracker observes a transition from GPRS
546 protected void onDataConnectionDetached() {
548 * We presently believe it is unnecessary to tear down the PDP context
549 * when GPRS detaches, but we should stop the network polling.
552 notifyDataConnection(Phone.REASON_GPRS_DETACHED);
555 private void onDataConnectionAttached() {
556 if (getOverallState() == State.CONNECTED) {
558 notifyDataConnection(Phone.REASON_GPRS_ATTACHED);
560 // Only check for default APN state
561 ApnContext defaultApnContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT);
562 if (defaultApnContext != null) {
563 if (defaultApnContext.getState() == State.FAILED) {
564 cleanUpConnection(false, defaultApnContext);
565 defaultApnContext.getDataConnection().resetRetryCount();
567 trySetupData(Phone.REASON_GPRS_ATTACHED, Phone.APN_TYPE_DEFAULT);
573 protected boolean isDataAllowed() {
574 int gprsState = mPhone.getServiceStateTracker().getCurrentDataConnectionState();
575 boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
578 (gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) &&
579 mPhone.mSIMRecords.getRecordsLoaded() &&
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.mSIMRecords.getRecordsLoaded()) reason += " - SIM not loaded";
591 if (mPhone.getState() != Phone.State.IDLE) {
592 reason += " - PhoneState= " + mPhone.getState();
594 if (!mInternalDataEnabled) reason += " - mInternalDataEnabled= false";
595 if (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled()) {
596 reason += " - Roaming and data roaming not enabled";
598 if (mIsPsRestricted) reason += " - mIsPsRestricted= true";
599 if (!desiredPowerState) reason += " - desiredPowerState= false";
600 log("Data not allowed due to" + reason);
605 private boolean trySetupData(String reason, String type) {
607 log("***trySetupData for type:" + type +
608 " due to " + (reason == null ? "(unspecified)" : reason) +
609 " isPsRestricted=" + mIsPsRestricted);
613 type = Phone.APN_TYPE_DEFAULT;
616 ApnContext apnContext = mApnContexts.get(type);
618 if (apnContext == null ){
619 if (DBG) log("new apn context for type:" + type);
620 apnContext = new ApnContext(type, LOG_TAG);
621 mApnContexts.put(type, apnContext);
623 apnContext.setReason(reason);
625 return trySetupData(apnContext);
629 private boolean trySetupData(ApnContext apnContext) {
632 log("trySetupData for type:" + apnContext.getApnType() +
633 " due to " + apnContext.getReason());
634 log("[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted);
637 if (mPhone.getSimulatedRadioControl() != null) {
638 // Assume data is connected on the simulator
639 // FIXME this can be improved
640 apnContext.setState(State.CONNECTED);
641 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
643 log("(fix?) We're on the simulator; assuming data is connected");
647 boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
649 if ((apnContext.getState() == State.IDLE || apnContext.getState() == State.SCANNING) &&
650 isDataAllowed(apnContext) && getAnyDataEnabled()) {
652 if (apnContext.getState() == State.IDLE) {
653 ArrayList<ApnSetting> waitingApns = buildWaitingApns(apnContext.getApnType());
654 if (waitingApns.isEmpty()) {
655 if (DBG) log("No APN found");
656 notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN, apnContext);
657 notifyOffApnsOfAvailability(apnContext.getReason(), false);
660 apnContext.setWaitingApns(waitingApns);
661 log ("Create from mAllApns : " + apnListToString(mAllApns));
666 log ("Setup watingApns : " + apnListToString(apnContext.getWaitingApns()));
668 // apnContext.setReason(apnContext.getReason());
669 boolean retValue = setupData(apnContext);
670 notifyOffApnsOfAvailability(apnContext.getReason(), retValue);
673 // TODO: check the condition.
674 if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)
675 && (apnContext.getState() == State.IDLE
676 || apnContext.getState() == State.SCANNING))
677 mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
678 notifyOffApnsOfAvailability(apnContext.getReason(), false);
684 // Disabled apn's still need avail/unavail notificiations - send them out
685 protected void notifyOffApnsOfAvailability(String reason, boolean availability) {
686 if (mAvailability == availability) return;
687 mAvailability = availability;
689 for (ApnContext apnContext : mApnContexts.values()) {
690 if (!apnContext.isReady()) {
691 if (DBG) log("notify disconnected for type:" + apnContext.getApnType());
692 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
693 apnContext.getApnType(),
694 Phone.DataState.DISCONNECTED);
700 * If tearDown is true, this only tears down a CONNECTED session. Presently,
701 * there is no mechanism for abandoning an INITING/CONNECTING session,
702 * but would likely involve cancelling pending async requests or
703 * setting a flag or new state to ignore them when they came in
704 * @param tearDown true if the underlying GsmDataConnection should be
706 * @param reason reason for the clean up.
708 protected void cleanUpAllConnections(boolean tearDown, String reason) {
709 if (DBG) log("Clean up all connections due to " + reason);
711 Iterator<ApnContext> it = mApnContexts.values().iterator();
712 while (it.hasNext()) {
713 ApnContext apnContext = it.next();
714 apnContext.setReason(reason);
715 cleanUpConnection(tearDown, apnContext);
719 // TODO: Do we need mRequestedApnType?
720 mRequestedApnType = Phone.APN_TYPE_DEFAULT;
724 * Cleanup all connections.
726 * TODO: Cleanup only a specified connection passed as a parameter.
727 * Also, make sure when you clean up a conn, if it is last apply
728 * logic as though it is cleanupAllConnections
730 * @param tearDown true if the underlying DataConnection should be disconnected.
731 * @param reason for the clean up.
735 protected void onCleanUpAllConnections(String cause) {
736 cleanUpAllConnections(true, cause);
739 private void cleanUpConnection(boolean tearDown, ApnContext apnContext) {
741 if (apnContext == null) {
742 if (DBG) log("apn context is null");
746 if (DBG) log("Clean up connection due to " + apnContext.getReason());
748 // Clear the reconnect alarm, if set.
749 if (apnContext.getReconnectIntent() != null) {
751 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
752 am.cancel(apnContext.getReconnectIntent());
753 apnContext.setReconnectIntent(null);
756 if (apnContext.getState() == State.IDLE || apnContext.getState() == State.DISCONNECTING) {
757 if (DBG) log("state is in " + apnContext.getState());
761 if (apnContext.getState() == State.FAILED) {
762 if (DBG) log("state is in FAILED");
763 apnContext.setState(State.IDLE);
767 DataConnection conn = apnContext.getDataConnection();
769 apnContext.setState(State.DISCONNECTING);
771 Message msg = obtainMessage(EVENT_DISCONNECT_DONE, apnContext);
772 conn.disconnect(apnContext.getReason(), msg);
774 conn.resetSynchronously();
775 apnContext.setState(State.IDLE);
776 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
783 * @param types comma delimited list of APN types
784 * @return array of APN types
786 private String[] parseTypes(String types) {
788 // If unset, set to DEFAULT.
789 if (types == null || types.equals("")) {
790 result = new String[1];
791 result[0] = Phone.APN_TYPE_ALL;
793 result = types.split(",");
798 private ArrayList<ApnSetting> createApnList(Cursor cursor) {
799 ArrayList<ApnSetting> result = new ArrayList<ApnSetting>();
800 if (cursor.moveToFirst()) {
802 String[] types = parseTypes(
803 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
804 ApnSetting apn = new ApnSetting(
805 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
806 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
807 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
808 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
809 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY)),
810 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)),
811 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC)),
812 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY)),
813 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)),
814 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
815 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
816 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
818 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
819 cursor.getString(cursor.getColumnIndexOrThrow(
820 Telephony.Carriers.ROAMING_PROTOCOL)));
822 } while (cursor.moveToNext());
824 if (DBG) log("createApnList: X result=" + result);
828 private GsmDataConnection findFreeDataConnection() {
829 for (DataConnection dc : mDataConnections.values()) {
830 if (dc.isInactive()) {
831 log("found free GsmDataConnection");
832 return (GsmDataConnection) dc;
835 log("NO free GsmDataConnection");
839 protected GsmDataConnection findReadyDataConnection(ApnSetting apn) {
841 log("findReadyDataConnection for apn string <" +
842 (apn!=null?(apn.toString()):"null") +">");
843 for (DataConnection conn : mDataConnections.values()) {
844 GsmDataConnection dc = (GsmDataConnection) conn;
845 if (DBG) log("dc apn string <" +
846 (dc.getApn() != null ? (dc.getApn().toString()) : "null") + ">");
847 if (dc.getApn() != null && apn != null
848 && dc.getApn().toString().equals(apn.toString())) {
856 private boolean setupData(ApnContext apnContext) {
857 if (DBG) log("enter setupData!");
859 GsmDataConnection dc;
861 int profileId = getApnProfileID(apnContext.getApnType());
862 apn = apnContext.getNextWaitingApn();
864 if (DBG) log("setupData: return for no apn found!");
868 dc = findReadyDataConnection(apn);
871 if (DBG) log("setupData: No ready GsmDataConnection found!");
872 // TODO: When allocating you are mapping type to id. If more than 1 free,
873 // then could findFreeDataConnection get the wrong one??
874 dc = findFreeDataConnection();
878 dc = createDataConnection(apnContext);
882 if (DBG) log("setupData: No free GsmDataConnection found!");
886 apnContext.setApnSetting(apn);
887 apnContext.setDataConnection(dc);
888 dc.setProfileId( profileId );
889 dc.setActiveApnType(apnContext.getApnType());
891 Message msg = obtainMessage();
892 msg.what = EVENT_DATA_SETUP_COMPLETE;
893 msg.obj = apnContext;
895 if (DBG) log("dc connect!");
896 dc.connect(msg, apn);
898 apnContext.setState(State.INITING);
899 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
900 if (DBG) log("setupData: initing!");
904 private boolean dataCallStatesHasCID (ArrayList<DataCallState> states, int cid) {
905 for (int i = 0, s = states.size() ; i < s ; i++) {
906 if (states.get(i).cid == cid) return true;
911 private boolean dataCallStatesHasActiveCID (ArrayList<DataCallState> states, int cid) {
912 for (int i = 0, s = states.size() ; i < s ; i++) {
913 if ((states.get(i).cid == cid) && (states.get(i).active != 0)) {
922 * Handles changes to the APN database.
924 private void onApnChanged() {
925 // TODO: How to handle when multiple APNs are active?
928 ApnContext defaultApnContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT);
929 isConnected = (defaultApnContext.getState() != State.IDLE
930 && defaultApnContext.getState() != State.FAILED);
932 if (mPhone instanceof GSMPhone) {
933 // The "current" may no longer be valid. MMS depends on this to send properly. TBD
934 ((GSMPhone)mPhone).updateCurrentCarrierInProvider();
937 // TODO: It'd be nice to only do this if the changed entrie(s)
938 // match the current operator.
940 if (DBG) log("onApnChanged clean all connections");
941 cleanUpAllConnections(isConnected, Phone.REASON_APN_CHANGED);
943 // TODO: Won't work for multiple connections!!!!
944 defaultApnContext.getDataConnection().resetRetryCount();
945 defaultApnContext.setReason(Phone.REASON_APN_CHANGED);
946 trySetupData(defaultApnContext);
951 * @param explicitPoll if true, indicates that *we* polled for this
952 * update while state == CONNECTED rather than having it delivered
953 * via an unsolicited response (which could have happened at any
956 private void onDataStateChanged (AsyncResult ar, boolean explicitPoll) {
957 ArrayList<DataCallState> dataCallStates;
959 dataCallStates = (ArrayList<DataCallState>)(ar.result);
961 if (ar.exception != null) {
962 // This is probably "radio not available" or something
963 // of that sort. If so, the whole connection is going
964 // to come down soon anyway
968 Iterator<ApnContext> it = mApnContexts.values().iterator();
969 while (it.hasNext()) {
970 ApnContext apnContext = it.next();
971 onDataStateChanged(dataCallStates, explicitPoll, apnContext);
975 private void onDataStateChanged (ArrayList<DataCallState> dataCallStates,
976 boolean explicitPoll,
977 ApnContext apnContext) {
979 if (apnContext == null) {
984 if (apnContext.getState() == State.CONNECTED) {
985 // The way things are supposed to work, the PDP list
986 // should not contain the CID after it disconnects.
987 // However, the way things really work, sometimes the PDP
988 // context is still listed with active = false, which
989 // makes it hard to distinguish an activating context from
990 // an activated-and-then deactivated one.
991 if (!dataCallStatesHasCID(dataCallStates, apnContext.getDataConnection().getCid())) {
992 // It looks like the PDP context has deactivated.
993 // Tear everything down and try to reconnect.
995 Log.i(LOG_TAG, "PDP connection has dropped. Reconnecting");
997 // Add an event log when the network drops PDP
999 GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation());
1000 if (loc != null) cid = loc.getCid();
1001 EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid,
1002 TelephonyManager.getDefault().getNetworkType());
1004 cleanUpConnection(true, apnContext);
1006 } else if (!dataCallStatesHasActiveCID(dataCallStates,
1007 apnContext.getDataConnection().getCid())) {
1008 // Here, we only consider this authoritative if we asked for the
1009 // PDP list. If it was an unsolicited response, we poll again
1010 // to make sure everyone agrees on the initial state.
1012 if (!explicitPoll) {
1013 // We think it disconnected but aren't sure...poll from our side
1014 mPhone.mCM.getPDPContextList(
1015 this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
1017 Log.i(LOG_TAG, "PDP connection has dropped (active=false case). "
1020 // Log the network drop on the event log.
1022 GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation());
1023 if (loc != null) cid = loc.getCid();
1024 EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid,
1025 TelephonyManager.getDefault().getNetworkType());
1027 cleanUpConnection(true, apnContext);
1033 private void notifyDefaultData(ApnContext apnContext) {
1035 log("notifyDefaultData for type: " + apnContext.getApnType()
1036 + ", reason:" + apnContext.getReason());
1037 apnContext.setState(State.CONNECTED);
1038 // setState(State.CONNECTED);
1039 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1041 // reset reconnect timer
1042 apnContext.getDataConnection().resetRetryCount();
1045 // TODO: For multiple Active APNs not exactly sure how to do this.
1046 protected void gotoIdleAndNotifyDataConnection(String reason) {
1047 if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
1048 notifyDataConnection(reason);
1052 private void resetPollStats() {
1055 mSentSinceLastRecv = 0;
1056 mNetStatPollPeriod = POLL_NETSTAT_MILLIS;
1057 mNoRecvPollCount = 0;
1060 private void doRecovery() {
1061 if (getOverallState() == State.CONNECTED) {
1062 int maxPdpReset = Settings.Secure.getInt(mResolver,
1063 Settings.Secure.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT,
1064 DEFAULT_MAX_PDP_RESET_FAIL);
1065 if (mPdpResetCount < maxPdpReset) {
1067 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, mSentSinceLastRecv);
1068 cleanUpAllConnections(true, Phone.REASON_PDP_RESET);
1071 EventLog.writeEvent(EventLogTags.PDP_REREGISTER_NETWORK, mSentSinceLastRecv);
1072 mPhone.getServiceStateTracker().reRegisterNetwork(null);
1074 // TODO: Add increasingly drastic recovery steps, eg,
1075 // reset the radio, reset the device.
1080 protected void startNetStatPoll() {
1081 if (getOverallState() == State.CONNECTED && mNetStatPollEnabled == false) {
1082 log("[DataConnection] Start poll NetStat");
1084 mNetStatPollEnabled = true;
1090 protected void stopNetStatPoll() {
1091 mNetStatPollEnabled = false;
1092 removeCallbacks(mPollNetStat);
1093 log("[DataConnection] Stop poll NetStat");
1097 protected void restartRadio() {
1098 log("************TURN OFF RADIO**************");
1099 cleanUpAllConnections(true, Phone.REASON_RADIO_TURNED_OFF);
1100 mPhone.getServiceStateTracker().powerOffRadioSafely(this);
1101 /* Note: no need to call setRadioPower(true). Assuming the desired
1102 * radio power state is still ON (as tracked by ServiceStateTracker),
1103 * ServiceStateTracker will call setRadioPower when it receives the
1104 * RADIO_STATE_CHANGED notification for the power off. And if the
1105 * desired power state has changed in the interim, we don't want to
1106 * override it with an unconditional power on.
1109 int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0"));
1110 SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1));
1113 private Runnable mPollNetStat = new Runnable()
1117 long sent, received;
1118 long preTxPkts = -1, preRxPkts = -1;
1120 Activity newActivity;
1122 preTxPkts = mTxPkts;
1123 preRxPkts = mRxPkts;
1125 mTxPkts = TrafficStats.getMobileTxPackets();
1126 mRxPkts = TrafficStats.getMobileRxPackets();
1128 //log("rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts));
1130 if (mNetStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) {
1131 sent = mTxPkts - preTxPkts;
1132 received = mRxPkts - preRxPkts;
1134 if ( sent > 0 && received > 0 ) {
1135 mSentSinceLastRecv = 0;
1136 newActivity = Activity.DATAINANDOUT;
1138 } else if (sent > 0 && received == 0) {
1139 if (mPhone.getState() == Phone.State.IDLE) {
1140 mSentSinceLastRecv += sent;
1142 mSentSinceLastRecv = 0;
1144 newActivity = Activity.DATAOUT;
1145 } else if (sent == 0 && received > 0) {
1146 mSentSinceLastRecv = 0;
1147 newActivity = Activity.DATAIN;
1149 } else if (sent == 0 && received == 0) {
1150 newActivity = Activity.NONE;
1152 mSentSinceLastRecv = 0;
1153 newActivity = Activity.NONE;
1156 if (mActivity != newActivity && mIsScreenOn) {
1157 mActivity = newActivity;
1158 mPhone.notifyDataActivity();
1162 int watchdogTrigger = Settings.Secure.getInt(mResolver,
1163 Settings.Secure.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
1164 NUMBER_SENT_PACKETS_OF_HANG);
1166 if (mSentSinceLastRecv >= watchdogTrigger) {
1167 // we already have NUMBER_SENT_PACKETS sent without ack
1168 if (mNoRecvPollCount == 0) {
1169 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET_COUNTDOWN_TRIGGERED,
1170 mSentSinceLastRecv);
1173 int noRecvPollLimit = Settings.Secure.getInt(mResolver,
1174 Settings.Secure.PDP_WATCHDOG_ERROR_POLL_COUNT, NO_RECV_POLL_LIMIT);
1176 if (mNoRecvPollCount < noRecvPollLimit) {
1177 // It's possible the PDP context went down and we weren't notified.
1178 // Start polling the context list in an attempt to recover.
1179 if (DBG) log("no DATAIN in a while; polling PDP");
1180 mPhone.mCM.getDataCallList(obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
1184 // Slow down the poll interval to let things happen
1185 mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
1186 Settings.Secure.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS,
1187 POLL_NETSTAT_SLOW_MILLIS);
1189 if (DBG) log("Sent " + String.valueOf(mSentSinceLastRecv) +
1190 " pkts since last received start recovery process");
1192 sendMessage(obtainMessage(EVENT_START_RECOVERY));
1195 mNoRecvPollCount = 0;
1197 mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
1198 Settings.Secure.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS);
1200 mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
1201 Settings.Secure.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS,
1202 POLL_NETSTAT_SCREEN_OFF_MILLIS);
1206 if (mNetStatPollEnabled) {
1207 mDataConnectionTracker.postDelayed(this, mNetStatPollPeriod);
1213 * Returns true if the last fail cause is something that
1214 * seems like it deserves an error notification.
1215 * Transient errors are ignored
1217 private boolean shouldPostNotification(GsmDataConnection.FailCause cause) {
1218 return (cause != GsmDataConnection.FailCause.UNKNOWN);
1222 * Return true if data connection need to be setup after disconnected due to
1225 * @param reason the reason why data is disconnected
1226 * @return true if try setup data connection is need for this reason
1228 private boolean retryAfterDisconnected(String reason) {
1229 boolean retry = true;
1231 if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) {
1237 private void reconnectAfterFail(FailCause lastFailCauseCode, ApnContext apnContext) {
1238 if (apnContext == null) {
1239 Log.d(LOG_TAG, "It is impossible");
1242 if (apnContext.getState() == State.FAILED) {
1243 if (!apnContext.getDataConnection().isRetryNeeded()) {
1244 if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)){
1245 // if no more retries on a secondary APN attempt, tell the world and revert.
1246 notifyDataConnection(Phone.REASON_APN_FAILED);
1249 if (mReregisterOnReconnectFailure) {
1250 // We've re-registerd once now just retry forever.
1251 apnContext.getDataConnection().retryForeverUsingLastTimeout();
1253 // Try to Re-register to the network.
1254 log("PDP activate failed, Reregistering to the network");
1255 mReregisterOnReconnectFailure = true;
1256 mPhone.getServiceStateTracker().reRegisterNetwork(null);
1257 apnContext.getDataConnection().resetRetryCount();
1262 int nextReconnectDelay = apnContext.getDataConnection().getRetryTimer();
1263 log("PDP activate failed. Scheduling next attempt for "
1264 + (nextReconnectDelay / 1000) + "s");
1267 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
1268 Intent intent = new Intent(INTENT_RECONNECT_ALARM);
1269 intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason());
1270 // Should put an extra of apn type?
1271 intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, apnContext.getApnType());
1272 apnContext.setReconnectIntent(PendingIntent.getBroadcast (
1273 mPhone.getContext(), 0, intent, 0));
1274 am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1275 SystemClock.elapsedRealtime() + nextReconnectDelay,
1276 apnContext.getReconnectIntent());
1278 apnContext.getDataConnection().increaseRetryCount();
1280 if (!shouldPostNotification(lastFailCauseCode)) {
1281 Log.d(LOG_TAG, "NOT Posting GPRS Unavailable notification "
1282 + "-- likely transient error");
1284 notifyNoData(lastFailCauseCode, apnContext);
1289 private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode,
1290 ApnContext apnContext) {
1291 if (DBG) log( "notifyNoData for type:" + apnContext.getApnType());
1292 apnContext.setState(State.FAILED);
1293 if (lastFailCauseCode.isPermanentFail()
1294 && (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT))) {
1295 mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
1299 private void onRecordsLoaded() {
1301 for (ApnContext apnContext : mApnContexts.values()) {
1302 if (apnContext.isReady()) {
1303 apnContext.setReason(Phone.REASON_SIM_LOADED);
1304 if (apnContext.getState() == State.FAILED) {
1306 log("onRecordsLoaded clean connection for " + apnContext.getApnType());
1308 cleanUpConnection(false, apnContext);
1310 sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext));
1316 protected void onSetDependencyMet(String apnType, boolean met) {
1317 ApnContext apnContext = mApnContexts.get(apnType);
1318 if (apnContext == null) {
1319 log("ApnContext not found in onSetDependencyMet(" + apnType + ", " + met + ")");
1322 applyNewState(apnContext, apnContext.isEnabled(), met);
1325 private void applyNewState(ApnContext apnContext, boolean enabled, boolean met) {
1326 boolean cleanup = false;
1327 boolean trySetup = false;
1329 log("applyNewState(" + apnContext.getApnType() + ", " + enabled +
1330 "(" + apnContext.isEnabled() + "), " + met + "(" +
1331 apnContext.getDependencyMet() +"))");
1333 if (apnContext.isReady()) {
1334 if (enabled && met) return;
1336 apnContext.setReason(Phone.REASON_DATA_DISABLED);
1338 apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET);
1342 if (enabled && met) {
1343 if (apnContext.isEnabled()) {
1344 apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_MET);
1346 apnContext.setReason(Phone.REASON_DATA_ENABLED);
1348 DataConnection conn = checkForConnectionForApnContext(apnContext);
1350 if (apnContext.getState() == State.FAILED) {
1351 apnContext.setState(State.IDLE);
1355 // TODO send notifications
1357 log("Found existing connection for " + apnContext.getApnType() +
1360 apnContext.setDataConnection(conn);
1364 apnContext.setEnabled(enabled);
1365 apnContext.setDependencyMet(met);
1366 if (cleanup) cleanUpConnection(true, apnContext);
1367 if (trySetup) trySetupData(apnContext);
1370 private DataConnection checkForConnectionForApnContext(ApnContext apnContext) {
1371 // Loop through all apnContexts looking for one with a conn that satisfies this apnType
1372 String apnType = apnContext.getApnType();
1373 for (ApnContext c : mApnContexts.values()) {
1374 DataConnection conn = c.getDataConnection();
1376 ApnSetting apnSetting = c.getApnSetting();
1377 if (apnSetting != null && apnSetting.canHandleType(apnType)) return conn;
1384 protected void onEnableApn(int apnId, int enabled) {
1385 ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
1386 if (apnContext == null) {
1387 log("ApnContext not found in onEnableApn(" + apnId + ", " + enabled + ")");
1390 // TODO change our retry manager to use the appropriate numbers for the new APN
1391 log("onEnableApn with ApnContext E");
1392 applyNewState(apnContext, enabled == ENABLED, apnContext.getDependencyMet());
1396 // TODO: We shouldnt need this.
1397 protected boolean onTrySetupData(String reason) {
1398 return trySetupData(reason, Phone.APN_TYPE_DEFAULT);
1401 protected boolean onTrySetupData(ApnContext apnContext) {
1402 return trySetupData(apnContext);
1406 // TODO: Need to understand if more than DEFAULT is impacted?
1407 protected void onRoamingOff() {
1408 trySetupData(Phone.REASON_ROAMING_OFF, Phone.APN_TYPE_DEFAULT);
1412 // TODO: Need to understand if more than DEFAULT is impacted?
1413 protected void onRoamingOn() {
1414 if (getDataOnRoamingEnabled()) {
1415 trySetupData(Phone.REASON_ROAMING_ON, Phone.APN_TYPE_DEFAULT);
1417 if (DBG) log("Tear down data connection on roaming.");
1418 cleanUpAllConnections(true, Phone.REASON_ROAMING_ON);
1423 protected void onRadioAvailable() {
1424 if (mPhone.getSimulatedRadioControl() != null) {
1425 // Assume data is connected on the simulator
1426 // FIXME this can be improved
1427 // setState(State.CONNECTED);
1428 notifyDataConnection(null);
1430 log("We're on the simulator; assuming data is connected");
1433 if (getOverallState() != State.IDLE) {
1434 cleanUpConnection(true, null);
1439 protected void onRadioOffOrNotAvailable() {
1440 // Make sure our reconnect delay starts at the initial value
1441 // next time the radio comes on
1443 for (DataConnection dc : mDataConnections.values()) {
1444 dc.resetRetryCount();
1446 mReregisterOnReconnectFailure = false;
1448 if (mPhone.getSimulatedRadioControl() != null) {
1449 // Assume data is connected on the simulator
1450 // FIXME this can be improved
1451 log("We're on the simulator; assuming radio off is meaningless");
1453 if (DBG) log("Radio is off and clean up all connection");
1454 cleanUpAllConnections(false, Phone.REASON_RADIO_TURNED_OFF);
1459 protected void onDataSetupComplete(AsyncResult ar) {
1461 ApnContext apnContext = null;
1463 if(ar.userObj instanceof ApnContext){
1464 apnContext = (ApnContext)ar.userObj;
1467 if (ar.exception == null) {
1468 // Everything is setup
1469 // TODO: We should clear LinkProperties/Capabilities when torn down or disconnected
1471 log(String.format("onDataSetupComplete: success apn=%s",
1472 apnContext.getWaitingApns().get(0).apn));
1474 mLinkProperties = getLinkProperties(apnContext.getDataConnection());
1475 mLinkCapabilities = getLinkCapabilities(apnContext.getDataConnection());
1477 ApnSetting apn = apnContext.getApnSetting();
1478 if (apn.proxy != null && apn.proxy.length() != 0) {
1480 ProxyProperties proxy = new ProxyProperties(apn.proxy,
1481 Integer.parseInt(apn.port), null);
1482 mLinkProperties.setHttpProxy(proxy);
1483 } catch (NumberFormatException e) {
1484 loge("NumberFormatException making ProxyProperties (" + apn.port +
1489 // everything is setup
1490 if(TextUtils.equals(apnContext.getApnType(),Phone.APN_TYPE_DEFAULT)) {
1491 SystemProperties.set("gsm.defaultpdpcontext.active", "true");
1492 if (canSetPreferApn && mPreferredApn == null) {
1493 log("PREFERED APN is null");
1494 mPreferredApn = apnContext.getApnSetting();
1495 if (mPreferredApn != null) {
1496 setPreferredApn(mPreferredApn.id);
1500 SystemProperties.set("gsm.defaultpdpcontext.active", "false");
1502 notifyDefaultData(apnContext);
1504 // TODO: For simultaneous PDP support, we need to build another
1505 // trigger another TRY_SETUP_DATA for the next APN type. (Note
1506 // that the existing connection may service that type, in which
1507 // case we should try the next type, etc.
1508 // I dont believe for simultaneous PDP you need to trigger. Each
1509 // Connection should be independent and they can be setup simultaneously
1510 // So, dont have to wait till one is finished.
1512 GsmDataConnection.FailCause cause;
1513 cause = (GsmDataConnection.FailCause) (ar.result);
1517 apnString = apnContext.getWaitingApns().get(0).apn;
1518 } catch (Exception e) {
1519 apnString = "<unknown>";
1521 log(String.format("onDataSetupComplete: error apn=%s cause=%s", apnString, cause));
1523 if (cause.isEventLoggable()) {
1524 // Log this failure to the Event Logs.
1525 GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation());
1526 EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL,
1527 cause.ordinal(), loc != null ? loc.getCid() : -1,
1528 TelephonyManager.getDefault().getNetworkType());
1531 // Count permanent failures and remove the APN we just tried
1532 if (cause.isPermanentFail()) apnContext.decWaitingApnsPermFailCount();
1534 apnContext.removeNextWaitingApn();
1536 log(String.format("onDataSetupComplete: WaitingApns.size=%d" +
1537 " WaitingApnsPermFailureCountDown=%d",
1538 apnContext.getWaitingApns().size(),
1539 apnContext.getWaitingApnsPermFailCount()));
1542 // See if there are more APN's to try
1543 if (apnContext.getWaitingApns().isEmpty()) {
1544 if (apnContext.getWaitingApnsPermFailCount() == 0) {
1546 log("onDataSetupComplete: All APN's had permanent failures, stop retrying");
1548 apnContext.setState(State.FAILED);
1549 notifyDataConnection(Phone.REASON_APN_FAILED);
1551 if (DBG) log("onDataSetupComplete: Not all permanent failures, retry");
1552 startDelayedRetry(cause, apnContext);
1555 if (DBG) log("onDataSetupComplete: Try next APN");
1556 apnContext.setState(State.SCANNING);
1557 // Wait a bit before trying the next APN, so that
1558 // we're not tying up the RIL command channel
1559 sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext),
1566 * Called when EVENT_DISCONNECT_DONE is received.
1569 protected void onDisconnectDone(int connId, AsyncResult ar) {
1570 ApnContext apnContext = null;
1572 if(DBG) log("EVENT_DISCONNECT_DONE connId=" + connId);
1573 if (ar.userObj instanceof ApnContext) {
1574 apnContext = (ApnContext) ar.userObj;
1577 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1579 apnContext.setState(State.IDLE);
1580 apnContext.setApnSetting(null);
1582 // if all data connection are gone, check whether Airplane mode request was
1584 if (!isConnected()) {
1585 if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
1586 // Radio will be turned off. No need to retry data setup
1591 // Check if APN disabled.
1592 if (apnContext.getPendingAction() == ApnContext.PENDING_ACTION_APN_DISABLE) {
1593 apnContext.setEnabled(false);
1594 apnContext.setPendingAction(ApnContext.PENDING_ACTION_NONE);
1597 if (TextUtils.equals(apnContext.getApnType(), Phone.APN_TYPE_DEFAULT)
1598 && retryAfterDisconnected(apnContext.getReason())) {
1599 SystemProperties.set("gsm.defaultpdpcontext.active", "false");
1600 trySetupData(apnContext);
1602 else if (apnContext.getPendingAction() == ApnContext.PENDING_ACTION_RECONNECT)
1604 apnContext.setPendingAction(ApnContext.PENDING_ACTION_NONE);
1605 trySetupData(apnContext);
1609 protected void onPollPdp() {
1610 if (getOverallState() == State.CONNECTED) {
1611 // only poll when connected
1612 mPhone.mCM.getPDPContextList(this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
1613 sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS);
1618 protected void onVoiceCallStarted() {
1619 if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
1621 notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
1626 protected void onVoiceCallEnded() {
1627 if (isConnected()) {
1628 if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
1630 notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
1632 // clean slate after call end.
1636 // reset reconnect timer
1637 ApnContext defaultApnContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT);
1638 if (defaultApnContext != null) {
1639 defaultApnContext.getDataConnection().resetRetryCount();
1640 mReregisterOnReconnectFailure = false;
1641 // in case data setup was attempted when we were on a voice call
1642 trySetupData(Phone.REASON_VOICE_CALL_ENDED, Phone.APN_TYPE_DEFAULT);
1648 protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
1649 if (DBG) log("onCleanUpConnection");
1650 ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
1651 cleanUpConnection(tearDown, apnContext);
1654 protected boolean isConnected() {
1655 Iterator<ApnContext> it = mApnContexts.values().iterator();
1656 while (it.hasNext()) {
1657 ApnContext apnContext = it.next();
1658 if (apnContext.getState() == State.CONNECTED) {
1666 protected void notifyDataConnection(String reason) {
1667 if (DBG) log("notify all enabled connection for:" + reason);
1668 Iterator<ApnContext> it = mApnContexts.values().iterator();
1669 while (it.hasNext()) {
1670 ApnContext apnContext = it.next();
1671 if (apnContext.isReady()) {
1672 if (DBG) log("notify for type:"+apnContext.getApnType());
1673 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
1674 apnContext.getApnType());
1677 notifyDataAvailability(reason);
1681 * Based on the sim operator numeric, create a list for all possible
1682 * Data Connections and setup the preferredApn.
1684 private void createAllApnList() {
1685 mAllApns = new ArrayList<ApnSetting>();
1686 String operator = mPhone.mSIMRecords.getSIMOperatorNumeric();
1687 if (operator != null) {
1688 String selection = "numeric = '" + operator + "'";
1689 if (DBG) log("createAllApnList: selection=" + selection);
1691 Cursor cursor = mPhone.getContext().getContentResolver().query(
1692 Telephony.Carriers.CONTENT_URI, null, selection, null, null);
1694 if (cursor != null) {
1695 if (cursor.getCount() > 0) {
1696 mAllApns = createApnList(cursor);
1702 if (mAllApns.isEmpty()) {
1703 if (DBG) log("createAllApnList: No APN found for carrier: " + operator);
1704 mPreferredApn = null;
1705 // TODO: What is the right behaviour?
1706 //notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN);
1708 mPreferredApn = getPreferredApn();
1709 if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
1710 mPreferredApn = null;
1711 setPreferredApn(-1);
1713 if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn);
1715 if (DBG) log("createAllApnList: X mAllApns=" + mAllApns);
1718 /** Return the id for a new data connection */
1719 private GsmDataConnection createDataConnection(ApnContext apnContext) {
1720 String apnType = apnContext.getApnType();
1721 log("createDataConnection(" + apnType + ") E");
1722 RetryManager rm = new RetryManager();
1724 if (apnType.equals(Phone.APN_TYPE_DEFAULT)) {
1725 if (!rm.configure(SystemProperties.get("ro.gsm.data_retry_config"))) {
1726 if (!rm.configure(DEFAULT_DATA_RETRY_CONFIG)) {
1727 // Should never happen, log an error and default to a simple linear sequence.
1728 log("Could not configure using DEFAULT_DATA_RETRY_CONFIG="
1729 + DEFAULT_DATA_RETRY_CONFIG);
1730 rm.configure(20, 2000, 1000);
1734 if (!rm.configure(SystemProperties.get("ro.gsm.2nd_data_retry_config"))) {
1735 if (!rm.configure(SECONDARY_DATA_RETRY_CONFIG)) {
1736 // Should never happen, log an error and default to a simple sequence.
1737 log("Could note configure using SECONDARY_DATA_RETRY_CONFIG="
1738 + SECONDARY_DATA_RETRY_CONFIG);
1739 rm.configure("max_retries=3, 333, 333, 333");
1744 int id = mUniqueIdGenerator.getAndIncrement();
1745 GsmDataConnection conn = GsmDataConnection.makeDataConnection(mPhone, id, rm);
1746 conn.resetRetryCount();
1747 mDataConnections.put(id, conn);
1748 apnContext.setDataConnection(conn);
1750 log("createDataConnection(" + apnType + ") X id=" + id);
1754 private void destroyDataConnections() {
1755 if(mDataConnections != null) {
1756 log("destroyDataConnectionList clear mDataConnectionList");
1757 mDataConnections.clear();
1759 log("destroyDataConnectionList mDataConnecitonList is empty, ignore");
1764 * Build a list of APNs to be used to create PDP's.
1766 * @param requestedApnType
1767 * @return waitingApns list to be used to create PDP
1768 * error when waitingApns.isEmpty()
1770 private ArrayList<ApnSetting> buildWaitingApns(String requestedApnType) {
1771 ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
1773 if (requestedApnType.equals(Phone.APN_TYPE_DUN)) {
1774 ApnSetting dun = fetchDunApn();
1775 if (dun != null) apnList.add(dun);
1776 if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
1780 String operator = mPhone.mSIMRecords.getSIMOperatorNumeric();
1781 if (requestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
1782 if (canSetPreferApn && mPreferredApn != null) {
1783 log("Preferred APN:" + operator + ":"
1784 + mPreferredApn.numeric + ":" + mPreferredApn);
1785 if (mPreferredApn.numeric.equals(operator)) {
1786 apnList.add(mPreferredApn);
1787 if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
1790 if (DBG) log("buildWaitingApns: no preferred APN");
1791 setPreferredApn(-1);
1792 mPreferredApn = null;
1796 if (mAllApns != null) {
1797 for (ApnSetting apn : mAllApns) {
1798 if (apn.canHandleType(requestedApnType)) {
1803 loge("mAllApns is empty!");
1805 if (DBG) log("buildWaitingApns: X apnList=" + apnList);
1809 private String apnListToString (ArrayList<ApnSetting> apns) {
1810 StringBuilder result = new StringBuilder();
1811 for (int i = 0, size = apns.size(); i < size; i++) {
1813 .append(apns.get(i).toString())
1816 return result.toString();
1819 private void startDelayedRetry(GsmDataConnection.FailCause cause, ApnContext apnContext) {
1820 notifyNoData(cause, apnContext);
1821 reconnectAfterFail(cause, apnContext);
1824 private void setPreferredApn(int pos) {
1825 if (!canSetPreferApn) {
1829 ContentResolver resolver = mPhone.getContext().getContentResolver();
1830 resolver.delete(PREFERAPN_URI, null, null);
1833 ContentValues values = new ContentValues();
1834 values.put(APN_ID, pos);
1835 resolver.insert(PREFERAPN_URI, values);
1839 private ApnSetting getPreferredApn() {
1840 if (mAllApns.isEmpty()) {
1844 Cursor cursor = mPhone.getContext().getContentResolver().query(
1845 PREFERAPN_URI, new String[] { "_id", "name", "apn" },
1846 null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
1848 if (cursor != null) {
1849 canSetPreferApn = true;
1851 canSetPreferApn = false;
1854 if (canSetPreferApn && cursor.getCount() > 0) {
1856 cursor.moveToFirst();
1857 pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
1858 for(ApnSetting p:mAllApns) {
1859 if (p.id == pos && p.canHandleType(mRequestedApnType)) {
1866 if (cursor != null) {
1874 public void handleMessage (Message msg) {
1875 if (DBG) log("GSMDataConnTrack handleMessage "+msg);
1877 if (!mPhone.mIsTheCurrentActivePhone || mIsDisposed) {
1878 log("Ignore GSM msgs since GSM phone is inactive");
1883 case EVENT_RECORDS_LOADED:
1887 case EVENT_DATA_CONNECTION_DETACHED:
1888 onDataConnectionDetached();
1891 case EVENT_DATA_CONNECTION_ATTACHED:
1892 onDataConnectionAttached();
1895 case EVENT_DATA_STATE_CHANGED:
1896 onDataStateChanged((AsyncResult) msg.obj, false);
1899 case EVENT_GET_PDP_LIST_COMPLETE:
1900 onDataStateChanged((AsyncResult) msg.obj, true);
1903 case EVENT_POLL_PDP:
1907 case EVENT_START_NETSTAT_POLL:
1911 case EVENT_START_RECOVERY:
1915 case EVENT_APN_CHANGED:
1919 case EVENT_PS_RESTRICT_ENABLED:
1921 * We don't need to explicitly to tear down the PDP context
1922 * when PS restricted is enabled. The base band will deactive
1923 * PDP context and notify us with PDP_CONTEXT_CHANGED.
1924 * But we should stop the network polling and prevent reset PDP.
1926 log("[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
1928 mIsPsRestricted = true;
1931 case EVENT_PS_RESTRICT_DISABLED:
1933 * When PS restrict is removed, we need setup PDP connection if
1934 * PDP connection is down.
1936 log("[DSAC DEB] " + "EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
1937 mIsPsRestricted = false;
1938 if (isConnected()) {
1941 // TODO: Should all PDN states be checked to fail?
1942 if (mState == State.FAILED) {
1943 cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED);
1944 resetAllRetryCounts();
1945 mReregisterOnReconnectFailure = false;
1947 trySetupData(Phone.REASON_PS_RESTRICT_ENABLED, Phone.APN_TYPE_DEFAULT);
1950 case EVENT_TRY_SETUP_DATA:
1951 if (msg.obj instanceof ApnContext) {
1952 onTrySetupData((ApnContext)msg.obj);
1954 if (msg.obj instanceof String) {
1955 onTrySetupData((String)msg.obj);
1960 case EVENT_CLEAN_UP_CONNECTION:
1961 boolean tearDown = (msg.arg1 == 0) ? false : true;
1962 if (msg.obj instanceof ApnContext) {
1963 cleanUpConnection(tearDown, (ApnContext)msg.obj);
1965 loge("[GsmDataConnectionTracker] connectpion cleanup request w/o apn context");
1969 // handle the message in the super class DataConnectionTracker
1970 super.handleMessage(msg);
1975 protected int getApnProfileID(String apnType) {
1976 if (TextUtils.equals(apnType, Phone.APN_TYPE_IMS)) {
1977 return RILConstants.DATA_PROFILE_IMS;
1978 } else if (TextUtils.equals(apnType, Phone.APN_TYPE_FOTA)) {
1979 return RILConstants.DATA_PROFILE_FOTA;
1980 } else if (TextUtils.equals(apnType, Phone.APN_TYPE_CBS)) {
1981 return RILConstants.DATA_PROFILE_CBS;
1983 return RILConstants.DATA_PROFILE_DEFAULT;
1988 public boolean isAnyActiveDataConnections() {
1989 return isConnected();
1993 protected void log(String s) {
1994 Log.d(LOG_TAG, "[GsmDCT] " + s);
1998 protected void loge(String s) {
1999 Log.e(LOG_TAG, "[GsmDCT] " + s);