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.annotation.Nullable;
20 import android.net.NetworkCapabilities;
21 import android.net.NetworkRequest;
22 import android.os.Message;
23 import android.telephony.Annotation.ApnType;
24 import android.telephony.data.ApnSetting;
25 import android.text.TextUtils;
26 import android.util.LocalLog;
27 import android.util.SparseIntArray;
28 
29 import com.android.internal.R;
30 import com.android.internal.telephony.DctConstants;
31 import com.android.internal.telephony.Phone;
32 import com.android.internal.telephony.RetryManager;
33 import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType;
34 import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType;
35 import com.android.internal.util.IndentingPrintWriter;
36 import com.android.telephony.Rlog;
37 
38 import java.io.FileDescriptor;
39 import java.io.PrintWriter;
40 import java.util.ArrayList;
41 import java.util.List;
42 import java.util.concurrent.atomic.AtomicBoolean;
43 import java.util.concurrent.atomic.AtomicInteger;
44 
45 /**
46  * Maintain the Apn context
47  */
48 public class ApnContext {
49 
50     public final String LOG_TAG;
51     private final static String SLOG_TAG = "ApnContext";
52 
53     protected static final boolean DBG = false;
54 
55     private final Phone mPhone;
56 
57     private final String mApnType;
58 
59     private DctConstants.State mState;
60 
61     private int mPriority;
62 
63     private ApnSetting mApnSetting;
64 
65     private DataConnection mDataConnection;
66 
67     private String mReason;
68 
69     /**
70      * user/app requested connection on this APN
71      */
72     AtomicBoolean mDataEnabled;
73 
74     private final Object mRefCountLock = new Object();
75 
76     private final DcTracker mDcTracker;
77 
78 
79     /**
80      * Remember this as a change in this value to a more permissive state
81      * should cause us to retry even permanent failures
82      */
83     private boolean mConcurrentVoiceAndDataAllowed;
84 
85     /**
86      * used to track a single connection request so disconnects can get ignored if
87      * obsolete.
88      */
89     private final AtomicInteger mConnectionGeneration = new AtomicInteger(0);
90 
91     /**
92      * Retry manager that handles the APN retry and delays.
93      */
94     private final RetryManager mRetryManager;
95 
96     /**
97      * ApnContext constructor
98      * @param phone phone object
99      * @param typeId APN type Id
100      * @param logTag Tag for logging
101      * @param tracker Data call tracker
102      * @param priority Priority of APN type
103      */
ApnContext(Phone phone, int typeId, String logTag, DcTracker tracker, int priority)104     public ApnContext(Phone phone, int typeId, String logTag, DcTracker tracker, int priority) {
105         this(phone, ApnSetting.getApnTypeString(typeId), logTag, tracker, priority);
106     }
107 
108     /**
109      * ApnContext constructor
110      * @param phone phone object
111      * @param apnType APN type (e.g. default, supl, mms, etc...)
112      * @param logTag Tag for logging
113      * @param tracker Data call tracker
114      * @param priority Priority of APN type
115      */
ApnContext(Phone phone, String apnType, String logTag, DcTracker tracker, int priority)116     public ApnContext(Phone phone, String apnType, String logTag, DcTracker tracker,
117             int priority) {
118         mPhone = phone;
119         mApnType = apnType;
120         mState = DctConstants.State.IDLE;
121         setReason(Phone.REASON_DATA_ENABLED);
122         mDataEnabled = new AtomicBoolean(false);
123         mPriority = priority;
124         LOG_TAG = logTag;
125         mDcTracker = tracker;
126         mRetryManager = new RetryManager(phone, apnType);
127     }
128 
129 
130 
131     /**
132      * Get the APN type
133      * @return The APN type
134      */
getApnType()135     public String getApnType() {
136         return mApnType;
137     }
138 
139     /**
140      * Gets the APN type bitmask.
141      * @return The APN type bitmask
142      */
getApnTypeBitmask()143     public int getApnTypeBitmask() {
144         return ApnSetting.getApnTypesBitmaskFromString(mApnType);
145     }
146 
147     /**
148      * Get the associated data connection
149      * @return The data connection
150      */
getDataConnection()151     public synchronized DataConnection getDataConnection() {
152         return mDataConnection;
153     }
154 
155     /**
156      * This priority is taken into account when concurrent data connections are not allowed.  The
157      * APN with the HIGHER priority is given preference.
158      * @return The priority of the APN type
159      */
getPriority()160     public int getPriority() {
161         return mPriority;
162     }
163 
164     /**
165      * Updates the priority of this context.
166      * @param priority The priority of the APN type
167      */
setPriority(int priority)168     public void setPriority(int priority) {
169         mPriority = priority;
170     }
171 
172     /**
173      * Keeping for backwards compatibility and in case it's needed in the future
174      * @return true
175      */
isDependencyMet()176     public boolean isDependencyMet() {
177         return true;
178     }
179 
180     /**
181      * Set the associated data connection.
182      * @param dc data connection
183      */
setDataConnection(DataConnection dc)184     public synchronized void setDataConnection(DataConnection dc) {
185         log("setDataConnectionAc: old=" + mDataConnection + ",new=" + dc + " this=" + this);
186         mDataConnection = dc;
187     }
188 
189     /**
190      * Release data connection.
191      * @param reason The reason of releasing data connection
192      */
releaseDataConnection(String reason)193     public synchronized void releaseDataConnection(String reason) {
194         if (mDataConnection != null) {
195             mDataConnection.tearDown(this, reason, null);
196             mDataConnection = null;
197         }
198         setState(DctConstants.State.IDLE);
199     }
200 
201     /**
202      * Get the current APN setting.
203      * @return APN setting
204      */
getApnSetting()205     public synchronized ApnSetting getApnSetting() {
206         log("getApnSetting: apnSetting=" + mApnSetting);
207         return mApnSetting;
208     }
209 
210     /**
211      * Set the APN setting.
212      * @param apnSetting APN setting
213      */
setApnSetting(ApnSetting apnSetting)214     public synchronized void setApnSetting(ApnSetting apnSetting) {
215         log("setApnSetting: apnSetting=" + apnSetting);
216         mApnSetting = apnSetting;
217     }
218 
219     /**
220      * Set the list of APN candidates which will be used for data call setup later.
221      * @param waitingApns List of APN candidates
222      */
setWaitingApns(ArrayList<ApnSetting> waitingApns)223     public synchronized void setWaitingApns(ArrayList<ApnSetting> waitingApns) {
224         mRetryManager.setWaitingApns(waitingApns);
225     }
226 
227     /**
228      * Get the next available APN to try.
229      * @return APN setting which will be used for data call setup.{@code null} if there is no
230      * APN can be retried.
231      */
getNextApnSetting()232     public @Nullable ApnSetting getNextApnSetting() {
233         return mRetryManager.getNextApnSetting();
234     }
235 
236     /**
237      * Save the modem suggested delay for retrying the current APN.
238      * This method is called when we get the suggested delay from RIL.
239      * @param delay The delay in milliseconds
240      */
setModemSuggestedDelay(long delay)241     public void setModemSuggestedDelay(long delay) {
242         mRetryManager.setModemSuggestedDelay(delay);
243     }
244 
245     /**
246      * Get the delay for trying the next APN setting if the current one failed.
247      * @param failFastEnabled True if fail fast mode enabled. In this case we'll use a shorter
248      *                        delay.
249      * @return The delay in milliseconds
250      */
getDelayForNextApn(boolean failFastEnabled)251     public long getDelayForNextApn(boolean failFastEnabled) {
252         return mRetryManager.getDelayForNextApn(failFastEnabled || isFastRetryReason());
253     }
254 
255     /**
256      * Mark the current APN setting permanently failed, which means it will not be retried anymore.
257      * @param apn APN setting
258      */
markApnPermanentFailed(ApnSetting apn)259     public void markApnPermanentFailed(ApnSetting apn) {
260         mRetryManager.markApnPermanentFailed(apn);
261     }
262 
263     /**
264      * Get the list of waiting APNs.
265      * @return the list of waiting APNs
266      */
getWaitingApns()267     public ArrayList<ApnSetting> getWaitingApns() {
268         return mRetryManager.getWaitingApns();
269     }
270 
271     /**
272      * Save the state indicating concurrent voice/data allowed.
273      * @param allowed True if concurrent voice/data is allowed
274      */
setConcurrentVoiceAndDataAllowed(boolean allowed)275     public synchronized void setConcurrentVoiceAndDataAllowed(boolean allowed) {
276         mConcurrentVoiceAndDataAllowed = allowed;
277     }
278 
279     /**
280      * Get the state indicating concurrent voice/data allowed.
281      * @return True if concurrent voice/data is allowed
282      */
isConcurrentVoiceAndDataAllowed()283     public synchronized boolean isConcurrentVoiceAndDataAllowed() {
284         return mConcurrentVoiceAndDataAllowed;
285     }
286 
287     /**
288      * Set the current data call state.
289      * @param s Current data call state
290      */
setState(DctConstants.State s)291     public synchronized void setState(DctConstants.State s) {
292         log("setState: " + s + ", previous state:" + mState);
293 
294         if (mState != s) {
295             mStateLocalLog.log("State changed from " + mState + " to " + s);
296             mState = s;
297         }
298 
299         if (mState == DctConstants.State.FAILED) {
300             if (mRetryManager.getWaitingApns() != null) {
301                 // when teardown the connection and set to IDLE
302                 mRetryManager.getWaitingApns().clear();
303             }
304         }
305     }
306 
307     /**
308      * Get the current data call state.
309      * @return The current data call state
310      */
getState()311     public synchronized DctConstants.State getState() {
312         return mState;
313     }
314 
315     /**
316      * Check whether the data call is disconnected or not.
317      * @return True if the data call is disconnected
318      */
isDisconnected()319     public boolean isDisconnected() {
320         DctConstants.State currentState = getState();
321         return ((currentState == DctConstants.State.IDLE) ||
322                     currentState == DctConstants.State.FAILED);
323     }
324 
325     /**
326      * Set the reason for data call connection.
327      * @param reason Reason for data call connection
328      */
setReason(String reason)329     public synchronized void setReason(String reason) {
330         log("set reason as " + reason + ",current state " + mState);
331         mReason = reason;
332     }
333 
334     /**
335      * Get the reason for data call connection.
336      * @return The reason for data call connection
337      */
getReason()338     public synchronized String getReason() {
339         return mReason;
340     }
341 
342     /**
343      * Check if ready for data call connection
344      * @return True if ready, otherwise false.
345      */
isReady()346     public boolean isReady() {
347         return mDataEnabled.get() && isDependencyMet();
348     }
349 
350     /**
351      * Check if the data call is in the state which allow connecting.
352      * @return True if allowed, otherwise false.
353      */
isConnectable()354     public boolean isConnectable() {
355         return isReady() && ((mState == DctConstants.State.IDLE)
356                                 || (mState == DctConstants.State.RETRYING)
357                                 || (mState == DctConstants.State.FAILED));
358     }
359 
360     /**
361      * Check if apn reason is fast retry reason which should apply shorter delay between apn re-try.
362      * @return True if it is fast retry reason, otherwise false.
363      */
isFastRetryReason()364     private boolean isFastRetryReason() {
365         return Phone.REASON_NW_TYPE_CHANGED.equals(mReason) ||
366                 Phone.REASON_APN_CHANGED.equals(mReason);
367     }
368 
369     /** Check if the data call is in connected or connecting state.
370      * @return True if the data call is in connected or connecting state
371      */
isConnectedOrConnecting()372     public boolean isConnectedOrConnecting() {
373         return isReady() && ((mState == DctConstants.State.CONNECTED)
374                                 || (mState == DctConstants.State.CONNECTING)
375                                 || (mState == DctConstants.State.RETRYING));
376     }
377 
378     /**
379      * Set data call enabled/disabled state.
380      * @param enabled True if data call is enabled
381      */
setEnabled(boolean enabled)382     public void setEnabled(boolean enabled) {
383         log("set enabled as " + enabled + ", current state is " + mDataEnabled.get());
384         mDataEnabled.set(enabled);
385     }
386 
387     /**
388      * Check if the data call is enabled or not.
389      * @return True if enabled
390      */
isEnabled()391     public boolean isEnabled() {
392         return mDataEnabled.get();
393     }
394 
isProvisioningApn()395     public boolean isProvisioningApn() {
396         String provisioningApn = mPhone.getContext().getResources()
397                 .getString(R.string.mobile_provisioning_apn);
398         if (!TextUtils.isEmpty(provisioningApn) &&
399                 (mApnSetting != null) && (mApnSetting.getApnName() != null)) {
400             return (mApnSetting.getApnName().equals(provisioningApn));
401         } else {
402             return false;
403         }
404     }
405 
406     private final LocalLog mLocalLog = new LocalLog(150);
407     private final ArrayList<NetworkRequest> mNetworkRequests = new ArrayList<>();
408     private final LocalLog mStateLocalLog = new LocalLog(50);
409 
requestLog(String str)410     public void requestLog(String str) {
411         synchronized (mLocalLog) {
412             mLocalLog.log(str);
413         }
414     }
415 
requestNetwork(NetworkRequest networkRequest, @RequestNetworkType int type, Message onCompleteMsg)416     public void requestNetwork(NetworkRequest networkRequest, @RequestNetworkType int type,
417                                Message onCompleteMsg) {
418         synchronized (mRefCountLock) {
419             mNetworkRequests.add(networkRequest);
420             logl("requestNetwork for " + networkRequest + ", type="
421                     + DcTracker.requestTypeToString(type));
422             mDcTracker.enableApn(ApnSetting.getApnTypesBitmaskFromString(mApnType), type,
423                     onCompleteMsg);
424             if (mDataConnection != null) {
425                 // New network request added. Should re-evaluate properties of
426                 // the data connection. For example, the score may change.
427                 mDataConnection.reevaluateDataConnectionProperties();
428             }
429         }
430     }
431 
releaseNetwork(NetworkRequest networkRequest, @ReleaseNetworkType int type)432     public void releaseNetwork(NetworkRequest networkRequest, @ReleaseNetworkType int type) {
433         synchronized (mRefCountLock) {
434             if (mNetworkRequests.contains(networkRequest) == false) {
435                 logl("releaseNetwork can't find this request (" + networkRequest + ")");
436             } else {
437                 mNetworkRequests.remove(networkRequest);
438                 if (mDataConnection != null) {
439                     // New network request added. Should re-evaluate properties of
440                     // the data connection. For example, the score may change.
441                     mDataConnection.reevaluateDataConnectionProperties();
442                 }
443                 logl("releaseNetwork left with " + mNetworkRequests.size()
444                         + " requests.");
445                 if (mNetworkRequests.size() == 0
446                         || type == DcTracker.RELEASE_TYPE_DETACH
447                         || type == DcTracker.RELEASE_TYPE_HANDOVER) {
448                     mDcTracker.disableApn(ApnSetting.getApnTypesBitmaskFromString(mApnType), type);
449                 }
450             }
451         }
452     }
453 
454     /**
455      * @param excludeDun True if excluding requests that have DUN capability
456      * @return True if the attached network requests contain restricted capability.
457      */
hasRestrictedRequests(boolean excludeDun)458     public boolean hasRestrictedRequests(boolean excludeDun) {
459         synchronized (mRefCountLock) {
460             for (NetworkRequest nr : mNetworkRequests) {
461                 if (excludeDun &&
462                         nr.networkCapabilities.hasCapability(
463                         NetworkCapabilities.NET_CAPABILITY_DUN)) {
464                     continue;
465                 }
466                 if (!nr.networkCapabilities.hasCapability(
467                         NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
468                     return true;
469                 }
470             }
471         }
472         return false;
473     }
474 
475     private final SparseIntArray mRetriesLeftPerErrorCode = new SparseIntArray();
476 
resetErrorCodeRetries()477     public void resetErrorCodeRetries() {
478         logl("ApnContext.resetErrorCodeRetries");
479 
480         String[] config = mPhone.getContext().getResources().getStringArray(
481                 com.android.internal.R.array.config_cell_retries_per_error_code);
482         synchronized (mRetriesLeftPerErrorCode) {
483             mRetriesLeftPerErrorCode.clear();
484 
485             for (String c : config) {
486                 String errorValue[] = c.split(",");
487                 if (errorValue != null && errorValue.length == 2) {
488                     int count = 0;
489                     int errorCode = 0;
490                     try {
491                         errorCode = Integer.parseInt(errorValue[0]);
492                         count = Integer.parseInt(errorValue[1]);
493                     } catch (NumberFormatException e) {
494                         log("Exception parsing config_retries_per_error_code: " + e);
495                         continue;
496                     }
497                     if (count > 0 && errorCode > 0) {
498                         mRetriesLeftPerErrorCode.put(errorCode, count);
499                     }
500                 } else {
501                     log("Exception parsing config_retries_per_error_code: " + c);
502                 }
503             }
504         }
505     }
506 
restartOnError(int errorCode)507     public boolean restartOnError(int errorCode) {
508         boolean result = false;
509         int retriesLeft = 0;
510         synchronized(mRetriesLeftPerErrorCode) {
511             retriesLeft = mRetriesLeftPerErrorCode.get(errorCode);
512             switch (retriesLeft) {
513                 case 0: {
514                     // not set, never restart modem
515                     break;
516                 }
517                 case 1: {
518                     resetErrorCodeRetries();
519                     result = true;
520                     break;
521                 }
522                 default: {
523                     mRetriesLeftPerErrorCode.put(errorCode, retriesLeft - 1);
524                     result = false;
525                 }
526             }
527         }
528         logl("ApnContext.restartOnError(" + errorCode + ") found " + retriesLeft
529                 + " and returned " + result);
530         return result;
531     }
532 
incAndGetConnectionGeneration()533     public int incAndGetConnectionGeneration() {
534         return mConnectionGeneration.incrementAndGet();
535     }
536 
getConnectionGeneration()537     public int getConnectionGeneration() {
538         return mConnectionGeneration.get();
539     }
540 
getRetryAfterDisconnectDelay()541     long getRetryAfterDisconnectDelay() {
542         return mRetryManager.getRetryAfterDisconnectDelay();
543     }
544 
getApnTypeFromNetworkRequest(NetworkRequest nr)545     static @ApnType int getApnTypeFromNetworkRequest(NetworkRequest nr) {
546         NetworkCapabilities nc = nr.networkCapabilities;
547         // For now, ignore the bandwidth stuff
548         if (nc.getTransportTypes().length > 0 &&
549                 nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == false) {
550             return ApnSetting.TYPE_NONE;
551         }
552 
553         // in the near term just do 1-1 matches.
554         // TODO - actually try to match the set of capabilities
555         int apnType = ApnSetting.TYPE_NONE;
556         boolean error = false;
557 
558         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
559             apnType = ApnSetting.TYPE_DEFAULT;
560         }
561         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
562             if (apnType != ApnSetting.TYPE_NONE) error = true;
563             apnType = ApnSetting.TYPE_MMS;
564         }
565         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
566             if (apnType != ApnSetting.TYPE_NONE) error = true;
567             apnType = ApnSetting.TYPE_SUPL;
568         }
569         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
570             if (apnType != ApnSetting.TYPE_NONE) error = true;
571             apnType = ApnSetting.TYPE_DUN;
572         }
573         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
574             if (apnType != ApnSetting.TYPE_NONE) error = true;
575             apnType = ApnSetting.TYPE_FOTA;
576         }
577         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
578             if (apnType != ApnSetting.TYPE_NONE) error = true;
579             apnType = ApnSetting.TYPE_IMS;
580         }
581         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
582             if (apnType != ApnSetting.TYPE_NONE) error = true;
583             apnType = ApnSetting.TYPE_CBS;
584         }
585         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IA)) {
586             if (apnType != ApnSetting.TYPE_NONE) error = true;
587             apnType = ApnSetting.TYPE_IA;
588         }
589         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
590             if (apnType != ApnSetting.TYPE_NONE) error = true;
591             apnType = ApnSetting.TYPE_EMERGENCY;
592         }
593         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_MCX)) {
594             if (apnType != ApnSetting.TYPE_NONE) error = true;
595             apnType = ApnSetting.TYPE_MCX;
596         }
597         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_XCAP)) {
598             if (apnType != ApnSetting.TYPE_NONE) error = true;
599             apnType = ApnSetting.TYPE_XCAP;
600         }
601         if (error) {
602             // TODO: If this error condition is removed, the framework's handling of
603             // NET_CAPABILITY_NOT_RESTRICTED will need to be updated so requests for
604             // say FOTA and INTERNET are marked as restricted.  This is not how
605             // NetworkCapabilities.maybeMarkCapabilitiesRestricted currently works.
606             Rlog.d(SLOG_TAG, "Multiple apn types specified in request - result is unspecified!");
607         }
608         if (apnType == ApnSetting.TYPE_NONE) {
609             Rlog.d(SLOG_TAG, "Unsupported NetworkRequest in Telephony: nr=" + nr);
610         }
611         return apnType;
612     }
613 
getNetworkRequests()614     public List<NetworkRequest> getNetworkRequests() {
615         synchronized (mRefCountLock) {
616             return new ArrayList<NetworkRequest>(mNetworkRequests);
617         }
618     }
619 
620     @Override
toString()621     public synchronized String toString() {
622         // We don't print mDataConnection because its recursive.
623         return "{mApnType=" + mApnType + " mState=" + getState() + " mWaitingApns={"
624                     + mRetryManager.getWaitingApns() + " priority=" + mPriority + "}"
625                     + " mApnSetting={" + mApnSetting
626                     + "} mReason=" + mReason + " mDataEnabled=" + mDataEnabled + "}";
627     }
628 
log(String s)629     private void log(String s) {
630         if (DBG) {
631             Rlog.d(LOG_TAG, "[ApnContext:" + mApnType + "] " + s);
632         }
633     }
634 
logl(String s)635     private void logl(String s) {
636         log(s);
637         mLocalLog.log(s);
638     }
639 
dump(FileDescriptor fd, PrintWriter printWriter, String[] args)640     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
641         final IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
642         synchronized (mRefCountLock) {
643             pw.println(toString());
644             if (mNetworkRequests.size() > 0) {
645                 pw.println("NetworkRequests:");
646                 pw.increaseIndent();
647                 for (NetworkRequest nr : mNetworkRequests) {
648                     pw.println(nr);
649                 }
650                 pw.decreaseIndent();
651             }
652             pw.increaseIndent();
653             pw.println("-----");
654             pw.println("Local log:");
655             mLocalLog.dump(fd, pw, args);
656             pw.println("-----");
657             pw.decreaseIndent();
658             pw.println("Historical APN state:");
659             pw.increaseIndent();
660             mStateLocalLog.dump(fd, pw, args);
661             pw.decreaseIndent();
662             pw.println(mRetryManager);
663             pw.println("--------------------------");
664         }
665     }
666 }
667