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

Commit eae89cb4 authored by android-build-team Robot's avatar android-build-team Robot
Browse files

Snap for 5626642 from e88394a4 to qt-c2f2-release

Change-Id: I2de38b44986ce6c8c4995aed380fb12e5e001f46
parents 06e130a1 e88394a4
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -156,8 +156,7 @@ public interface PhoneInternalInterface {
            "carrierActionDisableMeteredApn";
    static final String REASON_CSS_INDICATOR_CHANGED = "cssIndicatorChanged";
    static final String REASON_RELEASED_BY_CONNECTIVITY_SERVICE = "releasedByConnectivityService";
    static final String REASON_APN_ADDED_TO_WHITELIST = "apnAddedToWhiteList";
    static final String REASON_APN_REMOVED_FROM_WHITELIST = "apnRemovedFromWhiteList";
    static final String REASON_DATA_ENABLED_OVERRIDE = "dataEnabledOverride";

    // Used for band mode selection methods
    static final int BM_UNSPECIFIED = RILConstants.BAND_MODE_UNSPECIFIED; // automatic
+3 −1
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import android.telephony.PreciseCallState;
import android.telephony.Rlog;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.util.LocalLog;

import com.android.internal.annotations.VisibleForTesting;
@@ -818,7 +819,8 @@ public class PhoneSwitcher extends Handler {
    // requests.
    private void updatePreferredDataPhoneId() {
        Phone voicePhone = findPhoneById(mPhoneIdInVoiceCall);
        if (voicePhone != null && voicePhone.isUserDataEnabled()) {
        if (voicePhone != null && voicePhone.getDataEnabledSettings().isDataEnabled(
                ApnSetting.TYPE_DEFAULT)) {
            // If a phone is in call and user enabled its mobile data, we
            // should switch internet connection to it. Because the other modem
            // will lose data connection anyway.
+65 −51
Original line number Diff line number Diff line
@@ -18,9 +18,9 @@ package com.android.internal.telephony;

import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT;
import static android.telephony.data.ApnSetting.TYPE_MMS;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.app.AppOpsManager;
@@ -41,7 +41,6 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
import android.telephony.RadioAccessFamily;
import android.telephony.Rlog;
@@ -50,7 +49,6 @@ import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.UiccAccessRule;
import android.telephony.UiccSlotInfo;
import android.telephony.data.ApnSetting;
import android.telephony.euicc.EuiccManager;
import android.text.TextUtils;
import android.util.LocalLog;
@@ -58,6 +56,7 @@ import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IccCardConstants.State;
import com.android.internal.telephony.dataconnection.DataEnabledOverride;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.telephony.uicc.UiccCard;
@@ -1729,11 +1728,11 @@ public class SubscriptionController extends ISub.Stub {

    public void syncGroupedSetting(int refSubId) {
        // Currently it only syncs allow MMS. Sync other settings as well if needed.
        int apnWhiteList = Integer.valueOf(getSubscriptionProperty(
                refSubId, SubscriptionManager.WHITE_LISTED_APN_DATA, mContext.getOpPackageName()));
        String dataEnabledOverrideRules = getSubscriptionProperty(
                refSubId, SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES);

        ContentValues value = new ContentValues(1);
        value.put(SubscriptionManager.WHITE_LISTED_APN_DATA, apnWhiteList);
        value.put(SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES, dataEnabledOverrideRules);
        databaseUpdateHelper(value, refSubId, true);
    }

@@ -2554,11 +2553,11 @@ public class SubscriptionController extends ISub.Stub {
    }

    /**
     * Store properties associated with SubscriptionInfo in database
     * Get properties associated with SubscriptionInfo from database
     *
     * @param subId Subscription Id of Subscription
     * @param propKey Column name in SubscriptionInfo database
     * @return Value associated with subId and propKey column in database
     * @hide
     */
    @Override
    public String getSubscriptionProperty(int subId, String propKey, String callingPackage) {
@@ -2566,14 +2565,29 @@ public class SubscriptionController extends ISub.Stub {
                mContext, subId, callingPackage, "getSubscriptionProperty")) {
            return null;
        }

        final long identity = Binder.clearCallingIdentity();
        try {
            return getSubscriptionProperty(subId, propKey);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    /**
     * Get properties associated with SubscriptionInfo from database. Note this is the version
     * without permission check for telephony internal use only.
     *
     * @param subId Subscription Id of Subscription
     * @param propKey Column name in SubscriptionInfo database
     * @return Value associated with subId and propKey column in database
     */
    public String getSubscriptionProperty(int subId, String propKey) {
        String resultValue = null;
        ContentResolver resolver = mContext.getContentResolver();
        Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
        try (Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
                new String[]{propKey},
                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
                new String[]{subId + ""}, null);

        try {
                new String[]{subId + ""}, null)) {
            if (cursor != null) {
                if (cursor.moveToFirst()) {
                    switch (propKey) {
@@ -2600,6 +2614,9 @@ public class SubscriptionController extends ISub.Stub {
                        case SubscriptionManager.WHITE_LISTED_APN_DATA:
                            resultValue = cursor.getInt(0) + "";
                            break;
                        case SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES:
                            resultValue = cursor.getString(0);
                            break;
                        default:
                            if(DBG) logd("Invalid column name");
                            break;
@@ -2610,11 +2627,8 @@ public class SubscriptionController extends ISub.Stub {
            } else {
                if(DBG) logd("Query failed");
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }

        if (DBG) logd("getSubscriptionProperty Query value = " + resultValue);
        return resultValue;
    }
@@ -3632,13 +3646,30 @@ public class SubscriptionController extends ISub.Stub {
        final long identity = Binder.clearCallingIdentity();
        try {
            validateSubId(subId);
            DataEnabledOverride dataEnabledOverride =
                    new DataEnabledOverride(getDataEnabledOverrideRules(subId));
            dataEnabledOverride.setAlwaysAllowMms(alwaysAllow);

            return setDataEnabledOverrideRules(subId, dataEnabledOverride.getRules());
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    /**
     * Set allowing mobile data during voice call.
     *
     * @param subId Subscription index
     * @param rules Data enabled override rules in string format. See {@link DataEnabledOverride}
     * for details.
     * @return {@code true} if settings changed, otherwise {@code false}.
     */
    public boolean setDataEnabledOverrideRules(int subId, @NonNull String rules) {
        if (DBG) logd("[setDataEnabledOverrideRules]+ rules:" + rules + " subId:" + subId);

        validateSubId(subId);
        ContentValues value = new ContentValues(1);
            int apnWhiteList = Integer.valueOf(getSubscriptionProperty(subId,
                    SubscriptionManager.WHITE_LISTED_APN_DATA, mContext.getOpPackageName()));
            apnWhiteList = alwaysAllow ? (apnWhiteList | TYPE_MMS) : (apnWhiteList & ~TYPE_MMS);
            value.put(SubscriptionManager.WHITE_LISTED_APN_DATA, apnWhiteList);
            if (DBG) logd("[setAlwaysAllowMmsData]- alwaysAllow:" + alwaysAllow + " set");
        value.put(SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES, rules);

        boolean result = databaseUpdateHelper(value, subId, true) > 0;

@@ -3646,37 +3677,20 @@ public class SubscriptionController extends ISub.Stub {
            // Refresh the Cache of Active Subscription Info List
            refreshCachedActiveSubscriptionInfoList();
            notifySubscriptionInfoChanged();
                Phone phone = PhoneFactory.getPhone(getPhoneId(subId));
                if (phone != null) {
                    phone.getDcTracker(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
                            .notifyApnWhiteListChange(TYPE_MMS, alwaysAllow);
                }
        }

        return result;
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    /**
     *  whether apnType is whitelisted. Being white listed means data connection is allowed
     *  even if user data is turned off.
     * Get data enabled override rules.
     *
     * @param subId Subscription index
     * @return Data enabled override rules in string
     */
    public boolean isApnWhiteListed(int subId, String callingPackage, int apnType) {
        return (getWhiteListedApnDataTypes(subId, callingPackage) & apnType) == apnType;
    }

    private @ApnSetting.ApnType int getWhiteListedApnDataTypes(int subId, String callingPackage) {
        String whiteListedApnData = getSubscriptionProperty(subId,
                SubscriptionManager.WHITE_LISTED_APN_DATA, callingPackage);

        try {
            return Integer.valueOf(whiteListedApnData);
        } catch (NumberFormatException e) {
            loge("[getWhiteListedApnDataTypes] couldn't parse apn data:" + whiteListedApnData);
        }

        return ApnSetting.TYPE_NONE;
    @NonNull
    public String getDataEnabledOverrideRules(int subId) {
        return TextUtils.emptyIfNull(getSubscriptionProperty(subId,
                SubscriptionManager.DATA_ENABLED_OVERRIDE_RULES));
    }
}
+3 −2
Original line number Diff line number Diff line
@@ -322,12 +322,13 @@ public class SubscriptionInfoUpdater extends Handler {

            case EVENT_REFRESH_EMBEDDED_SUBSCRIPTIONS:
                cardIds.add(msg.arg1);
                Runnable r = (Runnable) msg.obj;
                updateEmbeddedSubscriptions(cardIds, (hasChanges) -> {
                    if (hasChanges) {
                        SubscriptionController.getInstance().notifySubscriptionInfoChanged();
                    }
                    if (msg.obj != null) {
                        ((Runnable) msg.obj).run();
                    if (r != null) {
                        r.run();
                    }
                });
                break;
+406 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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.dataconnection;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.telephony.data.ApnSetting;
import android.telephony.data.ApnSetting.ApnType;
import android.text.TextUtils;
import android.util.ArrayMap;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.dataconnection.DataEnabledOverride.OverrideConditions.Condition;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * This class represents the rules for overriding data enabled settings in different conditions.
 * When data is disabled by the user, data can still be turned on temporarily when conditions
 * satisfy any rule here.
 */
public class DataEnabledOverride {

    private final Set<OverrideRule> mRules = new HashSet<>();

    /**
     * The rule for allowing data during voice call.
     */
    private static final OverrideRule OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL =
            new OverrideRule(ApnSetting.TYPE_ALL, OverrideConditions.CONDITION_IN_VOICE_CALL
                    | OverrideConditions.CONDITION_NON_DEFAULT);

    /**
     * The rule for always allowing mms. Without adding any condition to the rule, any condition can
     * satisfy this rule for mms.
     */
    private static final OverrideRule OVERRIDE_RULE_ALWAYS_ALLOW_MMS =
            new OverrideRule(ApnSetting.TYPE_MMS, OverrideConditions.CONDITION_UNCONDITIONALLY);

    /**
     * Data enabled override rule
     */
    private static class OverrideRule {
        /**
         * APN type of the rule. The rule is APN type specific. The override is applicable to the
         * specified APN type as well. For now we only support one APN type per rule. Can be
         * expanded to multiple APN types in the future.
         */
        private final @ApnType int mApnType;

        /** The required conditions for overriding */
        private final OverrideConditions mRequiredConditions;

        /**
         * Constructor
         *
         * @param rule The override rule string. For example, {@code mms=nonDefault} or
         * {@code default=voiceCall & nonDefault}
         */
        OverrideRule(@NonNull String rule) {
            String[] tokens = rule.trim().split("\\s*=\\s*");
            if (tokens.length != 2) {
                throw new IllegalArgumentException("Invalid data enabled override rule format: "
                        + rule);
            }

            if (TextUtils.isEmpty(tokens[0])) {
                throw new IllegalArgumentException("APN type can't be empty");
            }

            mApnType = ApnSetting.getApnTypesBitmaskFromString(tokens[0]);
            if (mApnType == ApnSetting.TYPE_NONE) {
                throw new IllegalArgumentException("Invalid APN type. Rule=" + rule);
            }

            mRequiredConditions = new OverrideConditions(tokens[1]);
        }

        /**
         * Constructor
         *
         * @param apnType APN type of the rule
         * @param requiredConditions The required conditions for the rule
         */
        private OverrideRule(int apnType, int requiredConditions) {
            mApnType = apnType;
            mRequiredConditions = new OverrideConditions(requiredConditions);
        }

        /**
         * Check if this rule can be satisfied by the given APN type and provided conditions.
         *
         * @param apnType APN type to check
         * @param providedConditions The provided conditions to check
         * @return {@code true} if satisfied
         */
        boolean isSatisfiedByConditions(@ApnType int apnType, @Condition int providedConditions) {
            return (mApnType == apnType || mApnType == ApnSetting.TYPE_ALL)
                    && mRequiredConditions.allMet(providedConditions);
        }

        @Override
        public String toString() {
            return ApnSetting.getApnTypeString(mApnType) + "=" + mRequiredConditions;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            OverrideRule that = (OverrideRule) o;
            return mApnType == that.mApnType
                    && Objects.equals(mRequiredConditions, that.mRequiredConditions);
        }

        @Override
        public int hashCode() {
            return Objects.hash(mApnType, mRequiredConditions);
        }
    }

    /**
     * Represent the conditions for overriding data enabled settings
     */
    static class OverrideConditions {
        // Possible values for data enabled override condition. Note these flags are bitmasks.
        /** Unconditionally override enabled settings */
        static final int CONDITION_UNCONDITIONALLY = 0;

        /** Enable data only on subscription that is not user selected default data subscription */
        static final int CONDITION_NON_DEFAULT = 1 << 0;

        /** Enable data only when device has ongoing voice call */
        static final int CONDITION_IN_VOICE_CALL = 1 << 1;

        /** Enable data unconditionally in string format */
        static final String CONDITION_UNCONDITIONALLY_STRING = "unconditionally";

        /** Enable data only on subscription that is not default in string format */
        static final String CONDITION_NON_DEFAULT_STRING = "nonDefault";

        /** Enable data only when device has ongoing voice call in string format */
        static final String CONDITION_VOICE_CALL_STRING = "inVoiceCall";

        /** @hide */
        @IntDef(flag = true, prefix = { "OVERRIDE_CONDITION_" }, value = {
                CONDITION_NON_DEFAULT,
                CONDITION_IN_VOICE_CALL,
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface Condition {}

        private static final Map<Integer, String> OVERRIDE_CONDITION_INT_MAP = new ArrayMap<>();
        private static final Map<String, Integer> OVERRIDE_CONDITION_STRING_MAP = new ArrayMap<>();

        static {
            OVERRIDE_CONDITION_INT_MAP.put(CONDITION_NON_DEFAULT,
                    CONDITION_NON_DEFAULT_STRING);
            OVERRIDE_CONDITION_INT_MAP.put(CONDITION_IN_VOICE_CALL,
                    CONDITION_VOICE_CALL_STRING);

            OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_UNCONDITIONALLY_STRING,
                    CONDITION_UNCONDITIONALLY);
            OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_NON_DEFAULT_STRING,
                    CONDITION_NON_DEFAULT);
            OVERRIDE_CONDITION_STRING_MAP.put(CONDITION_VOICE_CALL_STRING,
                    CONDITION_IN_VOICE_CALL);
        }

        private final @Condition int mConditions;

        /**
         * Conditions for overriding data enabled setting
         *
         * @param conditions Conditions in string format
         */
        OverrideConditions(@NonNull String conditions) {
            mConditions = getBitmaskFromString(conditions);
        }

        /**
         * Conditions for overriding data enabled setting
         *
         * @param conditions Conditions in bitmask
         */
        OverrideConditions(@Condition int conditions) {
            mConditions = conditions;
        }

        private static String getStringFromBitmask(@Condition int conditions) {
            if (conditions == CONDITION_UNCONDITIONALLY) {
                return CONDITION_UNCONDITIONALLY_STRING;
            }
            List<String> conditionsStrings = new ArrayList<>();
            for (Integer condition : OVERRIDE_CONDITION_INT_MAP.keySet()) {
                if ((conditions & condition) == condition) {
                    conditionsStrings.add(OVERRIDE_CONDITION_INT_MAP.get(condition));
                }
            }
            return TextUtils.join("&", conditionsStrings);
        }

        private static @Condition int getBitmaskFromString(@NonNull String str) {
            if (TextUtils.isEmpty(str)) {
                throw new IllegalArgumentException("Empty rule string");
            }

            String[] conditionStrings = str.trim().split("\\s*&\\s*");
            int bitmask = 0;

            for (String conditionStr : conditionStrings) {
                if (!TextUtils.isEmpty(conditionStr)) {
                    if (!OVERRIDE_CONDITION_STRING_MAP.containsKey(conditionStr)) {
                        throw new IllegalArgumentException("Invalid conditions: " + str);
                    }
                    bitmask |= OVERRIDE_CONDITION_STRING_MAP.get(conditionStr);
                }
            }

            return bitmask;
        }

        /**
         * Check if provided conditions can meet all conditions in the rule.
         *
         * @param providedConditions The provided conditions
         * @return {@code true} if all conditions are met.
         */
        boolean allMet(@Condition int providedConditions) {
            return (providedConditions & mConditions) == mConditions;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            OverrideConditions that = (OverrideConditions) o;
            return mConditions == that.mConditions;
        }

        @Override
        public int hashCode() {
            return Objects.hash(mConditions);
        }

        @Override
        public String toString() {
            return getStringFromBitmask(mConditions);
        }
    }

    /**
     * Constructor
     *
     * @param rules Data enabled override rules
     */
    public DataEnabledOverride(@NonNull String rules) {
        updateRules(rules);
    }

    /**
     * Update the data enabled override rules.
     *
     * @param newRules New override rules
     */
    @VisibleForTesting
    public void updateRules(@NonNull String newRules) {
        mRules.clear();
        String[] rulesString = newRules.trim().split("\\s*,\\s*");
        for (String rule : rulesString) {
            if (!TextUtils.isEmpty(rule)) {
                mRules.add(new OverrideRule(rule));
            }
        }
    }

    /**
     * Set always allowing MMS
     *
     * @param allow {@code true} if always allowing, otherwise {@code false}.
     */
    public void setAlwaysAllowMms(boolean allow) {
        if (allow) {
            mRules.add(OVERRIDE_RULE_ALWAYS_ALLOW_MMS);
        } else {
            mRules.remove(OVERRIDE_RULE_ALWAYS_ALLOW_MMS);
        }
    }

    /**
     * Set allowing mobile data during voice call.
     *
     * @param allow {@code true} if allowing using data during voice call, {@code false} if
     * disallowed.
     */
    public void setDataAllowedInVoiceCall(boolean allow) {
        if (allow) {
            mRules.add(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL);
        } else {
            mRules.remove(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL);
        }
    }

    /**
     * Check if data is allowed during voice call.
     *
     * @return {@code true} if data is allowed during voice call.
     */
    public boolean isDataAllowedInVoiceCall() {
        return mRules.contains(OVERRIDE_RULE_ALLOW_DATA_DURING_VOICE_CALL);
    }

    private boolean canSatisfyAnyRule(@ApnType int apnType,
                                      @Condition int providedConditions) {
        for (OverrideRule rule : mRules) {
            if (rule.isSatisfiedByConditions(apnType, providedConditions)) {
                return true;
            }
        }
        return false;
    }

    private @Condition int getCurrentConditions(Phone phone) {
        int conditions = 0;

        if (phone != null) {
            // Check if the device is on voice call
            if (phone.getCallTracker().getState() != PhoneConstants.State.IDLE) {
                conditions |= OverrideConditions.CONDITION_IN_VOICE_CALL;
            }

            if (phone.getSubId() != SubscriptionController.getInstance().getDefaultDataSubId()) {
                conditions |= OverrideConditions.CONDITION_NON_DEFAULT;
            }
        }

        return conditions;
    }

    /**
     * Check for given APN type if we should enable data.
     *
     * @param phone Phone object
     * @param apnType APN type
     * @return {@code true} if data should be enabled for the current condition.
     */
    public boolean shouldOverrideDataEnabledSettings(Phone phone, @ApnType int apnType) {
        return canSatisfyAnyRule(apnType, getCurrentConditions(phone));
    }

    /**
     * Get data enabled override rules.
     *
     * @return Get data enabled override rules in string format
     */
    @NonNull
    public String getRules() {
        List<String> ruleStrings = new ArrayList<>();
        for (OverrideRule rule : mRules) {
            ruleStrings.add(rule.toString());
        }
        return TextUtils.join(",", ruleStrings);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        DataEnabledOverride that = (DataEnabledOverride) o;
        return mRules.equals(that.mRules);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mRules);
    }

    @Override
    public String toString() {
        return "DataEnabledOverride: [rules=\"" + getRules() + "\"]";
    }
}
Loading