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

Commit 558016e7 authored by rambowang's avatar rambowang
Browse files

Introduce APIs to support Data Only Cellular Service

For carriers, introduce a carrier config to specify the preferred
cellular services the subscription supports.

For applications, introduce a SubscriptionInfo API to query the cellular
service capabilities the subscription supports.

Bug: 296097429
Test: atest FrameworksTelephonyTests
Change-Id: I5f84ceaaff4a250d54b19c96031973353b6b1e72
parent 216c3446
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -43277,6 +43277,7 @@ package android.telephony {
    field public static final String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
    field public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
    field public static final String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
    field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY = "cellular_service_capabilities_int_array";
    field public static final String KEY_CELLULAR_USAGE_SETTING_INT = "cellular_usage_setting_int";
    field public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL = "check_pricing_with_carrier_data_roaming_bool";
    field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool";
@@ -45271,6 +45272,7 @@ package android.telephony {
    method @Nullable public String getMncString();
    method @Deprecated public String getNumber();
    method public int getPortIndex();
    method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") @NonNull public java.util.Set<java.lang.Integer> getServiceCapabilities();
    method public int getSimSlotIndex();
    method public int getSubscriptionId();
    method public int getSubscriptionType();
@@ -45352,6 +45354,9 @@ package android.telephony {
    field public static final int PHONE_NUMBER_SOURCE_CARRIER = 2; // 0x2
    field public static final int PHONE_NUMBER_SOURCE_IMS = 3; // 0x3
    field public static final int PHONE_NUMBER_SOURCE_UICC = 1; // 0x1
    field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_DATA = 3; // 0x3
    field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_SMS = 2; // 0x2
    field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_VOICE = 1; // 0x1
    field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0
    field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1
    field public static final int USAGE_SETTING_DATA_CENTRIC = 2; // 0x2
@@ -45600,6 +45605,8 @@ package android.telephony {
    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataEnabled();
    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataEnabledForReason(int);
    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataRoamingEnabled();
    method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public boolean isDeviceSmsCapable();
    method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public boolean isDeviceVoiceCapable();
    method public boolean isEmergencyNumber(@NonNull String);
    method public boolean isHearingAidCompatibilitySupported();
    method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isManualNetworkSelectionAllowed();
@@ -45609,9 +45616,9 @@ package android.telephony {
    method @RequiresPermission(android.Manifest.permission.READ_BASIC_PHONE_STATE) public boolean isPremiumCapabilityAvailableForPurchase(int);
    method public boolean isRadioInterfaceCapabilitySupported(@NonNull String);
    method public boolean isRttSupported();
    method public boolean isSmsCapable();
    method @Deprecated public boolean isSmsCapable();
    method @Deprecated public boolean isTtyModeSupported();
    method public boolean isVoiceCapable();
    method @Deprecated public boolean isVoiceCapable();
    method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
    method public boolean isWorldPhone();
    method @Deprecated public void listen(android.telephony.PhoneStateListener, int);
+9 −1
Original line number Diff line number Diff line
@@ -4940,6 +4940,13 @@ public final class Telephony {
         */
        public static final String COLUMN_IS_NTN = "is_ntn";

        /**
         * TelephonyProvider column name to indicate the service capability bitmasks.
         *
         * @hide
         */
        public static final String COLUMN_SERVICE_CAPABILITIES = "service_capabilities";

        /** All columns in {@link SimInfo} table. */
        private static final List<String> ALL_COLUMNS = List.of(
                COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID,
@@ -5011,7 +5018,8 @@ public final class Telephony {
                COLUMN_USER_HANDLE,
                COLUMN_SATELLITE_ENABLED,
                COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER,
                COLUMN_IS_NTN
                COLUMN_IS_NTN,
                COLUMN_SERVICE_CAPABILITIES
        );

        /**
+44 −0
Original line number Diff line number Diff line
@@ -10044,6 +10044,49 @@ public class CarrierConfigManager {
    public static final String KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_BUNDLE =
            "auto_data_switch_rat_signal_score_string_bundle";
    // TODO(b/316183370): replace @code with @link in javadoc after feature is released
    /**
     * An array of cellular services supported by a subscription.
     *
     * <p>Permissible values include:
     * <ul>
     *   <li>{@code SubscriptionManager#SERVICE_CAPABILITY_VOICE} for voice services</li>
     *   <li>{@code SubscriptionManager#SERVICE_CAPABILITY_SMS} for SMS services</li>
     *   <li>{@code SubscriptionManager#SERVICE_CAPABILITY_DATA} for data services</li>
     * </ul>
     *
     * <p>Carrier-specific factors may influence how these services are supported. Therefore,
     * modifying this carrier configuration might not always enable the specified services. These
     * capability bitmasks should be considered as indicators of a carrier's preferred services
     * to enhance user experience, rather than as absolute platform guarantees.
     *
     * <p>Device-level service capabilities, defined by
     * {@code TelephonyManager#isDeviceVoiceCapable} and
     * {@code TelephonyManager#isDeviceSmsCapable}, take precedence over these subscription-level
     * settings. For instance, a device where {@code TelephonyManager#isDeviceVoiceCapable} returns
     * false may not be able to make voice calls, even if subscribed to a service marked as
     * voice-capable.
     *
     * <p>To determine a subscription's cellular service capabilities, use
     * {@code SubscriptionInfo#getServiceCapabilities()}. To track changes in services, register
     * a {@link SubscriptionManager.OnSubscriptionsChangedListener} and invoke the
     * same method in its callback.
     *
     * <p>Emergency service availability may not depend on the cellular service capabilities.
     * For example, emergency calls might be possible on a subscription even if it lacks
     * {@code SubscriptionManager#SERVICE_CAPABILITY_VOICE}.
     *
     * <p>If unset, the default value is “[1, 2, 3]” (supports all cellular services).
     *
     * @see TelephonyManager#isDeviceVoiceCapable
     * @see TelephonyManager#isDeviceSmsCapable
     * @see SubscriptionInfo#getServiceCapabilities()
     * @see SubscriptionManager.OnSubscriptionsChangedListener
     */
    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
    public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY =
            "cellular_service_capabilities_int_array";
    /** The default value for every variable. */
    private static final PersistableBundle sDefaults;
@@ -10830,6 +10873,7 @@ public class CarrierConfigManager {
                new boolean[] {false, false, true, false, false});
        sDefaults.putStringArray(KEY_CARRIER_SERVICE_NAME_STRING_ARRAY, new String[0]);
        sDefaults.putStringArray(KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY, new String[0]);
        sDefaults.putIntArray(KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY, new int[]{1, 2, 3});
    }
    /**
+86 −2
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * A Parcelable class for Subscription Information.
@@ -261,6 +262,11 @@ public class SubscriptionInfo implements Parcelable {
     */
    private final boolean mIsOnlyNonTerrestrialNetwork;

    /**
     * The service capabilities (in the form of bitmask combination) the subscription supports.
     */
    private final int mServiceCapabilities;

    /**
     * @hide
     *
@@ -386,6 +392,7 @@ public class SubscriptionInfo implements Parcelable {
        this.mPortIndex = portIndex;
        this.mUsageSetting = usageSetting;
        this.mIsOnlyNonTerrestrialNetwork = false;
        this.mServiceCapabilities = 0;
    }

    /**
@@ -425,6 +432,7 @@ public class SubscriptionInfo implements Parcelable {
        this.mPortIndex = builder.mPortIndex;
        this.mUsageSetting = builder.mUsageSetting;
        this.mIsOnlyNonTerrestrialNetwork = builder.mIsOnlyNonTerrestrialNetwork;
        this.mServiceCapabilities = builder.mServiceCapabilities;
    }

    /**
@@ -882,6 +890,44 @@ public class SubscriptionInfo implements Parcelable {
        return mIsOnlyNonTerrestrialNetwork;
    }

    // TODO(b/316183370): replace @code with @link in javadoc after feature is released
    /**
     * Retrieves the service capabilities for the current subscription.
     *
     * <p>These capabilities are hint to system components and applications, allowing them to
     * enhance user experience. For instance, a Dialer application can inform the user that the
     * current subscription is incapable of making voice calls if the voice service is not
     * available.
     *
     * <p>Correct usage of these service capabilities must also consider the device's overall
     * service capabilities. For example, even if the subscription supports voice calls, a voice
     * call might not be feasible on a device that only supports data services. To determine the
     * device's capabilities for voice and SMS services, refer to
     * {@code TelephonyManager#isDeviceVoiceCapable()} and
     * {@code TelephonyManager#isDeviceSmsCapable()}.
     *
     * <p>Emergency service availability may not directly correlate with the subscription or
     * device's general service capabilities. In some cases, emergency calls might be possible
     * even if the subscription or device does not typically support voice services.
     *
     * @return A set of integer representing the subscription's service capabilities,
     * defined by {@code SubscriptionManager#SERVICE_CAPABILITY_VOICE},
     * {@code SubscriptionManager#SERVICE_CAPABILITY_SMS}
     * and {@code SubscriptionManager#SERVICE_CAPABILITY_DATA}.
     *
     * @see TelephonyManager#isDeviceVoiceCapable()
     * @see TelephonyManager#isDeviceSmsCapable()
     * @see CarrierConfigManager#KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY
     * @see SubscriptionManager#SERVICE_CAPABILITY_VOICE
     * @see SubscriptionManager#SERVICE_CAPABILITY_SMS
     * @see SubscriptionManager#SERVICE_CAPABILITY_DATA
     */
    @NonNull
    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
    public @SubscriptionManager.ServiceCapability Set<Integer> getServiceCapabilities() {
        return SubscriptionManager.getServiceCapabilitiesSet(mServiceCapabilities);
    }

    @NonNull
    public static final Parcelable.Creator<SubscriptionInfo> CREATOR =
            new Parcelable.Creator<SubscriptionInfo>() {
@@ -919,6 +965,8 @@ public class SubscriptionInfo implements Parcelable {
                    .setUiccApplicationsEnabled(source.readBoolean())
                    .setUsageSetting(source.readInt())
                    .setOnlyNonTerrestrialNetwork(source.readBoolean())
                    .setServiceCapabilities(
                            SubscriptionManager.getServiceCapabilitiesSet(source.readInt()))
                    .build();
        }

@@ -961,6 +1009,7 @@ public class SubscriptionInfo implements Parcelable {
        dest.writeBoolean(mAreUiccApplicationsEnabled);
        dest.writeInt(mUsageSetting);
        dest.writeBoolean(mIsOnlyNonTerrestrialNetwork);
        dest.writeInt(mServiceCapabilities);
    }

    @Override
@@ -1024,6 +1073,8 @@ public class SubscriptionInfo implements Parcelable {
                + " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled
                + " usageSetting=" + SubscriptionManager.usageSettingToString(mUsageSetting)
                + " isOnlyNonTerrestrialNetwork=" + mIsOnlyNonTerrestrialNetwork
                + " serviceCapabilities=" + SubscriptionManager.getServiceCapabilitiesSet(
                mServiceCapabilities).toString()
                + "]";
    }

@@ -1049,7 +1100,8 @@ public class SubscriptionInfo implements Parcelable {
                that.mNativeAccessRules) && Arrays.equals(mCarrierConfigAccessRules,
                that.mCarrierConfigAccessRules) && Objects.equals(mGroupUuid, that.mGroupUuid)
                && mCountryIso.equals(that.mCountryIso) && mGroupOwner.equals(that.mGroupOwner)
                && mIsOnlyNonTerrestrialNetwork == that.mIsOnlyNonTerrestrialNetwork;
                && mIsOnlyNonTerrestrialNetwork == that.mIsOnlyNonTerrestrialNetwork
                && mServiceCapabilities == that.mServiceCapabilities;
    }

    @Override
@@ -1058,7 +1110,7 @@ public class SubscriptionInfo implements Parcelable {
                mDisplayNameSource, mIconTint, mNumber, mDataRoaming, mMcc, mMnc, mIsEmbedded,
                mCardString, mIsOpportunistic, mGroupUuid, mCountryIso, mCarrierId, mProfileClass,
                mType, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex, mUsageSetting, mCardId,
                mIsGroupDisabled, mIsOnlyNonTerrestrialNetwork);
                mIsGroupDisabled, mIsOnlyNonTerrestrialNetwork, mServiceCapabilities);
        result = 31 * result + Arrays.hashCode(mEhplmns);
        result = 31 * result + Arrays.hashCode(mHplmns);
        result = 31 * result + Arrays.hashCode(mNativeAccessRules);
@@ -1262,6 +1314,11 @@ public class SubscriptionInfo implements Parcelable {
         */
        private boolean mIsOnlyNonTerrestrialNetwork = false;

        /**
         * Service capabilities bitmasks the subscription supports.
         */
        private int mServiceCapabilities = 0;

        /**
         * Default constructor.
         */
@@ -1305,6 +1362,7 @@ public class SubscriptionInfo implements Parcelable {
            mPortIndex = info.mPortIndex;
            mUsageSetting = info.mUsageSetting;
            mIsOnlyNonTerrestrialNetwork = info.mIsOnlyNonTerrestrialNetwork;
            mServiceCapabilities = info.mServiceCapabilities;
        }

        /**
@@ -1702,6 +1760,32 @@ public class SubscriptionInfo implements Parcelable {
            return this;
        }

        /**
         * Set the service capabilities that the subscription supports.
         *
         * @param capabilities Bitmask combination of SubscriptionManager
         *                     .SERVICE_CAPABILITY_XXX.
         * @return The builder.
         *
         * @throws IllegalArgumentException when any capability is not supported.
         */
        @NonNull
        @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
        public Builder setServiceCapabilities(
                @NonNull @SubscriptionManager.ServiceCapability Set<Integer> capabilities) {
            int combinedCapabilities = 0;
            for (int capability : capabilities) {
                if (capability < SubscriptionManager.SERVICE_CAPABILITY_VOICE
                        || capability > SubscriptionManager.SERVICE_CAPABILITY_MAX) {
                    throw new IllegalArgumentException(
                            "Invalid service capability value: " + capability);
                }
                combinedCapabilities |= SubscriptionManager.serviceCapabilityToBitmask(capability);
            }
            mServiceCapabilities = combinedCapabilities;
            return this;
        }

        /**
         * Build the {@link SubscriptionInfo}.
         *
+124 −0
Original line number Diff line number Diff line
@@ -88,10 +88,12 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -1138,6 +1140,14 @@ public class SubscriptionManager {
     */
    public static final String IS_NTN = SimInfo.COLUMN_IS_NTN;

    /**
     * TelephonyProvider column name to identify service capabilities.
     * Disabled by default.
     * <P>Type: INTEGER (int)</P>
     * @hide
     */
    public static final String SERVICE_CAPABILITIES = SimInfo.COLUMN_SERVICE_CAPABILITIES;

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = {"USAGE_SETTING_"},
@@ -1347,6 +1357,86 @@ public class SubscriptionManager {
            })
    public @interface PhoneNumberSource {}

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = {"SERVICE_CAPABILITY"},
            value = {
                    SERVICE_CAPABILITY_VOICE,
                    SERVICE_CAPABILITY_SMS,
                    SERVICE_CAPABILITY_DATA,
            })
    public @interface ServiceCapability {
    }

    /**
     * Represents a value indicating the voice calling capabilities of a subscription.
     *
     * <p>This value identifies whether the subscription supports various voice calling services.
     * These services can include circuit-switched (CS) calling, packet-switched (PS) IMS (IP
     * Multimedia Subsystem) calling, and over-the-top (OTT) calling options.
     *
     * <p>Note: The availability of emergency calling services is not solely dependent on this
     * voice capability. Emergency services may be accessible even if the subscription lacks
     * standard voice capabilities. However, the device's ability to support emergency calls
     * can be influenced by its inherent voice capabilities, as determined by
     * {@link TelephonyManager#isDeviceVoiceCapable()}.
     *
     * @see TelephonyManager#isDeviceVoiceCapable()
     */
    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
    public static final int SERVICE_CAPABILITY_VOICE = 1;

    /**
     * Represents a value indicating the SMS capabilities of a subscription.
     *
     * <p>This value identifies whether the subscription supports various sms services.
     * These services can include circuit-switched (CS) SMS, packet-switched (PS) IMS (IP
     * Multimedia Subsystem) SMS, and over-the-top (OTT) SMS options.
     *
     * <p>Note: The availability of emergency SMS services is not solely dependent on this
     * sms capability. Emergency services may be accessible even if the subscription lacks
     * standard sms capabilities. However, the device's ability to support emergency sms
     * can be influenced by its inherent sms capabilities, as determined by
     * {@link TelephonyManager#isDeviceSmsCapable()}.
     *
     * @see TelephonyManager#isDeviceSmsCapable()
     */
    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
    public static final int SERVICE_CAPABILITY_SMS = 2;

    /**
     * Represents a value indicating the data calling capabilities of a subscription.
     */
    @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
    public static final int SERVICE_CAPABILITY_DATA = 3;

    /**
     * Maximum value of service capabilities supported so far.
     * @hide
     */
    public static final int SERVICE_CAPABILITY_MAX = SERVICE_CAPABILITY_DATA;

    /**
     * Bitmask for {@code SERVICE_CAPABILITY_VOICE}.
     * @hide
     */
    public static final int SERVICE_CAPABILITY_VOICE_BITMASK =
            serviceCapabilityToBitmask(SERVICE_CAPABILITY_VOICE);

    /**
     * Bitmask for {@code SERVICE_CAPABILITY_SMS}.
     * @hide
     */
    public static final int SERVICE_CAPABILITY_SMS_BITMASK =
            serviceCapabilityToBitmask(SERVICE_CAPABILITY_SMS);

    /**
     * Bitmask for {@code SERVICE_CAPABILITY_DATA}.
     * @hide
     */
    public static final int SERVICE_CAPABILITY_DATA_BITMASK =
            serviceCapabilityToBitmask(SERVICE_CAPABILITY_DATA);

    private final Context mContext;

    /**
@@ -4484,4 +4574,38 @@ public class SubscriptionManager {
        }
        return new ArrayList<>();
    }

    /**
     * @return the bitmasks combination of all service capabilities.
     * @hide
     */
    public static int getAllServiceCapabilityBitmasks() {
        return SERVICE_CAPABILITY_VOICE_BITMASK | SERVICE_CAPABILITY_SMS_BITMASK
                | SERVICE_CAPABILITY_DATA_BITMASK;
    }

    /**
     * @return The set of service capability from a bitmask combined one.
     * @hide
     */
    @NonNull
    @ServiceCapability
    public static Set<Integer> getServiceCapabilitiesSet(int combinedServiceCapabilities) {
        Set<Integer> capabilities = new HashSet<>();
        for (int i = SERVICE_CAPABILITY_VOICE; i <= SERVICE_CAPABILITY_MAX; i++) {
            final int capabilityBitmask = serviceCapabilityToBitmask(i);
            if ((combinedServiceCapabilities & capabilityBitmask) == capabilityBitmask) {
                capabilities.add(i);
            }
        }
        return Collections.unmodifiableSet(capabilities);
    }

    /**
     * @return The service capability bitmask from a {@link ServiceCapability} value.
     * @hide
     */
    public static int serviceCapabilityToBitmask(@ServiceCapability int capability) {
        return 1 << (capability - 1);
    }
}
Loading