Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 293c6b3e authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Added data retry manager and retry rules"

parents 9e52aefa b8c2f98e
Loading
Loading
Loading
Loading
+36 −0
Original line number Original line Diff line number Diff line
@@ -33,10 +33,15 @@ import android.telephony.SubscriptionManager;
import android.util.IndentingPrintWriter;
import android.util.IndentingPrintWriter;


import com.android.internal.telephony.Phone;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.data.DataRetryManager.DataRetryRule;
import com.android.telephony.Rlog;
import com.android.telephony.Rlog;


import java.io.FileDescriptor;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentHashMap;


@@ -59,6 +64,8 @@ public class DataConfigManager extends Handler {
    /** The network capability priority map */
    /** The network capability priority map */
    private final Map<Integer, Integer> mNetworkCapabilityPriorityMap = new ConcurrentHashMap<>();
    private final Map<Integer, Integer> mNetworkCapabilityPriorityMap = new ConcurrentHashMap<>();


    private final List<DataRetryRule> mDataRetryRules = new ArrayList<>();

    private @Nullable PersistableBundle mCarrierConfig = null;
    private @Nullable PersistableBundle mCarrierConfig = null;
    private @Nullable Resources mResources = null;
    private @Nullable Resources mResources = null;


@@ -132,6 +139,7 @@ public class DataConfigManager extends Handler {
                mPhone.getSubId());
                mPhone.getSubId());


        updateNetworkCapabilityPriority();
        updateNetworkCapabilityPriority();
        updateDataRetryRules();


        log("Data config updated. Config is " + (isConfigCarrierSpecific() ? "" : "not ")
        log("Data config updated. Config is " + (isConfigCarrierSpecific() ? "" : "not ")
                + "carrier specific.");
                + "carrier specific.");
@@ -180,6 +188,28 @@ public class DataConfigManager extends Handler {
        return 0;
        return 0;
    }
    }


    /**
     * Update the data retry rules from the carrier config.
     */
    private void updateDataRetryRules() {
        mDataRetryRules.clear();
        if (mCarrierConfig == null) return;
        String[] dataRetryRulesStrings = mCarrierConfig.getStringArray(
                CarrierConfigManager.KEY_TELEPHONY_DATA_RETRY_RULES_STRING_ARRAY);
        if (dataRetryRulesStrings != null) {
            Arrays.stream(dataRetryRulesStrings)
                    .map(DataRetryRule::new)
                    .forEach(mDataRetryRules::add);
        }
    }

    /**
     * @return The data retry rules from carrier config.
     */
    public @NonNull List<DataRetryRule> getDataRetryRules() {
        return Collections.unmodifiableList(mDataRetryRules);
    }

    /**
    /**
     * Registration point for subscription info ready
     * Registration point for subscription info ready
     *
     *
@@ -232,6 +262,12 @@ public class DataConfigManager extends Handler {
                    + entry.getValue() + " ");
                    + entry.getValue() + " ");
        }
        }
        pw.println();
        pw.println();
        pw.println("Data retry rules:");
        pw.increaseIndent();
        for (DataRetryRule rule : mDataRetryRules) {
            pw.println(rule);
        }
        pw.decreaseIndent();
        pw.decreaseIndent();
        pw.decreaseIndent();
        pw.decreaseIndent();
        pw.decreaseIndent();
    }
    }
+311 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.telephony.data;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.NetworkCapabilities;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.telephony.Annotation.DataFailureCause;
import android.telephony.DataFailCause;
import android.text.TextUtils;
import android.util.IndentingPrintWriter;
import android.util.LocalLog;

import com.android.internal.telephony.Phone;
import com.android.telephony.Rlog;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

/**
 * DataRetryManager manages data network setup retry and its configurations.
 */
public class DataRetryManager extends Handler {
    /** Event for data config updated. */
    private static final int EVENT_DATA_CONFIG_UPDATED = 1;

