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