OSDN Git Service

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