    private final @NonNull Phone mPhone;
    private final @NonNull String mLogTag;
    private final @NonNull LocalLog mLocalLog = new LocalLog(128);

    private final @NonNull DataConfigManager mDataConfigManager;

    private @NonNull List<DataRetryRule> mDataRetryRuleList = new ArrayList<>();

    /**
     * Represent a data retry rule. A rule consists a retry type (e.g. either by capabilities,
     * fail cause, or both), and a retry interval.
     */
    public static final class DataRetryRule {
        private static final String RULE_TAG_CAPABILITIES = "capabilities";
        private static final String RULE_TAG_FAIL_CAUSES = "fail_causes";
        private static final String RULE_TAG_RETRY_INTERVAL = "retry_interval";
        private static final String RULE_TAG_BACKOFF = "backoff";
        private static final String RULE_TAG_MAXIMUM_RETRIES = "maximum_retries";

        /**
         * The data network setup retry interval. Note that if this is {@code null}, then
         * {@link #getMaxRetries()} must return 0.
         */
        private @Nullable Duration mRetryInterval = null;

        /**
         * {@code true} if this the retry interval should be backed off. For example, if the first
         * time retry interval is 5 seconds, next time if it fails again, the interval should be
         * doubled to 10 seconds.
         */
        private boolean mBackedOffDuration = false;

        /**
         * The maximum retry times. After reaching the retry times, data retry will not be scheduled
         * with timer. Only environment changes (e.g. Airplane mode, SIM state, RAT, registration
         * state changes, etc..) can trigger the retry.
         */
        private int mMaxRetries = 10;

        /**
         * The network capabilities NetworkCapability.NET_CAPABILITY_XXX. Each data setup must be
         * associated with at least one network request. If that network request contains network
         * capabilities specified here, then retry will happen. {@code null} means the retry rule
         * is not using network capabilities.
         */
        private @Nullable int[] mNetworkCapabilities = null;

        /**
         * The fail causes. If data setup failed with certain fail causes, then retry will happen.
         * {@code null} means the retry rule is not using the fail causes.
         */
        private @Nullable @DataFailureCause int[] mFailCauses = null;

