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