Update subscription services to support service capabilities
The CL mainly implements the changes below to support data-only service:
- Introduce a new internal field in SubInfoInternal to accommodate
the new service capability bitmasks in telephony.SimInfo table.
- Update SubscriptionDatabaseManager to get/set the db value.
- Modify SubscriptionManagerService to update service capabilities
value when the carrier config value has been overridden.
Bug: 296097429
Test: atest FrameworksTelephonyTests
Test: Basic functionality tests with flag on and off.
Change-Id: I5b91b3ab093e4b816382147790ef7a053f074533
diff --git a/flags/subscription.aconfig b/flags/subscription.aconfig
index dece11a..f7c2a62 100644
--- a/flags/subscription.aconfig
+++ b/flags/subscription.aconfig
@@ -12,4 +12,11 @@
namespace: "telephony"
description: "Enabled flag means subscriptions enforce filtering result base on calling user handle. It marks the telephony completion of user filtering."
bug: "296076674"
+}
+
+flag {
+ name: "data_only_cellular_service"
+ namespace: "telephony"
+ description: "Supports customized cellular service capabilities per subscription."
+ bug: "296097429"
}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
index 7b927f2..0756650 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionDatabaseManager.java
@@ -281,7 +281,10 @@
SubscriptionInfoInternal::getSatelliteAttachEnabledForCarrier),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_IS_NTN,
- SubscriptionInfoInternal::getOnlyNonTerrestrialNetwork)
+ SubscriptionInfoInternal::getOnlyNonTerrestrialNetwork),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_SERVICE_CAPABILITIES,
+ SubscriptionInfoInternal::getServiceCapabilities)
);
/**
@@ -412,7 +415,10 @@
SubscriptionDatabaseManager::setSatelliteAttachEnabledForCarrier),
new AbstractMap.SimpleImmutableEntry<>(
SimInfo.COLUMN_IS_NTN,
- SubscriptionDatabaseManager::setNtn)
+ SubscriptionDatabaseManager::setNtn),
+ new AbstractMap.SimpleImmutableEntry<>(
+ SimInfo.COLUMN_SERVICE_CAPABILITIES,
+ SubscriptionDatabaseManager::setServiceCapabilities)
);
/**
@@ -2074,6 +2080,19 @@
}
/**
+ * Set service capabilities the subscription support.
+ * @param subId Subscription id.
+ * @param capabilities Service capabilities bitmasks
+ */
+ public void setServiceCapabilities(int subId, int capabilities) {
+ if (!mFeatureFlags.dataOnlyCellularService()) {
+ return;
+ }
+ writeDatabaseAndCacheHelper(subId, SimInfo.COLUMN_SERVICE_CAPABILITIES,
+ capabilities, SubscriptionInfoInternal.Builder::setServiceCapabilities);
+ }
+
+ /**
* Reload the database from content provider to the cache. This must be a synchronous operation
* to prevent cache/database out-of-sync. Callers should be cautious to call this method because
* it might take longer time to complete.
@@ -2302,7 +2321,10 @@
SimInfo.COLUMN_SATELLITE_ENABLED)))
.setSatelliteAttachEnabledForCarrier(cursor.getInt(
cursor.getColumnIndexOrThrow(
- SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER)));
+ SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER)))
+ .setServiceCapabilities(cursor.getInt(
+ cursor.getColumnIndexOrThrow(
+ SimInfo.COLUMN_SERVICE_CAPABILITIES)));
if (mFeatureFlags.oemEnabledSatelliteFlag()) {
builder.setOnlyNonTerrestrialNetwork(cursor.getInt(cursor.getColumnIndexOrThrow(
SimInfo.COLUMN_IS_NTN)));
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
index f08a659..c6fc23d 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java
@@ -469,6 +469,11 @@
private final boolean mIsGroupDisabled;
/**
+ * Service capabilities (in the form of bitmask combination) the subscription supports.
+ */
+ private final int mServiceCapabilities;
+
+ /**
* Constructor from builder.
*
* @param builder Builder of {@link SubscriptionInfoInternal}.
@@ -543,6 +548,7 @@
// Below are the fields that do not exist in the SimInfo table.
this.mCardId = builder.mCardId;
this.mIsGroupDisabled = builder.mIsGroupDisabled;
+ this.mServiceCapabilities = builder.mServiceCapabilities;
}
/**
@@ -1193,6 +1199,13 @@
return !isOpportunistic() || TextUtils.isEmpty(mGroupUuid);
}
+ /**
+ * Return the service capabilities bitmasks the subscription supports.
+ */
+ public int getServiceCapabilities() {
+ return mServiceCapabilities;
+ }
+
/** @return converted {@link SubscriptionInfo}. */
@NonNull
public SubscriptionInfo toSubscriptionInfo() {
@@ -1229,6 +1242,8 @@
.setPortIndex(mPortIndex)
.setUsageSetting(mUsageSetting)
.setOnlyNonTerrestrialNetwork(mIsOnlyNonTerrestrialNetwork == 1)
+ .setServiceCapabilities(
+ SubscriptionManager.getServiceCapabilitiesSet(mServiceCapabilities))
.build();
}
@@ -1288,6 +1303,7 @@
+ " satellite_attach_enabled_for_carrier=" + mIsSatelliteAttachEnabledForCarrier
+ " getOnlyNonTerrestrialNetwork=" + mIsOnlyNonTerrestrialNetwork
+ " isGroupDisabled=" + mIsGroupDisabled
+ + " serviceCapabilities=" + mServiceCapabilities
+ "]";
}
@@ -1344,7 +1360,8 @@
that.mDeviceToDeviceStatusSharingContacts) && mNumberFromCarrier.equals(
that.mNumberFromCarrier) && mNumberFromIms.equals(that.mNumberFromIms)
&& mIsSatelliteAttachEnabledForCarrier == that.mIsSatelliteAttachEnabledForCarrier
- && mIsOnlyNonTerrestrialNetwork == that.mIsOnlyNonTerrestrialNetwork;
+ && mIsOnlyNonTerrestrialNetwork == that.mIsOnlyNonTerrestrialNetwork
+ && mServiceCapabilities == that.mServiceCapabilities;
}
@Override
@@ -1366,7 +1383,8 @@
mNumberFromCarrier,
mNumberFromIms, mPortIndex, mUsageSetting, mLastUsedTPMessageReference, mUserId,
mIsSatelliteEnabled, mCardId, mIsGroupDisabled,
- mIsSatelliteAttachEnabledForCarrier, mIsOnlyNonTerrestrialNetwork);
+ mIsSatelliteAttachEnabledForCarrier, mIsOnlyNonTerrestrialNetwork,
+ mServiceCapabilities);
result = 31 * result + Arrays.hashCode(mNativeAccessRules);
result = 31 * result + Arrays.hashCode(mCarrierConfigAccessRules);
result = 31 * result + Arrays.hashCode(mRcsConfig);
@@ -1754,6 +1772,11 @@
private boolean mIsGroupDisabled;
/**
+ * Service capabilities the subscription supports
+ */
+ private int mServiceCapabilities;
+
+ /**
* Default constructor.
*/
public Builder() {
@@ -1831,6 +1854,7 @@
// Below are the fields that do not exist in the SimInfo table.
mCardId = info.mCardId;
mIsGroupDisabled = info.mIsGroupDisabled;
+ mServiceCapabilities = info.mServiceCapabilities;
}
/**
@@ -2753,6 +2777,16 @@
}
/**
+ * Set the service capabilities the subscription supports.
+ * @param capabilities Cellular service capabilities bitmasks
+ * @return The builder
+ */
+ public Builder setServiceCapabilities(int capabilities) {
+ mServiceCapabilities = capabilities;
+ return this;
+ }
+
+ /**
* Build the {@link SubscriptionInfoInternal}.
*
* @return The {@link SubscriptionInfoInternal} instance.
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
index 3dc5cab..cb0ece8 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
@@ -1700,6 +1700,39 @@
preferredUsageSetting)
+ " newSetting=" + SubscriptionManager.usageSettingToString(newUsageSetting));
}
+
+ if (mFeatureFlags.dataOnlyCellularService()) {
+ final int[] servicesFromCarrierConfig =
+ config.getIntArray(
+ CarrierConfigManager.KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY);
+ int serviceBitmasks = 0;
+ boolean allServicesAreValid = true;
+ // Check if all services from carrier config are valid before setting to db
+ if (servicesFromCarrierConfig == null) {
+ allServicesAreValid = false;
+ } else {
+ for (int service : servicesFromCarrierConfig) {
+ if (service < SubscriptionManager.SERVICE_CAPABILITY_VOICE
+ || service > SubscriptionManager.SERVICE_CAPABILITY_MAX) {
+ allServicesAreValid = false;
+ break;
+ } else {
+ serviceBitmasks |= SubscriptionManager.serviceCapabilityToBitmask(service);
+ }
+ }
+ }
+ // In case we get invalid service override, fall back to default value.
+ // DO NOT throw exception which will crash phone process.
+ if (!allServicesAreValid) {
+ serviceBitmasks = SubscriptionManager.getAllServiceCapabilityBitmasks();
+ }
+
+ if (serviceBitmasks != subInfo.getServiceCapabilities()) {
+ log("updateSubscriptionByCarrierConfig: serviceCapabilities updated from "
+ + subInfo.getServiceCapabilities() + " to " + serviceBitmasks);
+ mSubscriptionDatabaseManager.setServiceCapabilities(subId, serviceBitmasks);
+ }
+ }
}
/**
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
index 2692057..c4b957e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
@@ -131,7 +131,8 @@
+ Telephony.SimInfo.COLUMN_SATELLITE_ENABLED + " INTEGER DEFAULT 0,"
+ Telephony.SimInfo.COLUMN_SATELLITE_ATTACH_ENABLED_FOR_CARRIER
+ " INTEGER DEFAULT 0, "
- + Telephony.SimInfo.COLUMN_IS_NTN + " INTEGER DEFAULT 0"
+ + Telephony.SimInfo.COLUMN_IS_NTN + " INTEGER DEFAULT 0,"
+ + Telephony.SimInfo.COLUMN_SERVICE_CAPABILITIES + " INTEGER DEFAULT 7"
+ ");";
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
index 9fa88e2..4afdfe9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
@@ -17,7 +17,10 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
import android.os.Parcel;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -25,12 +28,18 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import java.util.Set;
+
public class SubscriptionInfoTest {
private SubscriptionInfo mSubscriptionInfoUT;
- private static final String[] EHPLMNS = new String[] {"310999", "310998"};
- private static final String[] HPLMNS = new String[] {"310001"};
+ private static final String[] EHPLMNS = new String[]{"310999", "310998"};
+ private static final String[] HPLMNS = new String[]{"310001"};
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@After
public void tearDown() throws Exception {
@@ -39,6 +48,8 @@
@Before
public void setUp() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE);
+ mSetFlagsRule.enableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG);
mSubscriptionInfoUT = new SubscriptionInfo.Builder()
.setId(1)
.setIccId("890126042XXXXXXXXXXX")
@@ -55,6 +66,8 @@
.setHplmns(HPLMNS)
.setCountryIso("us")
.setOnlyNonTerrestrialNetwork(true)
+ .setServiceCapabilities(SubscriptionManager.getServiceCapabilitiesSet(
+ SubscriptionManager.SERVICE_CAPABILITY_DATA_BITMASK))
.build();
}
@@ -77,6 +90,8 @@
if (Flags.oemEnabledSatelliteFlag()) {
assertThat(mSubscriptionInfoUT.isOnlyNonTerrestrialNetwork()).isTrue();
}
+ assertThat(mSubscriptionInfoUT.getServiceCapabilities()).isEqualTo(
+ Set.of(SubscriptionManager.SERVICE_CAPABILITY_DATA));
}
@Test
@@ -101,5 +116,32 @@
assertThat(mSubscriptionInfoUT).isEqualTo(copiedInfo);
assertThat(mSubscriptionInfoUT).isNotEqualTo(differentDisplayName);
assertThat(mSubscriptionInfoUT).isNotEqualTo(differentSubId);
+
+ SubscriptionInfo differentServiceCapabilities =
+ new SubscriptionInfo.Builder(mSubscriptionInfoUT)
+ .setServiceCapabilities(SubscriptionManager.getServiceCapabilitiesSet(
+ SubscriptionManager.SERVICE_CAPABILITY_SMS_BITMASK))
+ .build();
+ assertThat(mSubscriptionInfoUT).isNotEqualTo(differentServiceCapabilities);
+ }
+
+ @Test
+ public void testInvalidServiceCapability_tooLarge() {
+ assertThrows("IllegalArgumentException should throw when set invalid service capability.",
+ IllegalArgumentException.class,
+ () -> new SubscriptionInfo.Builder()
+ .setServiceCapabilities(
+ Set.of(SubscriptionManager.SERVICE_CAPABILITY_MAX + 1))
+ .build());
+ }
+
+ @Test
+ public void testInvalidServiceCapability_tooSmall() {
+ assertThrows("IllegalArgumentException should throw when set invalid service capability.",
+ IllegalArgumentException.class,
+ () -> new SubscriptionInfo.Builder()
+ .setServiceCapabilities(
+ Set.of(SubscriptionManager.SERVICE_CAPABILITY_VOICE - 1))
+ .build());
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
index b42e6d6..d75b32a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionDatabaseManagerTest.java
@@ -124,6 +124,10 @@
static final int FAKE_SATELLITE_ATTACH_FOR_CARRIER_DISABLED = 0;
static final int FAKE_SATELLITE_IS_NTN_ENABLED = 1;
static final int FAKE_SATELLITE_IS_NTN_DISABLED = 0;
+ static final int FAKE_SERVICE_CAPABILITIES_1 =
+ SubscriptionManager.SERVICE_CAPABILITY_DATA_BITMASK;
+ static final int FAKE_SERVICE_CAPABILITIES_2 =
+ SubscriptionManager.SERVICE_CAPABILITY_SMS_BITMASK;
static final String FAKE_MAC_ADDRESS1 = "DC:E5:5B:38:7D:40";
static final String FAKE_MAC_ADDRESS2 = "DC:B5:4F:47:F3:4C";
@@ -199,6 +203,7 @@
FAKE_SATELLITE_ATTACH_FOR_CARRIER_DISABLED)
.setOnlyNonTerrestrialNetwork(FAKE_SATELLITE_IS_NTN_DISABLED)
.setGroupDisabled(false)
+ .setServiceCapabilities(FAKE_SERVICE_CAPABILITIES_1)
.build();
static final SubscriptionInfoInternal FAKE_SUBSCRIPTION_INFO2 =
@@ -270,6 +275,7 @@
FAKE_SATELLITE_ATTACH_FOR_CARRIER_ENABLED)
.setOnlyNonTerrestrialNetwork(FAKE_SATELLITE_IS_NTN_ENABLED)
.setGroupDisabled(false)
+ .setServiceCapabilities(FAKE_SERVICE_CAPABILITIES_2)
.build();
private SubscriptionDatabaseManager mDatabaseManagerUT;
@@ -429,6 +435,7 @@
doReturn(1).when(mUiccController).convertToPublicCardId(eq(FAKE_ICCID1));
doReturn(2).when(mUiccController).convertToPublicCardId(eq(FAKE_ICCID2));
when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+ when(mFeatureFlags.dataOnlyCellularService()).thenReturn(true);
mDatabaseManagerUT = new SubscriptionDatabaseManager(mContext, Looper.myLooper(),
mFeatureFlags, mSubscriptionDatabaseManagerCallback);
logd("SubscriptionDatabaseManagerTest -Setup!");
@@ -2174,4 +2181,32 @@
processAllMessages();
assertThat(latch.getCount()).isEqualTo(0);
}
+
+ @Test
+ public void testUpdateServiceCapabilities() throws Exception {
+ // exception is expected if there is nothing in the database.
+ assertThrows(IllegalArgumentException.class,
+ () -> mDatabaseManagerUT.setServiceCapabilities(1,
+ FAKE_SERVICE_CAPABILITIES_2));
+
+ SubscriptionInfoInternal subInfo = insertSubscriptionAndVerify(FAKE_SUBSCRIPTION_INFO1);
+ mDatabaseManagerUT.setServiceCapabilities(subInfo.getSubscriptionId(),
+ FAKE_SERVICE_CAPABILITIES_2);
+ processAllMessages();
+
+ subInfo = new SubscriptionInfoInternal.Builder(subInfo).setServiceCapabilities(
+ FAKE_SERVICE_CAPABILITIES_2).build();
+ verifySubscription(subInfo);
+ verify(mSubscriptionDatabaseManagerCallback, times(2)).onSubscriptionChanged(eq(1));
+
+ assertThat(mDatabaseManagerUT.getSubscriptionProperty(1,
+ SimInfo.COLUMN_SERVICE_CAPABILITIES))
+ .isEqualTo(FAKE_SERVICE_CAPABILITIES_2);
+ mDatabaseManagerUT.setSubscriptionProperty(
+ 1, SimInfo.COLUMN_SERVICE_CAPABILITIES,
+ FAKE_SERVICE_CAPABILITIES_1);
+ assertThat(
+ mDatabaseManagerUT.getSubscriptionInfoInternal(1).getServiceCapabilities())
+ .isEqualTo(FAKE_SERVICE_CAPABILITIES_1);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java
index 2505f32..d9addd1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionInfoInternalTest.java
@@ -18,16 +18,21 @@
import static com.google.common.truth.Truth.assertThat;
import android.os.ParcelUuid;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.UiccAccessRule;
import android.telephony.ims.ImsMmTelManager;
+import com.android.internal.telephony.flags.Flags;
+
+import org.junit.Rule;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Set;
public class SubscriptionInfoInternalTest {
private final SubscriptionInfoInternal mSubInfo =
@@ -108,6 +113,8 @@
SubscriptionDatabaseManagerTest.FAKE_SATELLITE_IS_NTN_ENABLED)
.setGroupDisabled(false)
.setOnlyNonTerrestrialNetwork(1)
+ .setServiceCapabilities(
+ SubscriptionManager.SERVICE_CAPABILITY_DATA_BITMASK)
.build();
private final SubscriptionInfoInternal mSubInfoNull =
@@ -136,8 +143,12 @@
.setDeviceToDeviceStatusSharingContacts("")
.build();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Test
public void testSubscriptionInfoInternalSetAndGet() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE);
assertThat(mSubInfo.getSubscriptionId()).isEqualTo(1);
assertThat(mSubInfo.getIccId()).isEqualTo(SubscriptionDatabaseManagerTest.FAKE_ICCID1);
assertThat(mSubInfo.getSimSlotIndex()).isEqualTo(0);
@@ -224,6 +235,8 @@
SubscriptionDatabaseManagerTest.FAKE_SATELLITE_IS_NTN_ENABLED);
assertThat(mSubInfo.isGroupDisabled()).isFalse();
assertThat(mSubInfo.getOnlyNonTerrestrialNetwork()).isEqualTo(1);
+ assertThat(mSubInfo.getServiceCapabilities()).isEqualTo(
+ SubscriptionManager.SERVICE_CAPABILITY_DATA_BITMASK);
}
@Test
@@ -235,6 +248,7 @@
@Test
public void testConvertToSubscriptionInfo() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE);
SubscriptionInfo subInfo = mSubInfo.toSubscriptionInfo();
assertThat(subInfo.getSubscriptionId()).isEqualTo(1);
@@ -287,6 +301,8 @@
SubscriptionManager.USAGE_SETTING_DEFAULT);
assertThat(subInfo.isGroupDisabled()).isFalse();
assertThat(subInfo.isOnlyNonTerrestrialNetwork()).isTrue();
+ assertThat(subInfo.getServiceCapabilities()).isEqualTo(
+ Set.of(SubscriptionManager.SERVICE_CAPABILITY_DATA));
}
@Test