        /**
         * Represent a single setup data network retry rule.
         *
         * The syntax of the retry rule:
         * 1. Retry based on {@link NetworkCapabilities}
         * "capabilities=[netCaps1|netCaps2|...], [retry_interval=x], [backoff=[true|false]],
         *     [maximum_retries=y]"
         *
         * 2. Retry based on {@link DataFailCause}
         * "fail_causes=[cause1|cause2|cause3|...], [retry_interval=x], [backoff=[true|false]],
         *     [maximum_retries=y]"
         *
         * 3. Retry based on {@link NetworkCapabilities} and {@link DataFailCause}
         * "capabilities=[netCaps1|netCaps2|...], fail_causes=[cause1|cause2|cause3|...],
         *     [retry_interval=x], [backoff=[true|false]], [maximum_retries=y]"
         *
         * For example,
         * "capabilities=eims, retry_interval=1000, maximum_retries=20" means if the attached
         * network request is emergency, then retry data network setup every 1 second for up to 20
         * times.
         *
         * "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2253|2254
         * , maximum_retries=0" means for those fail causes, never retry with timers. Note that
         * when environment changes, retry can still happens.
         *
         * @param ruleString The retry rule in string format.
         * @throws IllegalArgumentException if the string can't be parsed to a retry rule.
         */
        public DataRetryRule(@NonNull String ruleString) {
            if (TextUtils.isEmpty(ruleString)) {
                throw new IllegalArgumentException("illegal rule " + ruleString);
            }
            ruleString = ruleString.trim().toLowerCase(Locale.ROOT);
            String[] expressions = ruleString.split("\\s*,\\s*");
            for (String expression : expressions) {
                String[] tokens = expression.trim().split("\\s*=\\s*");
                if (tokens.length != 2) {
                    throw new IllegalArgumentException("illegal rule " + ruleString);
                }
                String key = tokens[0].trim();
                String value = tokens[1].trim();
                try {
                    switch (key) {
                        case RULE_TAG_CAPABILITIES:
                            mNetworkCapabilities = Arrays.stream(value.split("\\s*\\|\\s*"))
                                    .map(String::trim)
                                    .mapToInt(DataUtils::getNetworkCapabilityFromString)
                                    .toArray();
                            break;
                        case RULE_TAG_FAIL_CAUSES:
                            mFailCauses = Arrays.stream(value.split("\\s*\\|\\s*"))
                                    .map(String::trim)
                                    .mapToInt(Integer::valueOf)
                                    .toArray();
                            break;
                        case RULE_TAG_RETRY_INTERVAL:
                            mRetryInterval = Duration.ofMillis(Long.parseLong(value));
                            break;
                        case RULE_TAG_BACKOFF:
                            mBackedOffDuration = Boolean.parseBoolean(value);
                            break;
                        case RULE_TAG_MAXIMUM_RETRIES:
                            mMaxRetries = Integer.parseInt(value);
                            break;
                        default:
                            throw new IllegalArgumentException("unexpected key " + key);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new IllegalArgumentException("illegal rule " + ruleString + ", e=" + e);
                }
            }
        }

        /**
         * @return The data network setup retry interval. Note that if this is {@code null}, then
         * {@link #getMaxRetries()} must return 0.
         */
        public @Nullable Duration getRetryInterval() {
            return mRetryInterval;
        }

        /**
         * @return {@code true} if this the retry interval should be backed off. For example, if
         * the first time retry interval is 5 seconds, next time if it fails again, the interval
         * should be doubled to 10 seconds.
         */
        public boolean isBackedOffDuration() {
            return mBackedOffDuration;
        }

        /**
         * @return The maximum retry times. After reaching the retry times, data retry will not be
         * scheduled with timer. Only environment changes (e.g. Airplane mode, SIM state, RAT,
         * registration state changes, etc..) can trigger the retry.
         */
        public int getMaxRetries() {
            return mMaxRetries;
        }

        /**
         * @return The network capabilities. Each data setup must be associated with at least one
         * network request. If that network request contains network capabilities specified here,
         * then retry will happen. {@code null} means the retry rule is not using network
         * capabilities.
         */
        public @Nullable int[] getNetworkCapabilities() {
            return mNetworkCapabilities;
        }

        /**
         * @return The fail causes. If data setup failed with certain fail causes, then retry will
         * happen. {@code null} means the retry rule is not using the fail causes.
         */
        public @Nullable @DataFailureCause int[] getFailCauses() {
            return mFailCauses;
        }

        @Override
        public String toString() {
            return "[RetryRule: Network capabilities:[" + DataUtils.networkCapabilitiesToString(
                    mNetworkCapabilities) + "], Fail causes=" + Arrays.toString(mFailCauses)
                    + ", Retry interval=" + mRetryInterval + ", Maximum retries=" + mMaxRetries
                    + ", Backoff=" + mBackedOffDuration + "]";
        }
    }

    /**
     * Constructor
     *
     * @param phone The phone instance.
     * @param looper The looper to be used by the handler. Currently the handler thread is the
     * phone process's main thread.
     */
    public DataRetryManager(Phone phone, Looper looper) {
        super(looper);
        mPhone = phone;
        mLogTag = "DRM-" + mPhone.getPhoneId();

        mDataConfigManager = mPhone.getDataNetworkController().getDataConfigManager();
        mDataConfigManager.registerForConfigUpdate(this, EVENT_DATA_CONFIG_UPDATED);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case EVENT_DATA_CONFIG_UPDATED:
                onDataConfigUpdated();
                break;
        }
    }

    /**
     * Called when data config is updated.
     */
    private void onDataConfigUpdated() {
        // TODO: Reset all retry related stuffs.

        mDataRetryRuleList = mDataConfigManager.getDataRetryRules();
    }

