Merge "Replace "Priority Modes" with "Modes" (Settings)" into main
diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
index 8e09188..c0d67cc 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java
@@ -201,6 +201,19 @@
+ reason
+ ", broadcastId = "
+ broadcastId);
+ if (mAssistant == null
+ || mAssistant.getAllConnectedDevices().stream()
+ .anyMatch(
+ device -> BluetoothUtils
+ .hasActiveLocalBroadcastSourceForBtDevice(
+ device, mBtManager))) {
+ Log.d(
+ TAG,
+ "Skip handleOnBroadcastReady: null assistant or "
+ + "sink has active local source.");
+ cleanUp();
+ return;
+ }
handleOnBroadcastReady();
}
@@ -554,8 +567,7 @@
mGroupedConnectedDevices.getOrDefault(
mDeviceItemsForSharing.get(0).getGroupId(), ImmutableList.of()),
mBtManager);
- mGroupedConnectedDevices.clear();
- mDeviceItemsForSharing.clear();
+ cleanUp();
// TODO: Add metric for auto add by intent
return;
}
@@ -565,8 +577,7 @@
StartIntentHandleStage.HANDLED.ordinal());
if (mFragment == null) {
Log.d(TAG, "handleOnBroadcastReady: dialog fail to show due to null fragment.");
- mGroupedConnectedDevices.clear();
- mDeviceItemsForSharing.clear();
+ cleanUp();
return;
}
showDialog(eventData);
@@ -581,14 +592,12 @@
mGroupedConnectedDevices.getOrDefault(
item.getGroupId(), ImmutableList.of()),
mBtManager);
- mGroupedConnectedDevices.clear();
- mDeviceItemsForSharing.clear();
+ cleanUp();
}
@Override
public void onCancelClick() {
- mGroupedConnectedDevices.clear();
- mDeviceItemsForSharing.clear();
+ cleanUp();
}
};
AudioSharingUtils.postOnMainThread(
@@ -657,6 +666,11 @@
});
}
+ private void cleanUp() {
+ mGroupedConnectedDevices.clear();
+ mDeviceItemsForSharing.clear();
+ }
+
private enum StartIntentHandleStage {
TO_HANDLE,
HANDLE_AUTO_ADD,
diff --git a/src/com/android/settings/network/apn/ApnRepository.kt b/src/com/android/settings/network/apn/ApnRepository.kt
index 8433715..7ed0c86 100644
--- a/src/com/android/settings/network/apn/ApnRepository.kt
+++ b/src/com/android/settings/network/apn/ApnRepository.kt
@@ -21,10 +21,10 @@
import android.database.Cursor
import android.net.Uri
import android.provider.Telephony
-import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.util.Log
import com.android.settings.R
+import com.android.settings.network.telephony.telephonyManager
import com.android.settingslib.utils.ThreadUtils
import java.util.Locale
@@ -178,12 +178,11 @@
}
fun Context.getApnIdMap(subId: Int): Map<String, Any> {
- val subInfo = getSystemService(SubscriptionManager::class.java)!!
- .getActiveSubscriptionInfo(subId)
- val carrierId = subInfo.carrierId
+ val telephonyManager = telephonyManager(subId)
+ val carrierId = telephonyManager.simSpecificCarrierId
return if (carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) {
mapOf(Telephony.Carriers.CARRIER_ID to carrierId)
} else {
- mapOf(Telephony.Carriers.NUMERIC to subInfo.mccString + subInfo.mncString)
+ mapOf(Telephony.Carriers.NUMERIC to telephonyManager.simOperator)
}.also { Log.d(TAG, "[$subId] New APN item with id: $it") }
}
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index a5cdb95..91874c4 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -41,6 +41,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference;
@@ -66,6 +67,8 @@
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.utils.ThreadUtils;
+import kotlin.Unit;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -359,6 +362,16 @@
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
collectAirplaneModeAndFinishIfOn(this);
+
+ LifecycleOwner viewLifecycleOwner = getViewLifecycleOwner();
+ new SubscriptionRepository(requireContext())
+ .collectSubscriptionVisible(mSubId, viewLifecycleOwner, (isVisible) -> {
+ if (!isVisible) {
+ Log.d(LOG_TAG, "Due to subscription not visible, closes page");
+ finishFragment();
+ }
+ return Unit.INSTANCE;
+ });
}
@Override
@@ -532,11 +545,6 @@
Log.d(LOG_TAG, "Set subInfo to default subInfo.");
}
}
- if (mSubscriptionInfoEntity == null && getActivity() != null) {
- // If the current subId is not existed, finish it.
- finishFragment();
- return;
- }
onSubscriptionDetailChanged();
}
}
diff --git a/src/com/android/settings/network/telephony/SubscriptionRepository.kt b/src/com/android/settings/network/telephony/SubscriptionRepository.kt
index cc8c8b4..26ea9b3 100644
--- a/src/com/android/settings/network/telephony/SubscriptionRepository.kt
+++ b/src/com/android/settings/network/telephony/SubscriptionRepository.kt
@@ -50,6 +50,31 @@
fun getSelectableSubscriptionInfoList(): List<SubscriptionInfo> =
context.getSelectableSubscriptionInfoList()
+ /** Flow of whether the subscription visible for the given [subId]. */
+ fun isSubscriptionVisibleFlow(subId: Int): Flow<Boolean> {
+ return subscriptionsChangedFlow()
+ .map {
+ val subInfo =
+ subscriptionManager.availableSubscriptionInfoList?.firstOrNull { subInfo ->
+ subInfo.subscriptionId == subId
+ }
+ subInfo != null &&
+ SubscriptionUtil.isSubscriptionVisible(subscriptionManager, context, subInfo)
+ }
+ .conflate()
+ .onEach { Log.d(TAG, "[$subId] isSubscriptionVisibleFlow: $it") }
+ .flowOn(Dispatchers.Default)
+ }
+
+ /** TODO: Move this to UI layer, when UI layer migrated to Kotlin. */
+ fun collectSubscriptionVisible(
+ subId: Int,
+ lifecycleOwner: LifecycleOwner,
+ action: (Boolean) -> Unit,
+ ) {
+ isSubscriptionVisibleFlow(subId).collectLatestWithLifecycle(lifecycleOwner, action = action)
+ }
+
/** Flow of whether the subscription enabled for the given [subId]. */
fun isSubscriptionEnabledFlow(subId: Int): Flow<Boolean> {
if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
diff --git a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt
index e04763a..9b68970 100644
--- a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt
+++ b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.kt
@@ -81,6 +81,12 @@
}
override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ // Sub id could invalid, if this page is opened from external action and no sim is
+ // active.
+ // Ignore this case, since this page will be finished soon.
+ return
+ }
wifiCallingRepositoryFactory(subId).wifiCallingReadyFlow()
.collectLatestWithLifecycle(viewLifecycleOwner) { isReady ->
preference.isVisible = isReady
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java
index 558bc10..5073119 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java
@@ -443,6 +443,7 @@
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
when(mBtnView.isEnabled()).thenReturn(true);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
+ when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
doNothing().when(mBroadcast).startPrivateBroadcast();
mController =
@@ -469,11 +470,37 @@
}
@Test
+ public void onPlaybackStarted_hasLocalSource_noDialog() {
+ FeatureFlagUtils.setEnabled(
+ mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
+ when(mBtnView.isEnabled()).thenReturn(true);
+ when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
+ BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
+ when(state.getBroadcastId()).thenReturn(1);
+ when(mBroadcast.getLatestBroadcastId()).thenReturn(1);
+ when(mAssistant.getAllSources(mDevice2)).thenReturn(ImmutableList.of(state));
+ when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
+ doNothing().when(mBroadcast).startPrivateBroadcast();
+ mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
+ verify(mBroadcast).startPrivateBroadcast();
+ mController.mBroadcastCallback.onPlaybackStarted(0, 0);
+ shadowOf(Looper.getMainLooper()).idle();
+
+ verify(mAssistant, never()).addSource(any(), any(), anyBoolean());
+ verify(mFeatureFactory.metricsFeatureProvider, never())
+ .action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING));
+
+ List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
+ assertThat(childFragments).isEmpty();
+ }
+
+ @Test
public void onPlaybackStarted_showJoinAudioSharingDialog() {
FeatureFlagUtils.setEnabled(
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
when(mBtnView.isEnabled()).thenReturn(true);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
+ when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
doNothing().when(mBroadcast).startPrivateBroadcast();
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
@@ -519,6 +546,7 @@
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
when(mBtnView.isEnabled()).thenReturn(true);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
+ when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
doNothing().when(mBroadcast).startPrivateBroadcast();
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
@@ -545,6 +573,7 @@
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
when(mBtnView.isEnabled()).thenReturn(true);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
+ when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
doNothing().when(mBroadcast).startPrivateBroadcast();
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt
index 4155318..d2f16d7 100644
--- a/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt
@@ -21,8 +21,6 @@
import android.database.MatrixCursor
import android.net.Uri
import android.provider.Telephony
-import android.telephony.SubscriptionInfo
-import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -40,19 +38,15 @@
private val contentResolver = mock<ContentResolver>()
- private val mockSubscriptionInfo = mock<SubscriptionInfo> {
- on { mccString } doReturn MCC
- on { mncString } doReturn MNC
- }
+ private val mockTelephonyManager =
+ mock<TelephonyManager> { on { createForSubscriptionId(SUB_ID) } doReturn mock }
- private val mockSubscriptionManager = mock<SubscriptionManager> {
- on { getActiveSubscriptionInfo(SUB_ID) } doReturn mockSubscriptionInfo
- }
+ private val context: Context =
+ spy(ApplicationProvider.getApplicationContext()) {
+ on { contentResolver } doReturn contentResolver
+ on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
+ }
- private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
- on { contentResolver } doReturn contentResolver
- on { getSystemService(SubscriptionManager::class.java) } doReturn mockSubscriptionManager
- }
private val uri = mock<Uri> {}
@Test
@@ -91,9 +85,7 @@
@Test
fun getApnIdMap_knownCarrierId() {
- mockSubscriptionInfo.stub {
- on { carrierId } doReturn CARRIER_ID
- }
+ mockTelephonyManager.stub { on { simSpecificCarrierId } doReturn CARRIER_ID }
val idMap = context.getApnIdMap(SUB_ID)
@@ -102,19 +94,19 @@
@Test
fun getApnIdMap_unknownCarrierId() {
- mockSubscriptionInfo.stub {
- on { carrierId } doReturn TelephonyManager.UNKNOWN_CARRIER_ID
+ mockTelephonyManager.stub {
+ on { simSpecificCarrierId } doReturn TelephonyManager.UNKNOWN_CARRIER_ID
+ on { simOperator } doReturn SIM_OPERATOR
}
val idMap = context.getApnIdMap(SUB_ID)
- assertThat(idMap).containsExactly(Telephony.Carriers.NUMERIC, MCC + MNC)
+ assertThat(idMap).containsExactly(Telephony.Carriers.NUMERIC, SIM_OPERATOR)
}
private companion object {
const val SUB_ID = 2
const val CARRIER_ID = 10
- const val MCC = "310"
- const val MNC = "101"
+ const val SIM_OPERATOR = "310101"
}
}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt
index f75c14a..5dbc534 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt
@@ -190,6 +190,32 @@
}
@Test
+ fun isSubscriptionVisibleFlow_available_returnTrue() = runBlocking {
+ mockSubscriptionManager.stub {
+ on { getAvailableSubscriptionInfoList() } doReturn
+ listOf(SubscriptionInfo.Builder().apply { setId(SUB_ID_IN_SLOT_0) }.build())
+ }
+
+ val isVisible =
+ repository.isSubscriptionVisibleFlow(SUB_ID_IN_SLOT_0).firstWithTimeoutOrNull()
+
+ assertThat(isVisible).isTrue()
+ }
+
+ @Test
+ fun isSubscriptionVisibleFlow_unavailable_returnFalse() = runBlocking {
+ mockSubscriptionManager.stub {
+ on { getAvailableSubscriptionInfoList() } doReturn
+ listOf(SubscriptionInfo.Builder().apply { setId(SUB_ID_IN_SLOT_0) }.build())
+ }
+
+ val isVisible =
+ repository.isSubscriptionVisibleFlow(SUB_ID_IN_SLOT_1).firstWithTimeoutOrNull()
+
+ assertThat(isVisible).isFalse()
+ }
+
+ @Test
fun phoneNumberFlow() = runBlocking {
mockSubscriptionManager.stub {
on { getPhoneNumber(SUB_ID_IN_SLOT_1) } doReturn NUMBER_1