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