    /**
     * Log debug messages.
     * @param s debug messages
     */
    private void log(@NonNull String s) {
        Rlog.d(mLogTag, s);
    }

    /**
     * Log error messages.
     * @param s error messages
     */
    private void loge(@NonNull String s) {
        Rlog.e(mLogTag, s);
    }

    /**
     * Log debug messages and also log into the local log.
     * @param s debug messages
     */
    private void logl(@NonNull String s) {
        log(s);
        mLocalLog.log(s);
    }

    /**
     * Dump the state of DataRetryManager.
     *
     * @param fd File descriptor
     * @param printWriter Print writer
     * @param args Arguments
     */
    public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
        pw.println(DataRetryManager.class.getSimpleName() + "-" + mPhone.getPhoneId() + ":");
        pw.increaseIndent();
        pw.println("Retry rules:");
        pw.increaseIndent();
        for (DataRetryRule rule : mDataRetryRuleList) {
            pw.println(rule);
        }
        pw.decreaseIndent();
        pw.println("Local logs:");
        pw.increaseIndent();
        mLocalLog.dump(fd, pw, args);
        pw.decreaseIndent();
        pw.decreaseIndent();
    }
}
+22 −4
Original line number Original line Diff line number Diff line
@@ -19,6 +19,10 @@ package com.android.internal.telephony.data;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.net.NetworkCapabilities;
import android.net.NetworkCapabilities;


import java.util.Arrays;
import java.util.Locale;
import java.util.stream.Collectors;

/**
/**
 * This class contains all the utility methods used by telephony data stack.
 * This class contains all the utility methods used by telephony data stack.
 */
 */
