OSDN Git Service

Fix getActiveApnTypes for GsmDCT.
[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.SharedPreferences;
26 import android.database.ContentObserver;
27 import android.database.Cursor;
28 import android.net.ConnectivityManager;
29 import android.net.ProxyProperties;
30 import android.net.TrafficStats;
31 import android.net.Uri;
32 import android.net.LinkCapabilities;
33 import android.net.LinkProperties;
34 import android.net.NetworkConfig;
35 import android.os.AsyncResult;
36 import android.os.Message;
37 import android.os.SystemClock;
38 import android.os.SystemProperties;
39 import android.provider.Settings;
40 import android.provider.Telephony;
41 import android.telephony.ServiceState;
42 import android.telephony.TelephonyManager;
43 import android.telephony.gsm.GsmCellLocation;
44 import android.text.TextUtils;
45 import android.util.EventLog;
46 import android.util.Log;
47 import android.preference.PreferenceManager;
48
49 import com.android.internal.R;
50 import com.android.internal.telephony.ApnContext;
51 import com.android.internal.telephony.ApnSetting;
52 import com.android.internal.telephony.DataCallState;
53 import com.android.internal.telephony.DataConnection;
54 import com.android.internal.telephony.DataConnectionTracker;
55 import com.android.internal.telephony.Phone;
56 import com.android.internal.telephony.PhoneBase;
57 import com.android.internal.telephony.RetryManager;
58 import com.android.internal.telephony.EventLogTags;
59 import com.android.internal.telephony.DataConnection.FailCause;
60 import com.android.internal.telephony.RILConstants;
61
62 import java.io.IOException;
63 import java.net.InetAddress;
64 import java.net.InetSocketAddress;
65 import java.net.UnknownHostException;
66 import java.util.ArrayList;
67 import java.util.concurrent.ConcurrentHashMap;
68 import java.util.Iterator;
69 import java.util.Map;
70 import java.util.HashMap;
71
72 /**
73  * {@hide}
74  */
75 public final class GsmDataConnectionTracker extends DataConnectionTracker {
76     protected final String LOG_TAG = "GSM";
77
78     /**
79      * Handles changes to the APN db.
80      */
81     private class ApnChangeObserver extends ContentObserver {
82         public ApnChangeObserver () {
83             super(mDataConnectionTracker);
84         }
85
86         @Override
87         public void onChange(boolean selfChange) {
88             sendMessage(obtainMessage(EVENT_APN_CHANGED));
89         }
90     }
91
92     //***** Instance Variables
93
94     private boolean mReregisterOnReconnectFailure = false;
95     private ContentResolver mResolver;
96
97     // Count of PDP reset attempts; reset when we see incoming,
98     // call reRegisterNetwork, or pingTest succeeds.
99     private int mPdpResetCount = 0;
100
101     /** Delay between APN attempts */
102     protected static final int APN_DELAY_MILLIS = 5000;
103
104     //useful for debugging
105     boolean mFailNextConnect = false;
106
107     //***** Constants
108
109     private static final int POLL_PDP_MILLIS = 5 * 1000;
110
111     private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect";
112     private static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "type";
113
114     static final Uri PREFERAPN_URI = Uri.parse("content://telephony/carriers/preferapn");
115     static final String APN_ID = "apn_id";
116     private boolean canSetPreferApn = false;
117
118     @Override
119     protected void onActionIntentReconnectAlarm(Intent intent) {
120         log("GPRS reconnect alarm. Previous state was " + mState);
121
122         String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
123         String type = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE);
124         ApnContext apnContext = mApnContexts.get(type);
125         if (apnContext != null) {
126             apnContext.setReason(reason);
127             if (apnContext.getState() == State.FAILED) {
128                 Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
129                 msg.arg1 = 0; // tearDown is false
130                 msg.obj = (ApnContext)apnContext;
131                 sendMessage(msg);
132             }
133             sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext));
134         }
135     }
136
137     /** Watches for changes to the APN db. */
138     private ApnChangeObserver mApnObserver;
139
140     //***** Constructor
141
142     public GsmDataConnectionTracker(PhoneBase p) {
143         super(p);
144
145         p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null);
146         p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
147         p.mSIMRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
148         p.mCM.registerForDataNetworkStateChanged (this, EVENT_DATA_STATE_CHANGED, null);
149         p.getCallTracker().registerForVoiceCallEnded (this, EVENT_VOICE_CALL_ENDED, null);
150         p.getCallTracker().registerForVoiceCallStarted (this, EVENT_VOICE_CALL_STARTED, null);
151         p.getServiceStateTracker().registerForDataConnectionAttached(this,
152                 EVENT_DATA_CONNECTION_ATTACHED, null);
153         p.getServiceStateTracker().registerForDataConnectionDetached(this,
154                 EVENT_DATA_CONNECTION_DETACHED, null);
155         p.getServiceStateTracker().registerForRoamingOn(this, EVENT_ROAMING_ON, null);
156         p.getServiceStateTracker().registerForRoamingOff(this, EVENT_ROAMING_OFF, null);
157         p.getServiceStateTracker().registerForPsRestrictedEnabled(this,
158                 EVENT_PS_RESTRICT_ENABLED, null);
159         p.getServiceStateTracker().registerForPsRestrictedDisabled(this,
160                 EVENT_PS_RESTRICT_DISABLED, null);
161
162         mDataConnectionTracker = this;
163         mResolver = mPhone.getContext().getContentResolver();
164
165         mApnObserver = new ApnChangeObserver();
166         p.getContext().getContentResolver().registerContentObserver(
167                 Telephony.Carriers.CONTENT_URI, true, mApnObserver);
168
169         mApnContexts = new ConcurrentHashMap<String, ApnContext>();
170         initApnContextsAndDataConnection();
171         broadcastMessenger();
172     }
173
174     @Override
175     public void dispose() {
176         cleanUpAllConnections(false, null);
177
178         super.dispose();
179
180         //Unregister for all events
181         mPhone.mCM.unregisterForAvailable(this);
182         mPhone.mCM.unregisterForOffOrNotAvailable(this);
183         mPhone.mSIMRecords.unregisterForRecordsLoaded(this);
184         mPhone.mCM.unregisterForDataNetworkStateChanged(this);
185         mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
186         mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
187         mPhone.getServiceStateTracker().unregisterForDataConnectionAttached(this);
188         mPhone.getServiceStateTracker().unregisterForDataConnectionDetached(this);
189         mPhone.getServiceStateTracker().unregisterForRoamingOn(this);
190         mPhone.getServiceStateTracker().unregisterForRoamingOff(this);
191         mPhone.getServiceStateTracker().unregisterForPsRestrictedEnabled(this);
192         mPhone.getServiceStateTracker().unregisterForPsRestrictedDisabled(this);
193
194         mPhone.getContext().getContentResolver().unregisterContentObserver(this.mApnObserver);
195         mApnContexts.clear();
196
197         destroyDataConnections();
198     }
199
200     @Override
201     public boolean isApnTypeActive(String type) {
202         ApnContext apnContext = mApnContexts.get(type);
203         if (apnContext == null) return false;
204
205         return (apnContext.getDataConnection() != null);
206     }
207
208     /**
209      * The only circumstances under which we report that data connectivity is not
210      * possible are
211      * <ul>
212      * <li>Data is disallowed (roaming, power state, voice call, etc).</li>
213      * <li>The current data state is {@code DISCONNECTED} for a reason other than
214      * having explicitly disabled connectivity. In other words, data is not available
215      * because the phone is out of coverage or some like reason.</li>
216      * </ul>
217      * @return {@code true} if data connectivity is possible, {@code false} otherwise.
218      * TODO - do per-apn notifications of availability using dependencyMet values.
219      */
220     @Override
221     protected boolean isDataPossible() {
222         boolean possible = (isDataAllowed()
223                 && getAnyDataEnabled() && (getOverallState() == State.CONNECTED));
224         if (!possible && DBG && isDataAllowed()) {
225             log("Data not possible.  No coverage: dataState = " + getOverallState());
226         }
227         return possible;
228     }
229
230     @Override
231     protected void finalize() {
232         if(DBG) log("finalize");
233     }
234
235     @Override
236     protected String getActionIntentReconnectAlarm() {
237         return INTENT_RECONNECT_ALARM;
238     }
239
240     private ApnContext addApnContext(String type) {
241         ApnContext apnContext = new ApnContext(type, LOG_TAG);
242         apnContext.setDependencyMet(false);
243         mApnContexts.put(type, apnContext);
244         return apnContext;
245     }
246
247     protected void initApnContextsAndDataConnection() {
248         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
249         boolean defaultEnabled = !sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false);
250         // Load device network attributes from resources
251         String[] networkConfigStrings = mPhone.getContext().getResources().getStringArray(
252                 com.android.internal.R.array.networkAttributes);
253         for (String networkConfigString : networkConfigStrings) {
254             NetworkConfig networkConfig = new NetworkConfig(networkConfigString);
255             ApnContext apnContext = null;
256
257             switch (networkConfig.type) {
258             case ConnectivityManager.TYPE_MOBILE:
259                 apnContext = addApnContext(Phone.APN_TYPE_DEFAULT);
260                 apnContext.setEnabled(defaultEnabled);
261                 break;
262             case ConnectivityManager.TYPE_MOBILE_MMS:
263                 apnContext = addApnContext(Phone.APN_TYPE_MMS);
264                 break;
265             case ConnectivityManager.TYPE_MOBILE_SUPL:
266                 apnContext = addApnContext(Phone.APN_TYPE_SUPL);
267                 break;
268             case ConnectivityManager.TYPE_MOBILE_DUN:
269                 apnContext = addApnContext(Phone.APN_TYPE_DUN);
270                 break;
271             case ConnectivityManager.TYPE_MOBILE_HIPRI:
272                 apnContext = addApnContext(Phone.APN_TYPE_HIPRI);
273                 break;
274             case ConnectivityManager.TYPE_MOBILE_FOTA:
275                 apnContext = addApnContext(Phone.APN_TYPE_FOTA);
276                 break;
277             case ConnectivityManager.TYPE_MOBILE_IMS:
278                 apnContext = addApnContext(Phone.APN_TYPE_IMS);
279                 break;
280             case ConnectivityManager.TYPE_MOBILE_CBS:
281                 apnContext = addApnContext(Phone.APN_TYPE_CBS);
282                 break;
283             default:
284                 // skip unknown types
285                 continue;
286             }
287             if (apnContext != null) {
288                 // set the prop, but also apply the newly set enabled and dependency values
289                 onSetDependencyMet(apnContext.getApnType(), networkConfig.dependencyMet);
290             }
291         }
292     }
293
294     @Override
295     protected LinkProperties getLinkProperties(String apnType) {
296         ApnContext apnContext = mApnContexts.get(apnType);
297         if (apnContext != null && apnContext.getDataConnection() != null) {
298              if (DBG) log("get active pdp is not null, return link properites for " + apnType);
299              return apnContext.getDataConnection().getLinkProperties();
300         } else {
301             if (DBG) log("return new LinkProperties");
302             return new LinkProperties();
303         }
304     }
305
306     @Override
307     protected LinkCapabilities getLinkCapabilities(String apnType) {
308         ApnContext apnContext = mApnContexts.get(apnType);
309         if (apnContext!=null && apnContext.getDataConnection() != null) {
310              if (DBG) log("get active pdp is not null, return link Capabilities for " + apnType);
311              return apnContext.getDataConnection().getLinkCapabilities();
312         } else {
313             if (DBG) log("return new LinkCapabilities");
314             return new LinkCapabilities();
315         }
316     }
317
318     @Override
319     // Return all active apn types
320     public String[] getActiveApnTypes() {
321         if (DBG) log("get all active apn types");
322         ArrayList<String> result = new ArrayList<String>();
323
324         Iterator<ApnContext> it = mApnContexts.values().iterator();
325         while (it.hasNext()) {
326             ApnContext apnContext = it.next();
327             if (apnContext.isReady()) {
328                 result.add(apnContext.getApnType());
329             }
330         }
331
332         return (String[])result.toArray(new String[0]);
333     }
334
335     @Override
336     /**
337      * Return DEFAULT APN due to the limit of the interface
338      */
339     public synchronized String getActiveApnString() {
340         if (DBG) log( "get default active apn string");
341         ApnContext defaultApnContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT);
342         if (defaultApnContext != null && defaultApnContext.getApnSetting() != null) {
343             return defaultApnContext.getApnSetting().apn;
344         }
345         return null;
346     }
347
348     // Return active apn of specific apn type
349     public synchronized String getActiveApnString(String apnType) {
350         if (DBG) log( "get active apn string for type:" + apnType);
351         ApnContext apnContext = mApnContexts.get(apnType);
352         if (apnContext != null && apnContext.getApnSetting() != null) {
353             return apnContext.getApnSetting().apn;
354         }
355         return null;
356     }
357
358     @Override
359     protected void setState(State s) {
360         if (DBG) log("setState should not be used in GSM" + s);
361     }
362
363     // Return state of specific apn type
364     @Override
365     public synchronized State getState(String apnType) {
366         ApnContext apnContext = mApnContexts.get(apnType);
367         if (apnContext != null) {
368             return apnContext.getState();
369         }
370         return State.FAILED;
371     }
372
373     // Return state of overall
374     public State getOverallState() {
375         boolean isConnecting = false;
376         Iterator<ApnContext> it = mApnContexts.values().iterator();
377         while (it.hasNext()) {
378             ApnContext apnContext = it.next();
379             if (apnContext.getState() == State.CONNECTED ||
380                     apnContext.getState() == State.DISCONNECTING) {
381                 if (DBG) log("overall state is CONNECTED");
382                 return State.CONNECTED;
383             }
384             else if (apnContext.getState() == State.CONNECTING
385                     || apnContext.getState() == State.INITING) {
386                 isConnecting = true;
387             }
388         }
389         if (isConnecting) {
390             if (DBG) log( "overall state is CONNECTING");
391             return State.CONNECTING;
392         } else {
393             if (DBG) log( "overall state is IDLE");
394             return State.IDLE;
395         }
396     }
397
398     /**
399      * Ensure that we are connected to an APN of the specified type.
400      *
401      * @param type the APN type
402      * @return Success is indicated by {@code Phone.APN_ALREADY_ACTIVE} or
403      *         {@code Phone.APN_REQUEST_STARTED}. In the latter case, a
404      *         broadcast will be sent by the ConnectivityManager when a
405      *         connection to the APN has been established.
406      */
407     @Override
408     public synchronized int enableApnType(String apnType) {
409         if (DBG) log("calling enableApnType with type:" + apnType);
410
411         ApnContext apnContext = mApnContexts.get(apnType);
412         if (apnContext == null || !isApnTypeAvailable(apnType)) {
413             if (DBG) log("type not available");
414             return Phone.APN_TYPE_NOT_AVAILABLE;
415         }
416
417         // If already active, return
418         log("enableApnType(" + apnType + ")" + ", mState(" + apnContext.getState() + ")");
419
420         if (apnContext.getState() == State.INITING) {
421             if (DBG) log("return APN_REQUEST_STARTED");
422             return Phone.APN_REQUEST_STARTED;
423         }
424         else if (apnContext.getState() == State.CONNECTED) {
425             if (DBG) log("return APN_ALREADY_ACTIVE");
426             return Phone.APN_ALREADY_ACTIVE;
427         }
428         else if (apnContext.getState() == State.DISCONNECTING) {
429             if (DBG) log("requested APN while disconnecting");
430             apnContext.setPendingAction(ApnContext.PENDING_ACTION_RECONNECT);
431             return Phone.APN_REQUEST_STARTED;
432         }
433
434         if (DBG) log("new apn request for type " + apnType + " is to be handled");
435         setEnabled(apnTypeToId(apnType), true);
436         if (DBG) log("return APN_REQUEST_STARTED");
437         return Phone.APN_REQUEST_STARTED;
438     }
439
440     // A new APN has gone active and needs to send events to catch up with the
441     // current condition
442     private void notifyApnIdUpToCurrent(String reason, ApnContext apnContext, String type) {
443         switch (apnContext.getState()) {
444             case IDLE:
445             case INITING:
446                 break;
447             case CONNECTING:
448             case SCANNING:
449                 mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTING);
450                 break;
451             case CONNECTED:
452             case DISCONNECTING:
453                 mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTING);
454                 mPhone.notifyDataConnection(reason, type, Phone.DataState.CONNECTED);
455                 break;
456         }
457     }
458
459     @Override
460     public synchronized int disableApnType(String type) {
461         if (DBG) log("calling disableApnType with type:" + type);
462         ApnContext apnContext = mApnContexts.get(type);
463
464         if (apnContext != null) {
465             apnContext.setPendingAction(ApnContext.PENDING_ACTION_APN_DISABLE);
466
467             if (apnContext.getState() != State.IDLE && apnContext.getState() != State.FAILED) {
468                 Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
469                 msg.arg1 = 1; // tearDown is true;
470                 // TODO - don't set things on apnContext from public functions.
471                 // Maybe pass reason as arg2?
472                 apnContext.setReason(Phone.REASON_DATA_DISABLED);
473                 msg.obj = apnContext;
474                 sendMessage(msg);
475                 if (DBG) log("return APN_REQUEST_STARTED");
476                 return Phone.APN_REQUEST_STARTED;
477             } else {
478                 if (DBG) log("return APN_ALREADY_INACTIVE");
479                 return Phone.APN_ALREADY_INACTIVE;
480             }
481
482         } else {
483             if (DBG)
484                 log("no apn context was found, return APN_REQUEST_FAILED");
485             return Phone.APN_REQUEST_FAILED;
486         }
487     }
488
489     @Override
490     protected boolean isApnTypeAvailable(String type) {
491         if (type.equals(Phone.APN_TYPE_DUN)) {
492             return (fetchDunApn() != null);
493         }
494
495         if (mAllApns != null) {
496             for (ApnSetting apn : mAllApns) {
497                 if (apn.canHandleType(type)) {
498                     return true;
499                 }
500             }
501         }
502         return false;
503     }
504
505     protected boolean isEnabled(String apnType) {
506         ApnContext apnContext = mApnContexts.get(apnType);
507         if (apnContext == null) return false;
508         if (apnContext.getState() == State.DISCONNECTING
509                 && apnContext.getPendingAction() == ApnContext.PENDING_ACTION_APN_DISABLE) {
510             return false;
511         }
512         return true;
513     }
514
515     /**
516      * Report on whether data connectivity is enabled for any APN.
517      * @return {@code false} if data connectivity has been explicitly disabled,
518      * {@code true} otherwise.
519      */
520     @Override
521     public synchronized boolean getAnyDataEnabled() {
522         if (!(mInternalDataEnabled && mDataEnabled)) return false;
523         for (ApnContext apnContext : mApnContexts.values()) {
524             // Make sure we dont have a context that going down
525             // and is explicitly disabled.
526             if (isDataAllowed(apnContext)) {
527                 return true;
528             }
529         }
530         return false;
531     }
532
533     private boolean isDataAllowed(ApnContext apnContext) {
534         if(apnContext.getState() == State.DISCONNECTING
535                 && apnContext.getPendingAction() == ApnContext.PENDING_ACTION_APN_DISABLE) {
536             return false;
537         }
538         return apnContext.isReady() && isDataAllowed();
539     }
540
541     //****** Called from ServiceStateTracker
542     /**
543      * Invoked when ServiceStateTracker observes a transition from GPRS
544      * attach to detach.
545      */
546     protected void onDataConnectionDetached() {
547         /*
548          * We presently believe it is unnecessary to tear down the PDP context
549          * when GPRS detaches, but we should stop the network polling.
550          */
551         stopNetStatPoll();
552         notifyDataConnection(Phone.REASON_GPRS_DETACHED);
553     }
554
555     private void onDataConnectionAttached() {
556         if (getOverallState() == State.CONNECTED) {
557             startNetStatPoll();
558             notifyDataConnection(Phone.REASON_GPRS_ATTACHED);
559         } else {
560             // Only check for default APN state
561             ApnContext defaultApnContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT);
562             if (defaultApnContext != null) {
563                 if (defaultApnContext.getState() == State.FAILED) {
564                     cleanUpConnection(false, defaultApnContext);
565                     defaultApnContext.getDataConnection().resetRetryCount();
566                 }
567                 trySetupData(Phone.REASON_GPRS_ATTACHED, Phone.APN_TYPE_DEFAULT);
568             }
569         }
570     }
571
572     @Override
573     protected boolean isDataAllowed() {
574         int gprsState = mPhone.getServiceStateTracker().getCurrentDataConnectionState();
575         boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
576
577         boolean allowed =
578                     (gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) &&
579                     mPhone.mSIMRecords.getRecordsLoaded() &&
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.mSIMRecords.getRecordsLoaded()) reason += " - SIM not loaded";
591             if (mPhone.getState() != Phone.State.IDLE) {
592                 reason += " - PhoneState= " + mPhone.getState();
593             }
594             if (!mInternalDataEnabled) reason += " - mInternalDataEnabled= false";
595             if (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled()) {
596                 reason += " - Roaming and data roaming not enabled";
597             }
598             if (mIsPsRestricted) reason += " - mIsPsRestricted= true";
599             if (!desiredPowerState) reason += " - desiredPowerState= false";
600             log("Data not allowed due to" + reason);
601         }
602         return allowed;
603     }
604
605     private boolean trySetupData(String reason, String type) {
606         if (DBG) {
607             log("***trySetupData for type:" + type +
608                     " due to " + (reason == null ? "(unspecified)" : reason) +
609                     " isPsRestricted=" + mIsPsRestricted);
610         }
611
612         if (type == null) {
613             type = Phone.APN_TYPE_DEFAULT;
614         }
615
616         ApnContext apnContext = mApnContexts.get(type);
617
618         if (apnContext == null ){
619             if (DBG) log("new apn context for type:" + type);
620             apnContext = new ApnContext(type, LOG_TAG);
621             mApnContexts.put(type, apnContext);
622         }
623         apnContext.setReason(reason);
624
625         return trySetupData(apnContext);
626
627     }
628
629     private boolean trySetupData(ApnContext apnContext) {
630
631         if (DBG) {
632             log("trySetupData for type:" + apnContext.getApnType() +
633                     " due to " + apnContext.getReason());
634             log("[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted);
635         }
636
637         if (mPhone.getSimulatedRadioControl() != null) {
638             // Assume data is connected on the simulator
639             // FIXME  this can be improved
640             apnContext.setState(State.CONNECTED);
641             mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
642
643             log("(fix?) We're on the simulator; assuming data is connected");
644             return true;
645         }
646
647         boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
648
649         if ((apnContext.getState() == State.IDLE || apnContext.getState() == State.SCANNING) &&
650                 isDataAllowed(apnContext) && getAnyDataEnabled()) {
651
652             if (apnContext.getState() == State.IDLE) {
653                 ArrayList<ApnSetting> waitingApns = buildWaitingApns(apnContext.getApnType());
654                 if (waitingApns.isEmpty()) {
655                     if (DBG) log("No APN found");
656                     notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN, apnContext);
657                     notifyOffApnsOfAvailability(apnContext.getReason(), false);
658                     return false;
659                 } else {
660                     apnContext.setWaitingApns(waitingApns);
661                     log ("Create from mAllApns : " + apnListToString(mAllApns));
662                 }
663             }
664
665             if (DBG) {
666                 log ("Setup watingApns : " + apnListToString(apnContext.getWaitingApns()));
667             }
668             // apnContext.setReason(apnContext.getReason());
669             boolean retValue = setupData(apnContext);
670             notifyOffApnsOfAvailability(apnContext.getReason(), retValue);
671             return retValue;
672         } else {
673             // TODO: check the condition.
674             if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)
675                 && (apnContext.getState() == State.IDLE
676                     || apnContext.getState() == State.SCANNING))
677                 mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
678             notifyOffApnsOfAvailability(apnContext.getReason(), false);
679             return false;
680         }
681     }
682
683     @Override
684     // Disabled apn's still need avail/unavail notificiations - send them out
685     protected void notifyOffApnsOfAvailability(String reason, boolean availability) {
686         if (mAvailability == availability) return;
687         mAvailability = availability;
688
689         for (ApnContext apnContext : mApnContexts.values()) {
690             if (!apnContext.isReady()) {
691                 if (DBG) log("notify disconnected for type:" + apnContext.getApnType());
692                 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
693                                             apnContext.getApnType(),
694                                             Phone.DataState.DISCONNECTED);
695             }
696         }
697     }
698
699     /**
700      * If tearDown is true, this only tears down a CONNECTED session. Presently,
701      * there is no mechanism for abandoning an INITING/CONNECTING session,
702      * but would likely involve cancelling pending async requests or
703      * setting a flag or new state to ignore them when they came in
704      * @param tearDown true if the underlying GsmDataConnection should be
705      * disconnected.
706      * @param reason reason for the clean up.
707      */
708     protected void cleanUpAllConnections(boolean tearDown, String reason) {
709         if (DBG) log("Clean up all connections due to " + reason);
710
711         Iterator<ApnContext> it = mApnContexts.values().iterator();
712         while (it.hasNext()) {
713             ApnContext apnContext = it.next();
714                 apnContext.setReason(reason);
715             cleanUpConnection(tearDown, apnContext);
716         }
717
718         stopNetStatPoll();
719         // TODO: Do we need mRequestedApnType?
720         mRequestedApnType = Phone.APN_TYPE_DEFAULT;
721     }
722
723     /**
724      * Cleanup all connections.
725      *
726      * TODO: Cleanup only a specified connection passed as a parameter.
727      *       Also, make sure when you clean up a conn, if it is last apply
728      *       logic as though it is cleanupAllConnections
729      *
730      * @param tearDown true if the underlying DataConnection should be disconnected.
731      * @param reason for the clean up.
732      */
733
734     @Override
735     protected void onCleanUpAllConnections(String cause) {
736         cleanUpAllConnections(true, cause);
737     }
738
739     private void cleanUpConnection(boolean tearDown, ApnContext apnContext) {
740
741         if (apnContext == null) {
742             if (DBG) log("apn context is null");
743             return;
744         }
745
746         if (DBG) log("Clean up connection due to " + apnContext.getReason());
747
748         // Clear the reconnect alarm, if set.
749         if (apnContext.getReconnectIntent() != null) {
750             AlarmManager am =
751                 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
752             am.cancel(apnContext.getReconnectIntent());
753             apnContext.setReconnectIntent(null);
754         }
755
756         if (apnContext.getState() == State.IDLE || apnContext.getState() == State.DISCONNECTING) {
757             if (DBG) log("state is in " + apnContext.getState());
758             return;
759         }
760
761         if (apnContext.getState() == State.FAILED) {
762             if (DBG) log("state is in FAILED");
763             apnContext.setState(State.IDLE);
764             return;
765         }
766
767         DataConnection conn = apnContext.getDataConnection();
768         if (conn != null) {
769             apnContext.setState(State.DISCONNECTING);
770             if (tearDown) {
771                 Message msg = obtainMessage(EVENT_DISCONNECT_DONE, apnContext);
772                 conn.disconnect(apnContext.getReason(), msg);
773             } else {
774                 conn.resetSynchronously();
775                 apnContext.setState(State.IDLE);
776                 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
777             }
778         }
779     }
780
781
782     /**
783      * @param types comma delimited list of APN types
784      * @return array of APN types
785      */
786     private String[] parseTypes(String types) {
787         String[] result;
788         // If unset, set to DEFAULT.
789         if (types == null || types.equals("")) {
790             result = new String[1];
791             result[0] = Phone.APN_TYPE_ALL;
792         } else {
793             result = types.split(",");
794         }
795         return result;
796     }
797
798     private ArrayList<ApnSetting> createApnList(Cursor cursor) {
799         ArrayList<ApnSetting> result = new ArrayList<ApnSetting>();
800         if (cursor.moveToFirst()) {
801             do {
802                 String[] types = parseTypes(
803                         cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
804                 ApnSetting apn = new ApnSetting(
805                         cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
806                         cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)),
807                         cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)),
808                         cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)),
809                         cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY)),
810                         cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)),
811                         cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC)),
812                         cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY)),
813                         cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)),
814                         cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
815                         cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
816                         cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
817                         types,
818                         cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
819                         cursor.getString(cursor.getColumnIndexOrThrow(
820                                 Telephony.Carriers.ROAMING_PROTOCOL)));
821                 result.add(apn);
822             } while (cursor.moveToNext());
823         }
824         if (DBG) log("createApnList: X result=" + result);
825         return result;
826     }
827
828     private GsmDataConnection findFreeDataConnection() {
829         for (DataConnection dc : mDataConnections.values()) {
830             if (dc.isInactive()) {
831                 log("found free GsmDataConnection");
832                 return (GsmDataConnection) dc;
833             }
834         }
835         log("NO free GsmDataConnection");
836         return null;
837     }
838
839     protected GsmDataConnection findReadyDataConnection(ApnSetting apn) {
840         if (DBG)
841             log("findReadyDataConnection for apn string <" +
842                 (apn!=null?(apn.toString()):"null") +">");
843         for (DataConnection conn : mDataConnections.values()) {
844             GsmDataConnection dc = (GsmDataConnection) conn;
845             if (DBG) log("dc apn string <" +
846                          (dc.getApn() != null ? (dc.getApn().toString()) : "null") + ">");
847             if (dc.getApn() != null && apn != null
848                 && dc.getApn().toString().equals(apn.toString())) {
849                 return dc;
850             }
851         }
852         return null;
853     }
854
855
856     private boolean setupData(ApnContext apnContext) {
857         if (DBG) log("enter setupData!");
858         ApnSetting apn;
859         GsmDataConnection dc;
860
861         int profileId = getApnProfileID(apnContext.getApnType());
862         apn = apnContext.getNextWaitingApn();
863         if (apn == null) {
864             if (DBG) log("setupData: return for no apn found!");
865             return false;
866         }
867
868         dc = findReadyDataConnection(apn);
869
870         if (dc == null) {
871             if (DBG) log("setupData: No ready GsmDataConnection found!");
872             // TODO: When allocating you are mapping type to id. If more than 1 free,
873             // then could findFreeDataConnection get the wrong one??
874             dc = findFreeDataConnection();
875         }
876
877         if (dc == null) {
878             dc = createDataConnection(apnContext);
879         }
880
881         if (dc == null) {
882             if (DBG) log("setupData: No free GsmDataConnection found!");
883             return false;
884         }
885
886         apnContext.setApnSetting(apn);
887         apnContext.setDataConnection(dc);
888         dc.setProfileId( profileId );
889         dc.setActiveApnType(apnContext.getApnType());
890
891         Message msg = obtainMessage();
892         msg.what = EVENT_DATA_SETUP_COMPLETE;
893         msg.obj = apnContext;
894
895         if (DBG) log("dc connect!");
896         dc.connect(msg, apn);
897
898         apnContext.setState(State.INITING);
899         mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
900         if (DBG) log("setupData: initing!");
901         return true;
902     }
903
904     private boolean dataCallStatesHasCID (ArrayList<DataCallState> states, int cid) {
905         for (int i = 0, s = states.size() ; i < s ; i++) {
906             if (states.get(i).cid == cid) return true;
907         }
908         return false;
909     }
910
911     private boolean dataCallStatesHasActiveCID (ArrayList<DataCallState> states, int cid) {
912         for (int i = 0, s = states.size() ; i < s ; i++) {
913             if ((states.get(i).cid == cid) && (states.get(i).active != 0)) {
914                 return true;
915             }
916         }
917
918         return false;
919     }
920
921     /**
922      * Handles changes to the APN database.
923      */
924     private void onApnChanged() {
925         // TODO: How to handle when multiple APNs are active?
926         boolean isConnected;
927
928         ApnContext defaultApnContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT);
929         isConnected = (defaultApnContext.getState() != State.IDLE
930                        && defaultApnContext.getState() != State.FAILED);
931
932         if (mPhone instanceof GSMPhone) {
933             // The "current" may no longer be valid.  MMS depends on this to send properly. TBD
934             ((GSMPhone)mPhone).updateCurrentCarrierInProvider();
935         }
936
937         // TODO: It'd be nice to only do this if the changed entrie(s)
938         // match the current operator.
939         createAllApnList();
940         if (DBG) log("onApnChanged clean all connections");
941         cleanUpAllConnections(isConnected, Phone.REASON_APN_CHANGED);
942         if (!isConnected) {
943             // TODO: Won't work for multiple connections!!!!
944             defaultApnContext.getDataConnection().resetRetryCount();
945             defaultApnContext.setReason(Phone.REASON_APN_CHANGED);
946             trySetupData(defaultApnContext);
947         }
948     }
949
950     /**
951      * @param explicitPoll if true, indicates that *we* polled for this
952      * update while state == CONNECTED rather than having it delivered
953      * via an unsolicited response (which could have happened at any
954      * previous state
955      */
956     private void onDataStateChanged (AsyncResult ar, boolean explicitPoll) {
957         ArrayList<DataCallState> dataCallStates;
958
959         dataCallStates = (ArrayList<DataCallState>)(ar.result);
960
961         if (ar.exception != null) {
962             // This is probably "radio not available" or something
963             // of that sort. If so, the whole connection is going
964             // to come down soon anyway
965             return;
966         }
967
968         Iterator<ApnContext> it = mApnContexts.values().iterator();
969         while (it.hasNext()) {
970             ApnContext apnContext = it.next();
971             onDataStateChanged(dataCallStates, explicitPoll, apnContext);
972         }
973     }
974
975     private void onDataStateChanged (ArrayList<DataCallState> dataCallStates,
976                                      boolean explicitPoll,
977                                      ApnContext apnContext) {
978
979         if (apnContext == null) {
980             // Should not happen
981             return;
982         }
983
984         if (apnContext.getState() == State.CONNECTED) {
985             // The way things are supposed to work, the PDP list
986             // should not contain the CID after it disconnects.
987             // However, the way things really work, sometimes the PDP
988             // context is still listed with active = false, which
989             // makes it hard to distinguish an activating context from
990             // an activated-and-then deactivated one.
991             if (!dataCallStatesHasCID(dataCallStates, apnContext.getDataConnection().getCid())) {
992                 // It looks like the PDP context has deactivated.
993                 // Tear everything down and try to reconnect.
994
995                 Log.i(LOG_TAG, "PDP connection has dropped. Reconnecting");
996
997                 // Add an event log when the network drops PDP
998                 int cid = -1;
999                 GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation());
1000                 if (loc != null) cid = loc.getCid();
1001                 EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid,
1002                         TelephonyManager.getDefault().getNetworkType());
1003
1004                 cleanUpConnection(true, apnContext);
1005                 return;
1006             } else if (!dataCallStatesHasActiveCID(dataCallStates,
1007                     apnContext.getDataConnection().getCid())) {
1008                 // Here, we only consider this authoritative if we asked for the
1009                 // PDP list. If it was an unsolicited response, we poll again
1010                 // to make sure everyone agrees on the initial state.
1011
1012                 if (!explicitPoll) {
1013                     // We think it disconnected but aren't sure...poll from our side
1014                     mPhone.mCM.getPDPContextList(
1015                             this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
1016                 } else {
1017                     Log.i(LOG_TAG, "PDP connection has dropped (active=false case). "
1018                                     + " Reconnecting");
1019
1020                     // Log the network drop on the event log.
1021                     int cid = -1;
1022                     GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation());
1023                     if (loc != null) cid = loc.getCid();
1024                     EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP, cid,
1025                             TelephonyManager.getDefault().getNetworkType());
1026
1027                     cleanUpConnection(true, apnContext);
1028                 }
1029             }
1030         }
1031     }
1032
1033     private void notifyDefaultData(ApnContext apnContext) {
1034         if (DBG)
1035             log("notifyDefaultData for type: " + apnContext.getApnType()
1036                 + ", reason:" + apnContext.getReason());
1037         apnContext.setState(State.CONNECTED);
1038         // setState(State.CONNECTED);
1039         mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1040         startNetStatPoll();
1041         // reset reconnect timer
1042         apnContext.getDataConnection().resetRetryCount();
1043     }
1044
1045     // TODO: For multiple Active APNs not exactly sure how to do this.
1046     protected void gotoIdleAndNotifyDataConnection(String reason) {
1047         if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
1048         notifyDataConnection(reason);
1049         mActiveApn = null;
1050     }
1051
1052     private void resetPollStats() {
1053         mTxPkts = -1;
1054         mRxPkts = -1;
1055         mSentSinceLastRecv = 0;
1056         mNetStatPollPeriod = POLL_NETSTAT_MILLIS;
1057         mNoRecvPollCount = 0;
1058     }
1059
1060     private void doRecovery() {
1061         if (getOverallState() == State.CONNECTED) {
1062             int maxPdpReset = Settings.Secure.getInt(mResolver,
1063                     Settings.Secure.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT,
1064                     DEFAULT_MAX_PDP_RESET_FAIL);
1065             if (mPdpResetCount < maxPdpReset) {
1066                 mPdpResetCount++;
1067                 EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, mSentSinceLastRecv);
1068                 cleanUpAllConnections(true, Phone.REASON_PDP_RESET);
1069             } else {
1070                 mPdpResetCount = 0;
1071                 EventLog.writeEvent(EventLogTags.PDP_REREGISTER_NETWORK, mSentSinceLastRecv);
1072                 mPhone.getServiceStateTracker().reRegisterNetwork(null);
1073             }
1074             // TODO: Add increasingly drastic recovery steps, eg,
1075             // reset the radio, reset the device.
1076         }
1077     }
1078
1079     @Override
1080     protected void startNetStatPoll() {
1081         if (getOverallState() == State.CONNECTED && mNetStatPollEnabled == false) {
1082             log("[DataConnection] Start poll NetStat");
1083             resetPollStats();
1084             mNetStatPollEnabled = true;
1085             mPollNetStat.run();
1086         }
1087     }
1088
1089     @Override
1090     protected void stopNetStatPoll() {
1091         mNetStatPollEnabled = false;
1092         removeCallbacks(mPollNetStat);
1093         log("[DataConnection] Stop poll NetStat");
1094     }
1095
1096     @Override
1097     protected void restartRadio() {
1098         log("************TURN OFF RADIO**************");
1099         cleanUpAllConnections(true, Phone.REASON_RADIO_TURNED_OFF);
1100         mPhone.getServiceStateTracker().powerOffRadioSafely(this);
1101         /* Note: no need to call setRadioPower(true).  Assuming the desired
1102          * radio power state is still ON (as tracked by ServiceStateTracker),
1103          * ServiceStateTracker will call setRadioPower when it receives the
1104          * RADIO_STATE_CHANGED notification for the power off.  And if the
1105          * desired power state has changed in the interim, we don't want to
1106          * override it with an unconditional power on.
1107          */
1108
1109         int reset = Integer.parseInt(SystemProperties.get("net.ppp.reset-by-timeout", "0"));
1110         SystemProperties.set("net.ppp.reset-by-timeout", String.valueOf(reset+1));
1111     }
1112
1113     private Runnable mPollNetStat = new Runnable()
1114     {
1115
1116         public void run() {
1117             long sent, received;
1118             long preTxPkts = -1, preRxPkts = -1;
1119
1120             Activity newActivity;
1121
1122             preTxPkts = mTxPkts;
1123             preRxPkts = mRxPkts;
1124
1125             mTxPkts = TrafficStats.getMobileTxPackets();
1126             mRxPkts = TrafficStats.getMobileRxPackets();
1127
1128             //log("rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts));
1129
1130             if (mNetStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) {
1131                 sent = mTxPkts - preTxPkts;
1132                 received = mRxPkts - preRxPkts;
1133
1134                 if ( sent > 0 && received > 0 ) {
1135                     mSentSinceLastRecv = 0;
1136                     newActivity = Activity.DATAINANDOUT;
1137                     mPdpResetCount = 0;
1138                 } else if (sent > 0 && received == 0) {
1139                     if (mPhone.getState() == Phone.State.IDLE) {
1140                         mSentSinceLastRecv += sent;
1141                     } else {
1142                         mSentSinceLastRecv = 0;
1143                     }
1144                     newActivity = Activity.DATAOUT;
1145                 } else if (sent == 0 && received > 0) {
1146                     mSentSinceLastRecv = 0;
1147                     newActivity = Activity.DATAIN;
1148                     mPdpResetCount = 0;
1149                 } else if (sent == 0 && received == 0) {
1150                     newActivity = Activity.NONE;
1151                 } else {
1152                     mSentSinceLastRecv = 0;
1153                     newActivity = Activity.NONE;
1154                 }
1155
1156                 if (mActivity != newActivity && mIsScreenOn) {
1157                     mActivity = newActivity;
1158                     mPhone.notifyDataActivity();
1159                 }
1160             }
1161
1162             int watchdogTrigger = Settings.Secure.getInt(mResolver,
1163                     Settings.Secure.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
1164                     NUMBER_SENT_PACKETS_OF_HANG);
1165
1166             if (mSentSinceLastRecv >= watchdogTrigger) {
1167                 // we already have NUMBER_SENT_PACKETS sent without ack
1168                 if (mNoRecvPollCount == 0) {
1169                     EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET_COUNTDOWN_TRIGGERED,
1170                             mSentSinceLastRecv);
1171                 }
1172
1173                 int noRecvPollLimit = Settings.Secure.getInt(mResolver,
1174                         Settings.Secure.PDP_WATCHDOG_ERROR_POLL_COUNT, NO_RECV_POLL_LIMIT);
1175
1176                 if (mNoRecvPollCount < noRecvPollLimit) {
1177                     // It's possible the PDP context went down and we weren't notified.
1178                     // Start polling the context list in an attempt to recover.
1179                     if (DBG) log("no DATAIN in a while; polling PDP");
1180                     mPhone.mCM.getDataCallList(obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
1181
1182                     mNoRecvPollCount++;
1183
1184                     // Slow down the poll interval to let things happen
1185                     mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
1186                             Settings.Secure.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS,
1187                             POLL_NETSTAT_SLOW_MILLIS);
1188                 } else {
1189                     if (DBG) log("Sent " + String.valueOf(mSentSinceLastRecv) +
1190                                         " pkts since last received start recovery process");
1191                     stopNetStatPoll();
1192                     sendMessage(obtainMessage(EVENT_START_RECOVERY));
1193                 }
1194             } else {
1195                 mNoRecvPollCount = 0;
1196                 if (mIsScreenOn) {
1197                     mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
1198                             Settings.Secure.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS);
1199                 } else {
1200                     mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
1201                             Settings.Secure.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS,
1202                             POLL_NETSTAT_SCREEN_OFF_MILLIS);
1203                 }
1204             }
1205
1206             if (mNetStatPollEnabled) {
1207                 mDataConnectionTracker.postDelayed(this, mNetStatPollPeriod);
1208             }
1209         }
1210     };
1211
1212     /**
1213      * Returns true if the last fail cause is something that
1214      * seems like it deserves an error notification.
1215      * Transient errors are ignored
1216      */
1217     private boolean shouldPostNotification(GsmDataConnection.FailCause  cause) {
1218         return (cause != GsmDataConnection.FailCause.UNKNOWN);
1219     }
1220
1221     /**
1222      * Return true if data connection need to be setup after disconnected due to
1223      * reason.
1224      *
1225      * @param reason the reason why data is disconnected
1226      * @return true if try setup data connection is need for this reason
1227      */
1228     private boolean retryAfterDisconnected(String reason) {
1229         boolean retry = true;
1230
1231         if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) {
1232             retry = false;
1233         }
1234         return retry;
1235     }
1236
1237     private void reconnectAfterFail(FailCause lastFailCauseCode, ApnContext apnContext) {
1238         if (apnContext == null) {
1239             Log.d(LOG_TAG, "It is impossible");
1240             return;
1241         }
1242         if (apnContext.getState() == State.FAILED) {
1243             if (!apnContext.getDataConnection().isRetryNeeded()) {
1244                 if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)){
1245                     // if no more retries on a secondary APN attempt, tell the world and revert.
1246                     notifyDataConnection(Phone.REASON_APN_FAILED);
1247                     return;
1248                 }
1249                 if (mReregisterOnReconnectFailure) {
1250                     // We've re-registerd once now just retry forever.
1251                     apnContext.getDataConnection().retryForeverUsingLastTimeout();
1252                 } else {
1253                     // Try to Re-register to the network.
1254                     log("PDP activate failed, Reregistering to the network");
1255                     mReregisterOnReconnectFailure = true;
1256                     mPhone.getServiceStateTracker().reRegisterNetwork(null);
1257                     apnContext.getDataConnection().resetRetryCount();
1258                     return;
1259                 }
1260             }
1261
1262             int nextReconnectDelay = apnContext.getDataConnection().getRetryTimer();
1263             log("PDP activate failed. Scheduling next attempt for "
1264                     + (nextReconnectDelay / 1000) + "s");
1265
1266             AlarmManager am =
1267                 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
1268             Intent intent = new Intent(INTENT_RECONNECT_ALARM);
1269             intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason());
1270             // Should put an extra of apn type?
1271             intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, apnContext.getApnType());
1272             apnContext.setReconnectIntent(PendingIntent.getBroadcast (
1273                     mPhone.getContext(), 0, intent, 0));
1274             am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1275                     SystemClock.elapsedRealtime() + nextReconnectDelay,
1276                     apnContext.getReconnectIntent());
1277
1278             apnContext.getDataConnection().increaseRetryCount();
1279
1280             if (!shouldPostNotification(lastFailCauseCode)) {
1281                 Log.d(LOG_TAG, "NOT Posting GPRS Unavailable notification "
1282                                 + "-- likely transient error");
1283             } else {
1284                 notifyNoData(lastFailCauseCode, apnContext);
1285             }
1286         }
1287     }
1288
1289     private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode,
1290                               ApnContext apnContext) {
1291         if (DBG) log( "notifyNoData for type:" + apnContext.getApnType());
1292         apnContext.setState(State.FAILED);
1293         if (lastFailCauseCode.isPermanentFail()
1294             && (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT))) {
1295             mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
1296         }
1297     }
1298
1299     private void onRecordsLoaded() {
1300         createAllApnList();
1301         for (ApnContext apnContext : mApnContexts.values()) {
1302             if (apnContext.isReady()) {
1303                 apnContext.setReason(Phone.REASON_SIM_LOADED);
1304                 if (apnContext.getState() == State.FAILED) {
1305                     if (DBG) {
1306                         log("onRecordsLoaded clean connection for " + apnContext.getApnType());
1307                     }
1308                     cleanUpConnection(false, apnContext);
1309                 }
1310                 sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext));
1311             }
1312         }
1313     }
1314
1315     @Override
1316     protected void onSetDependencyMet(String apnType, boolean met) {
1317         ApnContext apnContext = mApnContexts.get(apnType);
1318         if (apnContext == null) {
1319             log("ApnContext not found in onSetDependencyMet(" + apnType + ", " + met + ")");
1320             return;
1321         }
1322         applyNewState(apnContext, apnContext.isEnabled(), met);
1323     }
1324
1325     private void applyNewState(ApnContext apnContext, boolean enabled, boolean met) {
1326         boolean cleanup = false;
1327         boolean trySetup = false;
1328         if (DBG) {
1329             log("applyNewState(" + apnContext.getApnType() + ", " + enabled +
1330                     "(" + apnContext.isEnabled() + "), " + met + "(" +
1331                     apnContext.getDependencyMet() +"))");
1332         }
1333         if (apnContext.isReady()) {
1334             if (enabled && met) return;
1335             if (!enabled) {
1336                 apnContext.setReason(Phone.REASON_DATA_DISABLED);
1337             } else {
1338                 apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_UNMET);
1339             }
1340             cleanup = true;
1341         } else {
1342             if (enabled && met) {
1343                 if (apnContext.isEnabled()) {
1344                     apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_MET);
1345                 } else {
1346                     apnContext.setReason(Phone.REASON_DATA_ENABLED);
1347                 }
1348                 DataConnection conn = checkForConnectionForApnContext(apnContext);
1349                 if (conn == null) {
1350                     if (apnContext.getState() == State.FAILED) {
1351                         apnContext.setState(State.IDLE);
1352                     }
1353                     trySetup = true;
1354                 } else {
1355                     // TODO send notifications
1356                     if (DBG) {
1357                         log("Found existing connection for " + apnContext.getApnType() +
1358                                 ": " + conn);
1359                     }
1360                     apnContext.setDataConnection(conn);
1361                 }
1362             }
1363         }
1364         apnContext.setEnabled(enabled);
1365         apnContext.setDependencyMet(met);
1366         if (cleanup) cleanUpConnection(true, apnContext);
1367         if (trySetup) trySetupData(apnContext);
1368     }
1369
1370     private DataConnection checkForConnectionForApnContext(ApnContext apnContext) {
1371         // Loop through all apnContexts looking for one with a conn that satisfies this apnType
1372         String apnType = apnContext.getApnType();
1373         for (ApnContext c : mApnContexts.values()) {
1374             DataConnection conn = c.getDataConnection();
1375             if (conn != null) {
1376                 ApnSetting apnSetting = c.getApnSetting();
1377                 if (apnSetting != null && apnSetting.canHandleType(apnType)) return conn;
1378             }
1379         }
1380         return null;
1381     }
1382
1383     @Override
1384     protected void onEnableApn(int apnId, int enabled) {
1385         ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
1386         if (apnContext == null) {
1387             log("ApnContext not found in onEnableApn(" + apnId + ", " + enabled + ")");
1388             return;
1389         }
1390         // TODO change our retry manager to use the appropriate numbers for the new APN
1391         log("onEnableApn with ApnContext E");
1392         applyNewState(apnContext, enabled == ENABLED, apnContext.getDependencyMet());
1393     }
1394
1395     @Override
1396     // TODO: We shouldnt need this.
1397     protected boolean onTrySetupData(String reason) {
1398         return trySetupData(reason, Phone.APN_TYPE_DEFAULT);
1399     }
1400
1401     protected boolean onTrySetupData(ApnContext apnContext) {
1402         return trySetupData(apnContext);
1403     }
1404
1405     @Override
1406     // TODO: Need to understand if more than DEFAULT is impacted?
1407     protected void onRoamingOff() {
1408         trySetupData(Phone.REASON_ROAMING_OFF, Phone.APN_TYPE_DEFAULT);
1409     }
1410
1411     @Override
1412     // TODO: Need to understand if more than DEFAULT is impacted?
1413     protected void onRoamingOn() {
1414         if (getDataOnRoamingEnabled()) {
1415             trySetupData(Phone.REASON_ROAMING_ON, Phone.APN_TYPE_DEFAULT);
1416         } else {
1417             if (DBG) log("Tear down data connection on roaming.");
1418             cleanUpAllConnections(true, Phone.REASON_ROAMING_ON);
1419         }
1420     }
1421
1422     @Override
1423     protected void onRadioAvailable() {
1424         if (mPhone.getSimulatedRadioControl() != null) {
1425             // Assume data is connected on the simulator
1426             // FIXME  this can be improved
1427             // setState(State.CONNECTED);
1428             notifyDataConnection(null);
1429
1430             log("We're on the simulator; assuming data is connected");
1431         }
1432
1433         if (getOverallState() != State.IDLE) {
1434             cleanUpConnection(true, null);
1435         }
1436     }
1437
1438     @Override
1439     protected void onRadioOffOrNotAvailable() {
1440         // Make sure our reconnect delay starts at the initial value
1441         // next time the radio comes on
1442
1443         for (DataConnection dc : mDataConnections.values()) {
1444             dc.resetRetryCount();
1445         }
1446         mReregisterOnReconnectFailure = false;
1447
1448         if (mPhone.getSimulatedRadioControl() != null) {
1449             // Assume data is connected on the simulator
1450             // FIXME  this can be improved
1451             log("We're on the simulator; assuming radio off is meaningless");
1452         } else {
1453             if (DBG) log("Radio is off and clean up all connection");
1454             cleanUpAllConnections(false, Phone.REASON_RADIO_TURNED_OFF);
1455         }
1456     }
1457
1458     @Override
1459     protected void onDataSetupComplete(AsyncResult ar) {
1460
1461         ApnContext apnContext = null;
1462
1463         if(ar.userObj instanceof ApnContext){
1464             apnContext = (ApnContext)ar.userObj;
1465         }
1466
1467         if (ar.exception == null) {
1468             // Everything is setup
1469             // TODO: We should clear LinkProperties/Capabilities when torn down or disconnected
1470             if (DBG) {
1471                 log(String.format("onDataSetupComplete: success apn=%s",
1472                     apnContext.getWaitingApns().get(0).apn));
1473             }
1474             mLinkProperties = getLinkProperties(apnContext.getDataConnection());
1475             mLinkCapabilities = getLinkCapabilities(apnContext.getDataConnection());
1476
1477             ApnSetting apn = apnContext.getApnSetting();
1478             if (apn.proxy != null && apn.proxy.length() != 0) {
1479                 try {
1480                     ProxyProperties proxy = new ProxyProperties(apn.proxy,
1481                             Integer.parseInt(apn.port), null);
1482                     mLinkProperties.setHttpProxy(proxy);
1483                 } catch (NumberFormatException e) {
1484                     loge("NumberFormatException making ProxyProperties (" + apn.port +
1485                             "): " + e);
1486                 }
1487             }
1488
1489             // everything is setup
1490             if(TextUtils.equals(apnContext.getApnType(),Phone.APN_TYPE_DEFAULT)) {
1491                 SystemProperties.set("gsm.defaultpdpcontext.active", "true");
1492                 if (canSetPreferApn && mPreferredApn == null) {
1493                     log("PREFERED APN is null");
1494                     mPreferredApn = apnContext.getApnSetting();
1495                     if (mPreferredApn != null) {
1496                         setPreferredApn(mPreferredApn.id);
1497                     }
1498                 }
1499             } else {
1500                 SystemProperties.set("gsm.defaultpdpcontext.active", "false");
1501             }
1502             notifyDefaultData(apnContext);
1503
1504             // TODO: For simultaneous PDP support, we need to build another
1505             // trigger another TRY_SETUP_DATA for the next APN type.  (Note
1506             // that the existing connection may service that type, in which
1507             // case we should try the next type, etc.
1508             // I dont believe for simultaneous PDP you need to trigger. Each
1509             // Connection should be independent and they can be setup simultaneously
1510             // So, dont have to wait till one is finished.
1511         } else {
1512             GsmDataConnection.FailCause cause;
1513             cause = (GsmDataConnection.FailCause) (ar.result);
1514             if (DBG) {
1515                 String apnString;
1516                 try {
1517                     apnString = apnContext.getWaitingApns().get(0).apn;
1518                 } catch (Exception e) {
1519                     apnString = "<unknown>";
1520                 }
1521                 log(String.format("onDataSetupComplete: error apn=%s cause=%s", apnString, cause));
1522             }
1523             if (cause.isEventLoggable()) {
1524                 // Log this failure to the Event Logs.
1525                 GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation());
1526                 EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL,
1527                         cause.ordinal(), loc != null ? loc.getCid() : -1,
1528                         TelephonyManager.getDefault().getNetworkType());
1529             }
1530
1531             // Count permanent failures and remove the APN we just tried
1532             if (cause.isPermanentFail()) apnContext.decWaitingApnsPermFailCount();
1533
1534             apnContext.removeNextWaitingApn();
1535             if (DBG) {
1536                 log(String.format("onDataSetupComplete: WaitingApns.size=%d" +
1537                         " WaitingApnsPermFailureCountDown=%d",
1538                         apnContext.getWaitingApns().size(),
1539                         apnContext.getWaitingApnsPermFailCount()));
1540             }
1541
1542             // See if there are more APN's to try
1543             if (apnContext.getWaitingApns().isEmpty()) {
1544                 if (apnContext.getWaitingApnsPermFailCount() == 0) {
1545                     if (DBG) {
1546                         log("onDataSetupComplete: All APN's had permanent failures, stop retrying");
1547                     }
1548                     apnContext.setState(State.FAILED);
1549                     notifyDataConnection(Phone.REASON_APN_FAILED);
1550                 } else {
1551                     if (DBG) log("onDataSetupComplete: Not all permanent failures, retry");
1552                     startDelayedRetry(cause, apnContext);
1553                 }
1554             } else {
1555                 if (DBG) log("onDataSetupComplete: Try next APN");
1556                 apnContext.setState(State.SCANNING);
1557                 // Wait a bit before trying the next APN, so that
1558                 // we're not tying up the RIL command channel
1559                 sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext),
1560                         APN_DELAY_MILLIS);
1561             }
1562         }
1563     }
1564
1565     /**
1566      * Called when EVENT_DISCONNECT_DONE is received.
1567      */
1568     @Override
1569     protected void onDisconnectDone(int connId, AsyncResult ar) {
1570         ApnContext apnContext = null;
1571
1572         if(DBG) log("EVENT_DISCONNECT_DONE connId=" + connId);
1573         if (ar.userObj instanceof ApnContext) {
1574             apnContext = (ApnContext) ar.userObj;
1575         }
1576
1577         mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
1578
1579         apnContext.setState(State.IDLE);
1580         apnContext.setApnSetting(null);
1581
1582         // if all data connection are gone, check whether Airplane mode request was
1583         // pending.
1584         if (!isConnected()) {
1585             if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
1586                 // Radio will be turned off. No need to retry data setup
1587                 return;
1588             }
1589         }
1590
1591         // Check if APN disabled.
1592         if (apnContext.getPendingAction() == ApnContext.PENDING_ACTION_APN_DISABLE) {
1593            apnContext.setEnabled(false);
1594            apnContext.setPendingAction(ApnContext.PENDING_ACTION_NONE);
1595         }
1596
1597         if (TextUtils.equals(apnContext.getApnType(), Phone.APN_TYPE_DEFAULT)
1598             && retryAfterDisconnected(apnContext.getReason())) {
1599             SystemProperties.set("gsm.defaultpdpcontext.active", "false");
1600             trySetupData(apnContext);
1601         }
1602         else if (apnContext.getPendingAction() == ApnContext.PENDING_ACTION_RECONNECT)
1603         {
1604             apnContext.setPendingAction(ApnContext.PENDING_ACTION_NONE);
1605             trySetupData(apnContext);
1606         }
1607     }
1608
1609     protected void onPollPdp() {
1610         if (getOverallState() == State.CONNECTED) {
1611             // only poll when connected
1612             mPhone.mCM.getPDPContextList(this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
1613             sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS);
1614         }
1615     }
1616
1617     @Override
1618     protected void onVoiceCallStarted() {
1619         if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
1620             stopNetStatPoll();
1621             notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
1622         }
1623     }
1624
1625     @Override
1626     protected void onVoiceCallEnded() {
1627         if (isConnected()) {
1628             if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
1629                 startNetStatPoll();
1630                 notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
1631             } else {
1632                 // clean slate after call end.
1633                 resetPollStats();
1634             }
1635         } else {
1636             // reset reconnect timer
1637             ApnContext defaultApnContext = mApnContexts.get(Phone.APN_TYPE_DEFAULT);
1638             if (defaultApnContext != null) {
1639                 defaultApnContext.getDataConnection().resetRetryCount();
1640                 mReregisterOnReconnectFailure = false;
1641                 // in case data setup was attempted when we were on a voice call
1642                 trySetupData(Phone.REASON_VOICE_CALL_ENDED, Phone.APN_TYPE_DEFAULT);
1643             }
1644         }
1645     }
1646
1647     @Override
1648     protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
1649         if (DBG) log("onCleanUpConnection");
1650         ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
1651         cleanUpConnection(tearDown, apnContext);
1652     }
1653
1654     protected boolean isConnected() {
1655         Iterator<ApnContext> it = mApnContexts.values().iterator();
1656          while (it.hasNext()) {
1657             ApnContext apnContext = it.next();
1658             if (apnContext.getState() == State.CONNECTED) {
1659             return true;
1660             }
1661         }
1662         return false;
1663     }
1664
1665     @Override
1666     protected void notifyDataConnection(String reason) {
1667         if (DBG) log("notify all enabled connection for:" + reason);
1668         Iterator<ApnContext> it = mApnContexts.values().iterator();
1669         while (it.hasNext()) {
1670             ApnContext apnContext = it.next();
1671             if (apnContext.isReady()) {
1672                 if (DBG) log("notify for type:"+apnContext.getApnType());
1673                 mPhone.notifyDataConnection(reason != null ? reason : apnContext.getReason(),
1674                         apnContext.getApnType());
1675             }
1676         }
1677         notifyDataAvailability(reason);
1678     }
1679
1680     /**
1681      * Based on the sim operator numeric, create a list for all possible
1682      * Data Connections and setup the preferredApn.
1683      */
1684     private void createAllApnList() {
1685         mAllApns = new ArrayList<ApnSetting>();
1686         String operator = mPhone.mSIMRecords.getSIMOperatorNumeric();
1687         if (operator != null) {
1688             String selection = "numeric = '" + operator + "'";
1689             if (DBG) log("createAllApnList: selection=" + selection);
1690
1691             Cursor cursor = mPhone.getContext().getContentResolver().query(
1692                     Telephony.Carriers.CONTENT_URI, null, selection, null, null);
1693
1694             if (cursor != null) {
1695                 if (cursor.getCount() > 0) {
1696                     mAllApns = createApnList(cursor);
1697                 }
1698                 cursor.close();
1699             }
1700         }
1701
1702         if (mAllApns.isEmpty()) {
1703             if (DBG) log("createAllApnList: No APN found for carrier: " + operator);
1704             mPreferredApn = null;
1705             // TODO: What is the right behaviour?
1706             //notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN);
1707         } else {
1708             mPreferredApn = getPreferredApn();
1709             if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
1710                 mPreferredApn = null;
1711                 setPreferredApn(-1);
1712             }
1713             if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn);
1714         }
1715         if (DBG) log("createAllApnList: X mAllApns=" + mAllApns);
1716     }
1717
1718     /** Return the id for a new data connection */
1719     private GsmDataConnection createDataConnection(ApnContext apnContext) {
1720         String apnType = apnContext.getApnType();
1721         log("createDataConnection(" + apnType + ") E");
1722         RetryManager rm = new RetryManager();
1723
1724         if (apnType.equals(Phone.APN_TYPE_DEFAULT)) {
1725             if (!rm.configure(SystemProperties.get("ro.gsm.data_retry_config"))) {
1726                 if (!rm.configure(DEFAULT_DATA_RETRY_CONFIG)) {
1727                     // Should never happen, log an error and default to a simple linear sequence.
1728                     log("Could not configure using DEFAULT_DATA_RETRY_CONFIG="
1729                             + DEFAULT_DATA_RETRY_CONFIG);
1730                     rm.configure(20, 2000, 1000);
1731                 }
1732             }
1733         } else {
1734             if (!rm.configure(SystemProperties.get("ro.gsm.2nd_data_retry_config"))) {
1735                 if (!rm.configure(SECONDARY_DATA_RETRY_CONFIG)) {
1736                     // Should never happen, log an error and default to a simple sequence.
1737                     log("Could note configure using SECONDARY_DATA_RETRY_CONFIG="
1738                             + SECONDARY_DATA_RETRY_CONFIG);
1739                     rm.configure("max_retries=3, 333, 333, 333");
1740                 }
1741             }
1742         }
1743
1744         int id = mUniqueIdGenerator.getAndIncrement();
1745         GsmDataConnection conn = GsmDataConnection.makeDataConnection(mPhone, id, rm);
1746         conn.resetRetryCount();
1747         mDataConnections.put(id, conn);
1748         apnContext.setDataConnection(conn);
1749
1750         log("createDataConnection(" + apnType + ") X id=" + id);
1751         return conn;
1752     }
1753
1754     private void destroyDataConnections() {
1755         if(mDataConnections != null) {
1756             log("destroyDataConnectionList clear mDataConnectionList");
1757             mDataConnections.clear();
1758         } else {
1759             log("destroyDataConnectionList mDataConnecitonList is empty, ignore");
1760         }
1761     }
1762
1763     /**
1764      * Build a list of APNs to be used to create PDP's.
1765      *
1766      * @param requestedApnType
1767      * @return waitingApns list to be used to create PDP
1768      *          error when waitingApns.isEmpty()
1769      */
1770     private ArrayList<ApnSetting> buildWaitingApns(String requestedApnType) {
1771         ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
1772
1773         if (requestedApnType.equals(Phone.APN_TYPE_DUN)) {
1774             ApnSetting dun = fetchDunApn();
1775             if (dun != null) apnList.add(dun);
1776             if (DBG) log("buildWaitingApns: X added APN_TYPE_DUN apnList=" + apnList);
1777             return apnList;
1778         }
1779
1780         String operator = mPhone.mSIMRecords.getSIMOperatorNumeric();
1781         if (requestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
1782             if (canSetPreferApn && mPreferredApn != null) {
1783                 log("Preferred APN:" + operator + ":"
1784                         + mPreferredApn.numeric + ":" + mPreferredApn);
1785                 if (mPreferredApn.numeric.equals(operator)) {
1786                     apnList.add(mPreferredApn);
1787                     if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
1788                     return apnList;
1789                 } else {
1790                     if (DBG) log("buildWaitingApns: no preferred APN");
1791                     setPreferredApn(-1);
1792                     mPreferredApn = null;
1793                 }
1794             }
1795         }
1796         if (mAllApns != null) {
1797             for (ApnSetting apn : mAllApns) {
1798                 if (apn.canHandleType(requestedApnType)) {
1799                     apnList.add(apn);
1800                 }
1801             }
1802         } else {
1803             loge("mAllApns is empty!");
1804         }
1805         if (DBG) log("buildWaitingApns: X apnList=" + apnList);
1806         return apnList;
1807     }
1808
1809     private String apnListToString (ArrayList<ApnSetting> apns) {
1810         StringBuilder result = new StringBuilder();
1811         for (int i = 0, size = apns.size(); i < size; i++) {
1812             result.append('[')
1813                   .append(apns.get(i).toString())
1814                   .append(']');
1815         }
1816         return result.toString();
1817     }
1818
1819     private void startDelayedRetry(GsmDataConnection.FailCause cause, ApnContext apnContext) {
1820         notifyNoData(cause, apnContext);
1821         reconnectAfterFail(cause, apnContext);
1822     }
1823
1824     private void setPreferredApn(int pos) {
1825         if (!canSetPreferApn) {
1826             return;
1827         }
1828
1829         ContentResolver resolver = mPhone.getContext().getContentResolver();
1830         resolver.delete(PREFERAPN_URI, null, null);
1831
1832         if (pos >= 0) {
1833             ContentValues values = new ContentValues();
1834             values.put(APN_ID, pos);
1835             resolver.insert(PREFERAPN_URI, values);
1836         }
1837     }
1838
1839     private ApnSetting getPreferredApn() {
1840         if (mAllApns.isEmpty()) {
1841             return null;
1842         }
1843
1844         Cursor cursor = mPhone.getContext().getContentResolver().query(
1845                 PREFERAPN_URI, new String[] { "_id", "name", "apn" },
1846                 null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
1847
1848         if (cursor != null) {
1849             canSetPreferApn = true;
1850         } else {
1851             canSetPreferApn = false;
1852         }
1853
1854         if (canSetPreferApn && cursor.getCount() > 0) {
1855             int pos;
1856             cursor.moveToFirst();
1857             pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
1858             for(ApnSetting p:mAllApns) {
1859                 if (p.id == pos && p.canHandleType(mRequestedApnType)) {
1860                     cursor.close();
1861                     return p;
1862                 }
1863             }
1864         }
1865
1866         if (cursor != null) {
1867             cursor.close();
1868         }
1869
1870         return null;
1871     }
1872
1873     @Override
1874     public void handleMessage (Message msg) {
1875         if (DBG) log("GSMDataConnTrack handleMessage "+msg);
1876
1877         if (!mPhone.mIsTheCurrentActivePhone || mIsDisposed) {
1878             log("Ignore GSM msgs since GSM phone is inactive");
1879             return;
1880         }
1881
1882         switch (msg.what) {
1883             case EVENT_RECORDS_LOADED:
1884                 onRecordsLoaded();
1885                 break;
1886
1887             case EVENT_DATA_CONNECTION_DETACHED:
1888                 onDataConnectionDetached();
1889                 break;
1890
1891             case EVENT_DATA_CONNECTION_ATTACHED:
1892                 onDataConnectionAttached();
1893                 break;
1894
1895             case EVENT_DATA_STATE_CHANGED:
1896                 onDataStateChanged((AsyncResult) msg.obj, false);
1897                 break;
1898
1899             case EVENT_GET_PDP_LIST_COMPLETE:
1900                 onDataStateChanged((AsyncResult) msg.obj, true);
1901                 break;
1902
1903             case EVENT_POLL_PDP:
1904                 onPollPdp();
1905                 break;
1906
1907             case EVENT_START_NETSTAT_POLL:
1908                 startNetStatPoll();
1909                 break;
1910
1911             case EVENT_START_RECOVERY:
1912                 doRecovery();
1913                 break;
1914
1915             case EVENT_APN_CHANGED:
1916                 onApnChanged();
1917                 break;
1918
1919             case EVENT_PS_RESTRICT_ENABLED:
1920                 /**
1921                  * We don't need to explicitly to tear down the PDP context
1922                  * when PS restricted is enabled. The base band will deactive
1923                  * PDP context and notify us with PDP_CONTEXT_CHANGED.
1924                  * But we should stop the network polling and prevent reset PDP.
1925                  */
1926                 log("[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
1927                 stopNetStatPoll();
1928                 mIsPsRestricted = true;
1929                 break;
1930
1931             case EVENT_PS_RESTRICT_DISABLED:
1932                 /**
1933                  * When PS restrict is removed, we need setup PDP connection if
1934                  * PDP connection is down.
1935                  */
1936                 log("[DSAC DEB] " + "EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
1937                 mIsPsRestricted  = false;
1938                 if (isConnected()) {
1939                     startNetStatPoll();
1940                 } else {
1941                     // TODO: Should all PDN states be checked to fail?
1942                     if (mState == State.FAILED) {
1943                         cleanUpAllConnections(false, Phone.REASON_PS_RESTRICT_ENABLED);
1944                         resetAllRetryCounts();
1945                         mReregisterOnReconnectFailure = false;
1946                     }
1947                     trySetupData(Phone.REASON_PS_RESTRICT_ENABLED, Phone.APN_TYPE_DEFAULT);
1948                 }
1949                 break;
1950             case EVENT_TRY_SETUP_DATA:
1951                 if (msg.obj instanceof ApnContext) {
1952                     onTrySetupData((ApnContext)msg.obj);
1953                 } else {
1954                     if (msg.obj instanceof String) {
1955                         onTrySetupData((String)msg.obj);
1956                     }
1957                 }
1958                 break;
1959
1960             case EVENT_CLEAN_UP_CONNECTION:
1961                 boolean tearDown = (msg.arg1 == 0) ? false : true;
1962                 if (msg.obj instanceof ApnContext) {
1963                     cleanUpConnection(tearDown, (ApnContext)msg.obj);
1964                 } else {
1965                     loge("[GsmDataConnectionTracker] connectpion cleanup request w/o apn context");
1966                 }
1967                 break;
1968             default:
1969                 // handle the message in the super class DataConnectionTracker
1970                 super.handleMessage(msg);
1971                 break;
1972         }
1973     }
1974
1975     protected int getApnProfileID(String apnType) {
1976         if (TextUtils.equals(apnType, Phone.APN_TYPE_IMS)) {
1977             return RILConstants.DATA_PROFILE_IMS;
1978         } else if (TextUtils.equals(apnType, Phone.APN_TYPE_FOTA)) {
1979             return RILConstants.DATA_PROFILE_FOTA;
1980         } else if (TextUtils.equals(apnType, Phone.APN_TYPE_CBS)) {
1981             return RILConstants.DATA_PROFILE_CBS;
1982         } else {
1983             return RILConstants.DATA_PROFILE_DEFAULT;
1984         }
1985     }
1986
1987     @Override
1988     public boolean isAnyActiveDataConnections() {
1989         return isConnected();
1990     }
1991
1992     @Override
1993     protected void log(String s) {
1994         Log.d(LOG_TAG, "[GsmDCT] " + s);
1995     }
1996
1997     @Override
1998     protected void loge(String s) {
1999         Log.e(LOG_TAG, "[GsmDCT] " + s);
2000     }
2001 }