OSDN Git Service

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