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