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

Commit f1adfa0d authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Added data retry manager and retry rules" am: 293c6b3e am: 72437f79

Original change: https://android-review.googlesource.com/c/platform/frameworks/opt/telephony/+/2001330

Change-Id: Ib9bd025b88a20e1c818e7748f0950214c4e2302c
parents fa9bd365 72437f79
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -33,10 +33,15 @@ import android.telephony.SubscriptionManager;
import android.util.IndentingPrintWriter;

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

import java.io.FileDescriptor;
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.concurrent.ConcurrentHashMap;

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

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

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

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

        updateNetworkCapabilityPriority();
        updateDataRetryRules();

        log("Data config updated. Config is " + (isConfigCarrierSpecific() ? "" : "not ")
                + "carrier specific.");
@@ -180,6 +188,28 @@ public class DataConfigManager extends Handler {
        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
     *
@@ -232,6 +262,12 @@ public class DataConfigManager extends Handler {
                    + entry.getValue() + " ");
        }
        pw.println();
        pw.println("Data retry rules:");
        pw.increaseIndent();
        for (DataRetryRule rule : mDataRetryRules) {
            pw.println(rule);
        }
        pw.decreaseIndent();
        pw.decreaseIndent();
        pw.decreaseIndent();
    }
+311 −0
Original line number 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 Diff line number Diff line
@@ -19,6 +19,10 @@ package com.android.internal.telephony.data;
import android.annotation.NonNull;
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.
 */
@@ -30,7 +34,7 @@ public class DataUtils {
     * @return The network capability.
     */
    public static int getNetworkCapabilityFromString(@NonNull String capabilityString) {
        switch (capabilityString) {
        switch (capabilityString.toUpperCase(Locale.ROOT)) {
            case "MMS": return NetworkCapabilities.NET_CAPABILITY_MMS;
            case "SUPL": return NetworkCapabilities.NET_CAPABILITY_SUPL;
            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.
     *
     * @param netCap Network capability
     * @return Network capability in string format
     * @param netCap Network capability.
     * @return Network capability in string format.
     */
    public static String networkCapabilityToString(int netCap) {
        switch (netCap) {
@@ -98,4 +102,18 @@ public class DataUtils {
                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 Diff line number Diff line
@@ -86,6 +86,8 @@ import com.android.ims.ImsEcbm;
import com.android.ims.ImsManager;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
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.DataEnabledSettings;
import com.android.internal.telephony.dataconnection.DataThrottler;
@@ -198,6 +200,10 @@ public abstract class TelephonyTest {
    @Mock
    protected DcTracker mDcTracker;
    @Mock
    protected DataNetworkController mDataNetworkController;
    @Mock
    protected DataConfigManager mDataConfigManager;
    @Mock
    protected DisplayInfoController mDisplayInfoController;
    @Mock
    protected GsmCdmaCall mGsmCdmaCall;
@@ -572,6 +578,7 @@ public abstract class TelephonyTest {
        doReturn(mTransportManager).when(mPhone).getTransportManager();
        doReturn(mDataEnabledSettings).when(mPhone).getDataEnabledSettings();
        doReturn(mDcTracker).when(mPhone).getDcTracker(anyInt());
        doReturn(mDataNetworkController).when(mPhone).getDataNetworkController();
        doReturn(mCarrierPrivilegesTracker).when(mPhone).getCarrierPrivilegesTracker();
        doReturn(mSignalStrength).when(mPhone).getSignalStrength();
        doReturn(mVoiceCallSessionStats).when(mPhone).getVoiceCallSessionStats();
@@ -582,6 +589,7 @@ public abstract class TelephonyTest {
        doReturn(mLinkBandwidthEstimator).when(mPhone).getLinkBandwidthEstimator();
        doReturn(mCellIdentity).when(mPhone).getCurrentCellIdentity();
        doReturn(mCellLocation).when(mCellIdentity).asCellLocation();
        doReturn(mDataConfigManager).when(mDataNetworkController).getDataConfigManager();

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

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

Loading