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