ApnContext.java revision e8b308038b3449509c92478ee26096302d768855
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
17package com.android.internal.telephony.dataconnection;
18
19import android.app.PendingIntent;
20import android.content.Context;
21import android.content.res.Resources;
22import android.net.NetworkConfig;
23import android.telephony.Rlog;
24import android.text.TextUtils;
25import android.util.LocalLog;
26import android.util.SparseIntArray;
27
28import com.android.internal.R;
29import com.android.internal.telephony.DctConstants;
30import com.android.internal.telephony.Phone;
31import com.android.internal.util.IndentingPrintWriter;
32
33import java.io.FileDescriptor;
34import java.io.PrintWriter;
35import java.util.ArrayList;
36import java.util.concurrent.atomic.AtomicBoolean;
37import java.util.concurrent.atomic.AtomicInteger;
38
39/**
40 * Maintain the Apn context
41 */
42public class ApnContext {
43
44    public final String LOG_TAG;
45
46    protected static final boolean DBG = false;
47
48    private final Context mContext;
49
50    private final String mApnType;
51
52    private DctConstants.State mState;
53
54    private ArrayList<ApnSetting> mWaitingApns = null;
55
56    /**
57     * Used to check if conditions (new RAT) are resulting in a new list which warrants a retry.
58     * Set in the last trySetupData call.
59     */
60    private ArrayList<ApnSetting> mOriginalWaitingApns = null;
61
62    public final int priority;
63
64    /** A zero indicates that all waiting APNs had a permanent error */
65    private AtomicInteger mWaitingApnsPermanentFailureCountDown;
66
67    private ApnSetting mApnSetting;
68
69    DcAsyncChannel mDcAc;
70
71    String mReason;
72
73    PendingIntent mReconnectAlarmIntent;
74
75    /**
76     * user/app requested connection on this APN
77     */
78    AtomicBoolean mDataEnabled;
79
80    private final Object mRefCountLock = new Object();
81    private int mRefCount = 0;
82
83    /**
84     * carrier requirements met
85     */
86    AtomicBoolean mDependencyMet;
87
88    private final DcTrackerBase mDcTracker;
89
90    /**
91     * Remember this as a change in this value to a more permissive state
92     * should cause us to retry even permanent failures
93     */
94    private boolean mConcurrentVoiceAndDataAllowed;
95
96    /**
97     * used to track a single connection request so disconnects can get ignored if
98     * obsolete.
99     */
100    private final AtomicInteger mConnectionGeneration = new AtomicInteger(0);
101
102    public ApnContext(Context context, String apnType, String logTag, NetworkConfig config,
103            DcTrackerBase tracker) {
104        mContext = context;
105        mApnType = apnType;
106        mState = DctConstants.State.IDLE;
107        setReason(Phone.REASON_DATA_ENABLED);
108        mDataEnabled = new AtomicBoolean(false);
109        mDependencyMet = new AtomicBoolean(config.dependencyMet);
110        mWaitingApnsPermanentFailureCountDown = new AtomicInteger(0);
111        priority = config.priority;
112        LOG_TAG = logTag;
113        mDcTracker = tracker;
114    }
115
116    public String getApnType() {
117        return mApnType;
118    }
119
120    public synchronized DcAsyncChannel getDcAc() {
121        return mDcAc;
122    }
123
124    public synchronized void setDataConnectionAc(DcAsyncChannel dcac) {
125        if (DBG) {
126            log("setDataConnectionAc: old dcac=" + mDcAc + " new dcac=" + dcac
127                    + " this=" + this);
128        }
129        mDcAc = dcac;
130    }
131
132    public synchronized void releaseDataConnection(String reason) {
133        if (mDcAc != null) {
134            mDcAc.tearDown(this, reason, null);
135            mDcAc = null;
136        }
137        setState(DctConstants.State.IDLE);
138    }
139
140    public synchronized PendingIntent getReconnectIntent() {
141        return mReconnectAlarmIntent;
142    }
143
144    public synchronized void setReconnectIntent(PendingIntent intent) {
145        mReconnectAlarmIntent = intent;
146    }
147
148    public synchronized ApnSetting getApnSetting() {
149        if (DBG) log("getApnSetting: apnSetting=" + mApnSetting);
150        return mApnSetting;
151    }
152
153    public synchronized void setApnSetting(ApnSetting apnSetting) {
154        if (DBG) log("setApnSetting: apnSetting=" + apnSetting);
155        mApnSetting = apnSetting;
156    }
157
158    public synchronized void setWaitingApns(ArrayList<ApnSetting> waitingApns) {
159        mWaitingApns = waitingApns;
160        mOriginalWaitingApns = new ArrayList<ApnSetting>(waitingApns);
161        mWaitingApnsPermanentFailureCountDown.set(mWaitingApns.size());
162    }
163
164    public int getWaitingApnsPermFailCount() {
165        return mWaitingApnsPermanentFailureCountDown.get();
166    }
167
168    public void decWaitingApnsPermFailCount() {
169        mWaitingApnsPermanentFailureCountDown.decrementAndGet();
170    }
171
172    public synchronized ApnSetting getNextWaitingApn() {
173        ArrayList<ApnSetting> list = mWaitingApns;
174        ApnSetting apn = null;
175
176        if (list != null) {
177            if (!list.isEmpty()) {
178                apn = list.get(0);
179            }
180        }
181        return apn;
182    }
183
184    public synchronized void removeWaitingApn(ApnSetting apn) {
185        if (mWaitingApns != null) {
186            mWaitingApns.remove(apn);
187        }
188    }
189
190    public synchronized ArrayList<ApnSetting> getOriginalWaitingApns() {
191        return mOriginalWaitingApns;
192    }
193
194    public synchronized ArrayList<ApnSetting> getWaitingApns() {
195        return mWaitingApns;
196    }
197
198    public synchronized void setConcurrentVoiceAndDataAllowed(boolean allowed) {
199        mConcurrentVoiceAndDataAllowed = allowed;
200    }
201
202    public synchronized boolean isConcurrentVoiceAndDataAllowed() {
203        return mConcurrentVoiceAndDataAllowed;
204    }
205
206    public synchronized void setState(DctConstants.State s) {
207        if (DBG) {
208            log("setState: " + s + ", previous state:" + mState);
209        }
210
211        mState = s;
212
213        if (mState == DctConstants.State.FAILED) {
214            if (mWaitingApns != null) {
215                mWaitingApns.clear(); // when teardown the connection and set to IDLE
216            }
217        }
218    }
219
220    public synchronized DctConstants.State getState() {
221        return mState;
222    }
223
224    public boolean isDisconnected() {
225        DctConstants.State currentState = getState();
226        return ((currentState == DctConstants.State.IDLE) ||
227                    currentState == DctConstants.State.FAILED);
228    }
229
230    public synchronized void setReason(String reason) {
231        if (DBG) {
232            log("set reason as " + reason + ",current state " + mState);
233        }
234        mReason = reason;
235    }
236
237    public synchronized String getReason() {
238        return mReason;
239    }
240
241    public boolean isReady() {
242        return mDataEnabled.get() && mDependencyMet.get();
243    }
244
245    public boolean isConnectable() {
246        return isReady() && ((mState == DctConstants.State.IDLE)
247                                || (mState == DctConstants.State.SCANNING)
248                                || (mState == DctConstants.State.RETRYING)
249                                || (mState == DctConstants.State.FAILED));
250    }
251
252    public boolean isConnectedOrConnecting() {
253        return isReady() && ((mState == DctConstants.State.CONNECTED)
254                                || (mState == DctConstants.State.CONNECTING)
255                                || (mState == DctConstants.State.SCANNING)
256                                || (mState == DctConstants.State.RETRYING));
257    }
258
259    public void setEnabled(boolean enabled) {
260        if (DBG) {
261            log("set enabled as " + enabled + ", current state is " + mDataEnabled.get());
262        }
263        mDataEnabled.set(enabled);
264    }
265
266    public boolean isEnabled() {
267        return mDataEnabled.get();
268    }
269
270    public void setDependencyMet(boolean met) {
271        if (DBG) {
272            log("set mDependencyMet as " + met + " current state is " + mDependencyMet.get());
273        }
274        mDependencyMet.set(met);
275    }
276
277    public boolean getDependencyMet() {
278       return mDependencyMet.get();
279    }
280
281    public boolean isProvisioningApn() {
282        String provisioningApn = mContext.getResources()
283                .getString(R.string.mobile_provisioning_apn);
284        if (!TextUtils.isEmpty(provisioningApn) &&
285                (mApnSetting != null) && (mApnSetting.apn != null)) {
286            return (mApnSetting.apn.equals(provisioningApn));
287        } else {
288            return false;
289        }
290    }
291
292    private final ArrayList<LocalLog> mLocalLogs = new ArrayList<LocalLog>();
293
294    public void requestLog(String str) {
295        synchronized (mRefCountLock) {
296            for (LocalLog l : mLocalLogs) {
297                l.log(str);
298            }
299        }
300    }
301
302    public void incRefCount(LocalLog log) {
303        synchronized (mRefCountLock) {
304            if (mRefCount == 0) {
305               // we wanted to leave the last in so it could actually capture the tear down
306               // of the network
307               requestLog("clearing log with size=" + mLocalLogs.size());
308               mLocalLogs.clear();
309            }
310            if (mLocalLogs.contains(log)) {
311                log.log("ApnContext.incRefCount has duplicate add - " + mRefCount);
312            } else {
313                mLocalLogs.add(log);
314                log.log("ApnContext.incRefCount - " + mRefCount);
315            }
316            if (mRefCount++ == 0) {
317                mDcTracker.setEnabled(mDcTracker.apnTypeToId(mApnType), true);
318            }
319        }
320    }
321
322    public void decRefCount(LocalLog log) {
323        synchronized (mRefCountLock) {
324            // leave the last log alive to capture the actual tear down
325            if (mRefCount != 1) {
326                if (mLocalLogs.remove(log)) {
327                    log.log("ApnContext.decRefCount - " + mRefCount);
328                } else {
329                    log.log("ApnContext.decRefCount didn't find log - " + mRefCount);
330                }
331            } else {
332                log.log("ApnContext.decRefCount - 1");
333            }
334            if (mRefCount-- == 1) {
335                mDcTracker.setEnabled(mDcTracker.apnTypeToId(mApnType), false);
336            }
337            if (mRefCount < 0) {
338                log.log("ApnContext.decRefCount went to " + mRefCount);
339                mRefCount = 0;
340            }
341        }
342    }
343
344    private final SparseIntArray mRetriesLeftPerErrorCode = new SparseIntArray();
345
346    public void resetErrorCodeRetries() {
347        requestLog("ApnContext.resetErrorCodeRetries");
348        if (DBG) log("ApnContext.resetErrorCodeRetries");
349
350        String[] config = Resources.getSystem().getStringArray(
351                com.android.internal.R.array.config_cell_retries_per_error_code);
352        synchronized (mRetriesLeftPerErrorCode) {
353            mRetriesLeftPerErrorCode.clear();
354
355            for (String c : config) {
356                String errorValue[] = c.split(",");
357                if (errorValue != null && errorValue.length == 2) {
358                    int count = 0;
359                    int errorCode = 0;
360                    try {
361                        errorCode = Integer.parseInt(errorValue[0]);
362                        count = Integer.parseInt(errorValue[1]);
363                    } catch (NumberFormatException e) {
364                        log("Exception parsing config_retries_per_error_code: " + e);
365                        continue;
366                    }
367                    if (count > 0 && errorCode > 0) {
368                        mRetriesLeftPerErrorCode.put(errorCode, count);
369                    }
370                } else {
371                    log("Exception parsing config_retries_per_error_code: " + c);
372                }
373            }
374        }
375    }
376
377    public boolean restartOnError(int errorCode) {
378        boolean result = false;
379        int retriesLeft = 0;
380        synchronized(mRetriesLeftPerErrorCode) {
381            retriesLeft = mRetriesLeftPerErrorCode.get(errorCode);
382            switch (retriesLeft) {
383                case 0: {
384                    // not set, never restart modem
385                    break;
386                }
387                case 1: {
388                    resetErrorCodeRetries();
389                    result = true;
390                    break;
391                }
392                default: {
393                    mRetriesLeftPerErrorCode.put(errorCode, retriesLeft - 1);
394                    result = false;
395                }
396            }
397        }
398        String str = "ApnContext.restartOnError(" + errorCode + ") found " + retriesLeft +
399                " and returned " + result;
400        if (DBG) log(str);
401        requestLog(str);
402        return result;
403    }
404
405    public int incAndGetConnectionGeneration() {
406        return mConnectionGeneration.incrementAndGet();
407    }
408
409    public int getConnectionGeneration() {
410        return mConnectionGeneration.get();
411    }
412
413    @Override
414    public synchronized String toString() {
415        // We don't print mDataConnection because its recursive.
416        return "{mApnType=" + mApnType + " mState=" + getState() + " mWaitingApns={" +
417                mWaitingApns + "} mWaitingApnsPermanentFailureCountDown=" +
418                mWaitingApnsPermanentFailureCountDown + " mApnSetting={" + mApnSetting +
419                "} mReason=" + mReason + " mDataEnabled=" + mDataEnabled + " mDependencyMet=" +
420                mDependencyMet + "}";
421    }
422
423    private void log(String s) {
424        Rlog.d(LOG_TAG, "[ApnContext:" + mApnType + "] " + s);
425    }
426
427    public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
428        final IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
429        synchronized (mRefCountLock) {
430            pw.println(toString());
431            if (mRefCount > 0) {
432                pw.increaseIndent();
433                for (LocalLog l : mLocalLogs) {
434                    l.dump(fd, pw, args);
435                }
436                pw.decreaseIndent();
437            }
438        }
439    }
440}
441