@@ -30,7 +34,7 @@ public class DataUtils {
     * @return The network capability.
     * @return The network capability.
     */
     */
    public static int getNetworkCapabilityFromString(@NonNull String capabilityString) {
    public static int getNetworkCapabilityFromString(@NonNull String capabilityString) {
        switch (capabilityString) {
        switch (capabilityString.toUpperCase(Locale.ROOT)) {
            case "MMS": return NetworkCapabilities.NET_CAPABILITY_MMS;
            case "MMS": return NetworkCapabilities.NET_CAPABILITY_MMS;
            case "SUPL": return NetworkCapabilities.NET_CAPABILITY_SUPL;
            case "SUPL": return NetworkCapabilities.NET_CAPABILITY_SUPL;
            case "DUN": return NetworkCapabilities.NET_CAPABILITY_DUN;
            case "DUN": return NetworkCapabilities.NET_CAPABILITY_DUN;
@@ -50,12 +54,12 @@ public class DataUtils {
    }
    }


    /**
    /**
     * Convert capabilities to string.
     * Convert a network capability to string.
     *
     *
     * This is for debugging and logging purposes only.
     * This is for debugging and logging purposes only.
     *
     *
     * @param netCap Network capability
     * @param netCap Network capability.
     * @return Network capability in string format
     * @return Network capability in string format.
     */
     */
    public static String networkCapabilityToString(int netCap) {
    public static String networkCapabilityToString(int netCap) {
        switch (netCap) {
        switch (netCap) {
@@ -98,4 +102,18 @@ public class DataUtils {
                return "Unknown(" + Integer.toString(netCap) + ")";
                return "Unknown(" + Integer.toString(netCap) + ")";
        }
        }
    }
    }

    /**
     * Convert network capabilities to string.
     *
     * This is for debugging and logging purposes only.
     *
     * @param netCaps Network capabilities.
     * @return Network capabilities in string format.
     */
    public static String networkCapabilitiesToString(int[] netCaps) {
        return Arrays.stream(netCaps)
                .mapToObj(DataUtils::networkCapabilityToString)
                .collect(Collectors.joining("|"));
    }
}
}
+8 −0
Original line number Original line Diff line number Diff line
@@ -86,6 +86,8 @@ import com.android.ims.ImsEcbm;
import com.android.ims.ImsManager;
import com.android.ims.ImsManager;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
import com.android.internal.telephony.cdma.EriManager;
import com.android.internal.telephony.cdma.EriManager;
import com.android.internal.telephony.data.DataConfigManager;
import com.android.internal.telephony.data.DataNetworkController;
import com.android.internal.telephony.dataconnection.DataEnabledOverride;
import com.android.internal.telephony.dataconnection.DataEnabledOverride;
import com.android.internal.telephony.dataconnection.DataEnabledSettings;
import com.android.internal.telephony.dataconnection.DataEnabledSettings;
import com.android.internal.telephony.dataconnection.DataThrottler;
import com.android.internal.telephony.dataconnection.DataThrottler;
@@ -198,6 +200,10 @@ public abstract class TelephonyTest {
    @Mock
    @Mock
    protected DcTracker mDcTracker;
    protected DcTracker mDcTracker;
    @Mock
    @Mock
    protected DataNetworkController mDataNetworkController;
    @Mock
    protected DataConfigManager mDataConfigManager;
    @Mock
    protected DisplayInfoController mDisplayInfoController;
    protected DisplayInfoController mDisplayInfoController;
    @Mock
    @Mock
    protected GsmCdmaCall mGsmCdmaCall;
    protected GsmCdmaCall mGsmCdmaCall;
@@ -572,6 +578,7 @@ public abstract class TelephonyTest {
        doReturn(mTransportManager).when(mPhone).getTransportManager();
        doReturn(mTransportManager).when(mPhone).getTransportManager();
        doReturn(mDataEnabledSettings).when(mPhone).getDataEnabledSettings();
        doReturn(mDataEnabledSettings).when(mPhone).getDataEnabledSettings();
        doReturn(mDcTracker).when(mPhone).getDcTracker(anyInt());
        doReturn(mDcTracker).when(mPhone).getDcTracker(anyInt());
        doReturn(mDataNetworkController).when(mPhone).getDataNetworkController();
        doReturn(mCarrierPrivilegesTracker).when(mPhone).getCarrierPrivilegesTracker();
        doReturn(mCarrierPrivilegesTracker).when(mPhone).getCarrierPrivilegesTracker();
        doReturn(mSignalStrength).when(mPhone).getSignalStrength();
        doReturn(mSignalStrength).when(mPhone).getSignalStrength();
        doReturn(mVoiceCallSessionStats).when(mPhone).getVoiceCallSessionStats();
        doReturn(mVoiceCallSessionStats).when(mPhone).getVoiceCallSessionStats();
@@ -582,6 +589,7 @@ public abstract class TelephonyTest {
        doReturn(mLinkBandwidthEstimator).when(mPhone).getLinkBandwidthEstimator();
        doReturn(mLinkBandwidthEstimator).when(mPhone).getLinkBandwidthEstimator();
        doReturn(mCellIdentity).when(mPhone).getCurrentCellIdentity();
        doReturn(mCellIdentity).when(mPhone).getCurrentCellIdentity();
        doReturn(mCellLocation).when(mCellIdentity).asCellLocation();
        doReturn(mCellLocation).when(mCellIdentity).asCellLocation();
        doReturn(mDataConfigManager).when(mDataNetworkController).getDataConfigManager();


        //mUiccController
        //mUiccController
        doReturn(mUiccCardApplication3gpp).when(mUiccController).getUiccCardApplication(anyInt(),
        doReturn(mUiccCardApplication3gpp).when(mUiccController).getUiccCardApplication(anyInt(),
+1 −0
Original line number Original line Diff line number Diff line
@@ -82,6 +82,7 @@ public class DataNetworkControllerTest extends TelephonyTest {


    @After
    @After
    public void tearDown() throws Exception {
    public void tearDown() throws Exception {
        mDataNetworkControllerTestHandler.quit();
        super.tearDown();
        super.tearDown();
    }
    }


Loading