From ec27c604618ececc3d01f8b39abad0b14d2ce74c Mon Sep 17 00:00:00 2001 From: SongFerngWang Date: Mon, 11 Dec 2023 05:43:12 +0800 Subject: [PATCH] Add condition whether esim is visible or not Bug: 314736037 Test: SubscriptionInfoListViewModelTest pass and build pass Change-Id: I7dc86ca93691f044d951122c0c669c790b7aef98 --- .../network/SubscriptionInfoListViewModel.kt | 5 +- .../settings/network/SubscriptionUtil.java | 40 ++++- .../SubscriptionsPreferenceController.java | 2 +- .../SubscriptionInfoListViewModelTest.kt | 153 ++++++++++++++++++ 4 files changed, 194 insertions(+), 6 deletions(-) create mode 100644 tests/spa_unit/src/com/android/settings/network/SubscriptionInfoListViewModelTest.kt diff --git a/src/com/android/settings/network/SubscriptionInfoListViewModel.kt b/src/com/android/settings/network/SubscriptionInfoListViewModel.kt index d30b21d6f03..ee88177e491 100644 --- a/src/com/android/settings/network/SubscriptionInfoListViewModel.kt +++ b/src/com/android/settings/network/SubscriptionInfoListViewModel.kt @@ -19,8 +19,10 @@ package com.android.settings.network import android.app.Application import android.telephony.SubscriptionInfo import android.telephony.SubscriptionManager + import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope + import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asExecutor import kotlinx.coroutines.channels.awaitClose @@ -32,13 +34,12 @@ import kotlinx.coroutines.plus class SubscriptionInfoListViewModel(application: Application) : AndroidViewModel(application) { private val scope = viewModelScope + Dispatchers.Default - val subscriptionInfoListFlow = callbackFlow> { val subscriptionManager = application.getSystemService(SubscriptionManager::class.java)!! val listener = object : SubscriptionManager.OnSubscriptionsChangedListener() { override fun onSubscriptionsChanged() { - trySend(subscriptionManager.activeSubscriptionInfoList ?: emptyList()) + trySend(SubscriptionUtil.getActiveSubscriptions(subscriptionManager)) } } diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java index 9974ba27708..7a127bbf873 100644 --- a/src/com/android/settings/network/SubscriptionUtil.java +++ b/src/com/android/settings/network/SubscriptionUtil.java @@ -18,6 +18,8 @@ package com.android.settings.network; import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX; import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT; +import static android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING; + import static com.android.internal.util.CollectionUtils.emptyIfNull; import android.annotation.Nullable; @@ -36,9 +38,11 @@ import android.text.TextDirectionHeuristics; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.internal.telephony.MccTable; +import com.android.internal.telephony.flags.Flags; import com.android.settings.R; import com.android.settings.network.helper.SelectableSubscriptions; import com.android.settings.network.helper.SubscriptionAnnotation; @@ -84,6 +88,8 @@ public class SubscriptionUtil { } public static List getActiveSubscriptions(SubscriptionManager manager) { + //TODO (b/315499317) : Refactor the subscription utils. + if (sActiveResultsForTesting != null) { return sActiveResultsForTesting; } @@ -94,7 +100,12 @@ public class SubscriptionUtil { if (subscriptions == null) { return new ArrayList<>(); } - return subscriptions; + // Since the SubscriptionManager.getActiveSubscriptionInfoList() has checked whether the + // sim visible by the SubscriptionManager.isSubscriptionVisible(), here only checks whether + // the esim visible here. + return subscriptions.stream() + .filter(subInfo -> subInfo != null && isEmbeddedSubscriptionVisible(subInfo)) + .collect(Collectors.toList()); } /** @@ -128,7 +139,7 @@ public class SubscriptionUtil { } /** - * Get subscription which is available to be displayed to the user + * Get subscriptionInfo which is available to be displayed to the user * per subscription id. * * @param context {@code Context} @@ -138,13 +149,20 @@ public class SubscriptionUtil { * @return {@code SubscriptionInfo} based on the given subscription id. Null of subscription * is invalid or not allowed to be displayed to the user. */ - public static SubscriptionInfo getAvailableSubscription(Context context, + public static SubscriptionInfo getAvailableSubscriptionBySubIdAndShowingForUser(Context context, ProxySubscriptionManager subscriptionManager, int subId) { + //TODO (b/315499317) : Refactor the subscription utils. final SubscriptionInfo subInfo = subscriptionManager.getAccessibleSubscriptionInfo(subId); if (subInfo == null) { return null; } + // hide provisioning/bootstrap and satellite profiles for user + if (isEmbeddedSubscriptionVisible(subInfo)) { + Log.d(TAG, "Do not insert the provision eSIM or NTN eSim"); + return null; + } + final ParcelUuid groupUuid = subInfo.getGroupUuid(); if (groupUuid != null) { @@ -567,6 +585,12 @@ public class SubscriptionUtil { public static boolean isSubscriptionVisible( SubscriptionManager subscriptionManager, Context context, SubscriptionInfo info) { if (info == null) return false; + + // hide provisioning/bootstrap and satellite profiles for user + if (isEmbeddedSubscriptionVisible(info)) { + return false; + } + // If subscription is NOT grouped opportunistic subscription, it's visible. if (info.getGroupUuid() == null || !info.isOpportunistic()) return true; @@ -786,4 +810,14 @@ public class SubscriptionUtil { } return (currentSubInfo == null) ? null : currentSubInfo.getSubInfo(); } + + private static boolean isEmbeddedSubscriptionVisible(@NonNull SubscriptionInfo subInfo) { + if (subInfo.isEmbedded() + && (subInfo.getProfileClass() == PROFILE_CLASS_PROVISIONING + || (Flags.oemEnabledSatelliteFlag() + && subInfo.isOnlyNonTerrestrialNetwork()))) { + return false; + } + return true; + } } diff --git a/src/com/android/settings/network/SubscriptionsPreferenceController.java b/src/com/android/settings/network/SubscriptionsPreferenceController.java index 0c3e6bd1b5d..6601828cd18 100644 --- a/src/com/android/settings/network/SubscriptionsPreferenceController.java +++ b/src/com/android/settings/network/SubscriptionsPreferenceController.java @@ -521,7 +521,7 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl * Uses to inject function and value for class and test class. */ public boolean canSubscriptionBeDisplayed(Context context, int subId) { - return (SubscriptionUtil.getAvailableSubscription(context, + return (SubscriptionUtil.getAvailableSubscriptionBySubIdAndShowingForUser(context, ProxySubscriptionManager.getInstance(context), subId) != null); } diff --git a/tests/spa_unit/src/com/android/settings/network/SubscriptionInfoListViewModelTest.kt b/tests/spa_unit/src/com/android/settings/network/SubscriptionInfoListViewModelTest.kt new file mode 100644 index 00000000000..020a4706997 --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/SubscriptionInfoListViewModelTest.kt @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2023 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.settings.network + +import android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING + +import android.app.Application +import android.content.Context +import android.platform.test.flag.junit.SetFlagsRule +import android.telephony.SubscriptionInfo +import android.telephony.SubscriptionManager +import android.telephony.TelephonyCallback +import android.telephony.TelephonyManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.internal.telephony.flags.Flags +import com.android.settings.network.telephony.CallStateFlowTest +import com.android.settingslib.spa.testutils.toListWithTimeout +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.async +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.stub + +@RunWith(AndroidJUnit4::class) +class SubscriptionInfoListViewModelTest { + @get:Rule + val mSetFlagsRule = SetFlagsRule() + private var subInfoListener: SubscriptionManager.OnSubscriptionsChangedListener? = null + private val mockSubscriptionManager = mock { + on { activeSubscriptionInfoList } doAnswer { activeSubscriptionInfoList } + on { addOnSubscriptionsChangedListener(any(), any()) } doAnswer { + subInfoListener = + it.arguments[1] as SubscriptionManager.OnSubscriptionsChangedListener + subInfoListener?.onSubscriptionsChanged() + } + } + + private val context: Context = spy(ApplicationProvider.getApplicationContext()) { + on { getSystemService(SubscriptionManager::class.java) } doReturn mockSubscriptionManager + } + + private val subscriptionInfoListViewModel: SubscriptionInfoListViewModel = + SubscriptionInfoListViewModel(context as Application); + + private var activeSubscriptionInfoList: List? = null + + @Test + fun onSubscriptionsChanged_noProvisioning_resultSameAsInput() = runBlocking { + activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2) + + val listDeferred = async { + subscriptionInfoListViewModel.subscriptionInfoListFlow.toListWithTimeout() + } + delay(100) + subInfoListener?.onSubscriptionsChanged() + + assertThat(listDeferred.await()).contains(activeSubscriptionInfoList) + } + + @Test + fun onSubscriptionsChanged_hasProvisioning_filterProvisioning() = runBlocking { + activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3) + val expectation = listOf(SUB_INFO_1, SUB_INFO_2) + + val listDeferred = async { + subscriptionInfoListViewModel.subscriptionInfoListFlow.toListWithTimeout() + } + delay(100) + subInfoListener?.onSubscriptionsChanged() + + assertThat(listDeferred.await()).contains(expectation) + } + + @Test + fun onSubscriptionsChanged_flagOffHasNonTerrestrialNetwork_filterNonTerrestrialNetwork() = + runBlocking { + mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + + activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_4) + val expectation = listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_4) + + val listDeferred = async { + subscriptionInfoListViewModel.subscriptionInfoListFlow.toListWithTimeout() + } + delay(100) + subInfoListener?.onSubscriptionsChanged() + + assertThat(listDeferred.await()).contains(expectation) + } + + @Test + fun onSubscriptionsChanged_flagOnHasNonTerrestrialNetwork_filterNonTerrestrialNetwork() = + runBlocking { + mSetFlagsRule.enableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + + activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_4) + val expectation = listOf(SUB_INFO_1, SUB_INFO_2) + + val listDeferred = async { + subscriptionInfoListViewModel.subscriptionInfoListFlow.toListWithTimeout() + } + delay(100) + subInfoListener?.onSubscriptionsChanged() + + assertThat(listDeferred.await()).contains(expectation) + } + + private companion object { + val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply { + setId(1) + }.build() + + val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply { + setId(2) + }.build() + + val SUB_INFO_3: SubscriptionInfo = SubscriptionInfo.Builder().apply { + setId(3) + setEmbedded(true) + setProfileClass(PROFILE_CLASS_PROVISIONING) + setOnlyNonTerrestrialNetwork(false) + }.build() + + val SUB_INFO_4: SubscriptionInfo = SubscriptionInfo.Builder().apply { + setId(4) + setEmbedded(true) + setOnlyNonTerrestrialNetwork(true) + }.build() + } +} \ No newline at end of file