From f1b43a970c6b757630a4b8305e45e5b3b5ffc643 Mon Sep 17 00:00:00 2001 From: Bonian Chen Date: Mon, 4 Nov 2019 10:36:39 +0800 Subject: [PATCH] [Settings] MobileNetworkActivity Refactor Split MobileNetworkActivity into 2 files. Enable testing through ActivityScenario Bug: 141833767 Test: manual make RunSettingsRoboTests -j ROBOTEST_FILTER=MobileNetworkActivityTest make RunSettingsRoboTests -j ROBOTEST_FILTER=ActiveSubsciptionsListenerTest Change-Id: I9f0f80f5edda53196ce0795113506495e8980e99 --- .../network/ActiveSubsciptionsListener.java | 205 ++++++++++++ .../telephony/MobileNetworkActivity.java | 213 +++++-------- tests/robotests/Android.bp | 5 +- ...roidx.test.internal.platform.ThreadChecker | 1 + ...test.internal.platform.app.ActivityInvoker | 1 + ...nternal.platform.content.PermissionGranter | 1 + ...test.internal.platform.os.ControlledLooper | 1 + .../androidx.test.platform.ui.UiController | 1 + .../ActiveSubsciptionsListenerTest.java | 153 +++++++++ .../telephony/MobileNetworkActivityTest.java | 293 ++++++++---------- 10 files changed, 574 insertions(+), 300 deletions(-) create mode 100644 src/com/android/settings/network/ActiveSubsciptionsListener.java create mode 100644 tests/robotests/resources/META-INF/services/androidx.test.internal.platform.ThreadChecker create mode 100644 tests/robotests/resources/META-INF/services/androidx.test.internal.platform.app.ActivityInvoker create mode 100644 tests/robotests/resources/META-INF/services/androidx.test.internal.platform.content.PermissionGranter create mode 100644 tests/robotests/resources/META-INF/services/androidx.test.internal.platform.os.ControlledLooper create mode 100644 tests/robotests/resources/META-INF/services/androidx.test.platform.ui.UiController create mode 100644 tests/robotests/src/com/android/settings/network/ActiveSubsciptionsListenerTest.java diff --git a/src/com/android/settings/network/ActiveSubsciptionsListener.java b/src/com/android/settings/network/ActiveSubsciptionsListener.java new file mode 100644 index 00000000000..1ed807834e3 --- /dev/null +++ b/src/com/android/settings/network/ActiveSubsciptionsListener.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 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.settings.network; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.text.TextUtils; + +import androidx.annotation.VisibleForTesting; + +import com.android.internal.telephony.TelephonyIntents; + +import java.util.List; + +/** + * A listener for active subscription change + */ +public abstract class ActiveSubsciptionsListener + extends SubscriptionManager.OnSubscriptionsChangedListener { + + /** + * Constructor + * + * @param context of this listener + */ + public ActiveSubsciptionsListener(Context context) { + mContext = context; + + mSubscriptionChangeIntentFilter = new IntentFilter(); + mSubscriptionChangeIntentFilter.addAction( + CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); + mSubscriptionChangeIntentFilter.addAction( + TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); + + mSubscriptionChangeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (isInitialStickyBroadcast()) { + return; + } + final String action = intent.getAction(); + if (TextUtils.isEmpty(action)) { + return; + } + if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) { + final int subId = intent.getIntExtra( + CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, + SubscriptionManager.INVALID_SUBSCRIPTION_ID); + if (!clearCachedSubId(subId)) { + return; + } + } + onSubscriptionsChanged(); + } + }; + } + + private Context mContext; + + private boolean mIsMonitoringDataChange; + private boolean mIsCachedDataAvailable; + private SubscriptionManager mSubscriptionManager; + + private IntentFilter mSubscriptionChangeIntentFilter; + + @VisibleForTesting + BroadcastReceiver mSubscriptionChangeReceiver; + + private List mCachedActiveSubscriptionInfo; + + /** + * Active subscriptions got changed + */ + public abstract void onChanged(); + + @Override + public void onSubscriptionsChanged() { + // clear value in cache + clearCache(); + listenerNotify(); + } + + /** + * Start listening subscriptions change + */ + public void start() { + monitorSubscriptionsChange(true); + } + + /** + * Stop listening subscriptions change + */ + public void stop() { + monitorSubscriptionsChange(false); + } + + /** + * Get SubscriptionManager + * + * @return a SubscriptionManager + */ + public SubscriptionManager getSubscriptionManager() { + if (mSubscriptionManager == null) { + mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); + } + return mSubscriptionManager; + } + + /** + * Get a list of active subscription info + * + * @return A list of active subscription info + */ + public List getActiveSubscriptionsInfo() { + if (mIsCachedDataAvailable) { + return mCachedActiveSubscriptionInfo; + } + mIsCachedDataAvailable = true; + mCachedActiveSubscriptionInfo = getSubscriptionManager().getActiveSubscriptionInfoList(); + return mCachedActiveSubscriptionInfo; + } + + /** + * Get an active subscription info with given subscription ID + * + * @param subId target subscription ID + * @return A subscription info which is active list + */ + public SubscriptionInfo getActiveSubscriptionInfo(int subId) { + final List subInfoList = getActiveSubscriptionsInfo(); + if (subInfoList == null) { + return null; + } + for (SubscriptionInfo subInfo : subInfoList) { + if (subInfo.getSubscriptionId() == subId) { + return subInfo; + } + } + return null; + } + + /** + * Clear data cached within listener + */ + public void clearCache() { + mIsCachedDataAvailable = false; + mCachedActiveSubscriptionInfo = null; + } + + private void monitorSubscriptionsChange(boolean on) { + if (mIsMonitoringDataChange == on) { + return; + } + mIsMonitoringDataChange = on; + if (on) { + mContext.registerReceiver(mSubscriptionChangeReceiver, + mSubscriptionChangeIntentFilter); + getSubscriptionManager().addOnSubscriptionsChangedListener(this); + listenerNotify(); + } else { + mContext.unregisterReceiver(mSubscriptionChangeReceiver); + getSubscriptionManager().removeOnSubscriptionsChangedListener(this); + clearCache(); + } + } + + private void listenerNotify() { + if (!mIsMonitoringDataChange) { + return; + } + onChanged(); + } + + private boolean clearCachedSubId(int subId) { + if ((!mIsCachedDataAvailable) || (mCachedActiveSubscriptionInfo == null)) { + return false; + } + for (SubscriptionInfo subInfo : mCachedActiveSubscriptionInfo) { + if (subInfo.getSubscriptionId() == subId) { + clearCache(); + return true; + } + } + return false; + } +} diff --git a/src/com/android/settings/network/telephony/MobileNetworkActivity.java b/src/com/android/settings/network/telephony/MobileNetworkActivity.java index 99de60eb46e..26107222ae5 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkActivity.java +++ b/src/com/android/settings/network/telephony/MobileNetworkActivity.java @@ -17,16 +17,11 @@ package com.android.settings.network.telephony; import android.app.ActionBar; -import android.content.BroadcastReceiver; -import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.os.Bundle; import android.os.UserManager; import android.provider.Settings; -import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; import android.view.Menu; import android.view.View; @@ -36,20 +31,20 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; -import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.CollectionUtils; import com.android.settings.R; import com.android.settings.core.FeatureFlags; import com.android.settings.core.SettingsBaseActivity; import com.android.settings.development.featureflags.FeatureFlagPersistent; -import com.android.settings.network.SubscriptionUtil; +import com.android.settings.network.ActiveSubsciptionsListener; import com.google.android.material.bottomnavigation.BottomNavigationView; -import java.util.ArrayList; import java.util.List; -import java.util.Objects; +/** + * Activity for displaying MobileNetworkSettings + */ public class MobileNetworkActivity extends SettingsBaseActivity { private static final String TAG = "MobileNetworkActivity"; @@ -58,31 +53,21 @@ public class MobileNetworkActivity extends SettingsBaseActivity { @VisibleForTesting static final int SUB_ID_NULL = Integer.MIN_VALUE; - @VisibleForTesting - SubscriptionManager mSubscriptionManager; - @VisibleForTesting - int mCurSubscriptionId; - @VisibleForTesting - List mSubscriptionInfos = new ArrayList<>(); - private PhoneChangeReceiver mPhoneChangeReceiver; - - private final SubscriptionManager.OnSubscriptionsChangedListener - mOnSubscriptionsChangeListener - = new SubscriptionManager.OnSubscriptionsChangedListener() { - @Override - public void onSubscriptionsChanged() { - if (!Objects.equals(mSubscriptionInfos, - mSubscriptionManager.getActiveSubscriptionInfoList(true))) { - updateSubscriptions(null); - } - } - }; + private ActiveSubsciptionsListener mSubscriptionAccess; + private int mCurSubscriptionId; @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); - updateSubscriptions(null); + + int updateSubscriptionIndex = SUB_ID_NULL; + if (intent != null) { + updateSubscriptionIndex = intent.getIntExtra(Settings.EXTRA_SUB_ID, SUB_ID_NULL); + } + + mCurSubscriptionId = updateSubscriptionIndex; + updateSubscriptions(getSubscription()); } @Override @@ -91,6 +76,7 @@ public class MobileNetworkActivity extends SettingsBaseActivity { final UserManager userManager = this.getSystemService(UserManager.class); if (!userManager.isAdminUser()) { this.finish(); + return; } if (FeatureFlagPersistent.isEnabled(this, FeatureFlags.NETWORK_INTERNET_V2)) { @@ -99,21 +85,13 @@ public class MobileNetworkActivity extends SettingsBaseActivity { setContentView(R.layout.mobile_network_settings_container); } setActionBar(findViewById(R.id.mobile_action_bar)); - mPhoneChangeReceiver = new PhoneChangeReceiver(this, new PhoneChangeReceiver.Client() { - @Override - public void onPhoneChange() { - // When the radio or carrier config changes (ex: CDMA->GSM), refresh the fragment. - switchFragment(new MobileNetworkSettings(), mCurSubscriptionId, - true /* forceUpdate */); - } - @Override - public int getSubscriptionId() { - return mCurSubscriptionId; + mSubscriptionAccess = new ActiveSubsciptionsListener(this) { + public void onChanged() { + updateSubscriptions(getSubscription()); } - }); - mSubscriptionManager = getSystemService(SubscriptionManager.class); - mSubscriptionInfos = mSubscriptionManager.getActiveSubscriptionInfoList(true); + }; + mCurSubscriptionId = savedInstanceState != null ? savedInstanceState.getInt(Settings.EXTRA_SUB_ID, SUB_ID_NULL) : SUB_ID_NULL; @@ -123,21 +101,21 @@ public class MobileNetworkActivity extends SettingsBaseActivity { actionBar.setDisplayHomeAsUpEnabled(true); } - updateSubscriptions(savedInstanceState); + final SubscriptionInfo subscription = getSubscription(); + updateTitleAndNavigation(subscription); } @Override protected void onStart() { super.onStart(); - mPhoneChangeReceiver.register(); - mSubscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); + mSubscriptionAccess.start(); + updateSubscriptions(getSubscription()); } @Override protected void onStop() { super.onStop(); - mPhoneChangeReceiver.unregister(); - mSubscriptionManager.removeOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); + mSubscriptionAccess.stop(); } @Override @@ -151,25 +129,30 @@ public class MobileNetworkActivity extends SettingsBaseActivity { outState.putInt(Settings.EXTRA_SUB_ID, mCurSubscriptionId); } - @VisibleForTesting - void updateSubscriptions(Bundle savedInstanceState) { + private void updateTitleAndNavigation(SubscriptionInfo subscription) { // Set the title to the name of the subscription. If we don't have subscription info, the // title will just default to the label for this activity that's already specified in // AndroidManifest.xml. - final SubscriptionInfo subscription = getSubscription(); if (subscription != null) { setTitle(subscription.getDisplayName()); } - mSubscriptionInfos = mSubscriptionManager.getActiveSubscriptionInfoList(true); - if (!FeatureFlagPersistent.isEnabled(this, FeatureFlags.NETWORK_INTERNET_V2)) { updateBottomNavigationView(); } + } - if (savedInstanceState == null) { - switchFragment(new MobileNetworkSettings(), getSubscriptionId()); + @VisibleForTesting + void updateSubscriptions(SubscriptionInfo subscription) { + if (subscription == null) { + return; } + final int subscriptionIndex = subscription.getSubscriptionId(); + + updateTitleAndNavigation(subscription); + switchFragment(subscription); + + mCurSubscriptionId = subscriptionIndex; } /** @@ -179,125 +162,73 @@ public class MobileNetworkActivity extends SettingsBaseActivity { */ @VisibleForTesting SubscriptionInfo getSubscription() { - final Intent intent = getIntent(); - if (intent != null) { - final int subId = intent.getIntExtra(Settings.EXTRA_SUB_ID, SUB_ID_NULL); - if (subId != SUB_ID_NULL) { - for (SubscriptionInfo subscription : - SubscriptionUtil.getAvailableSubscriptions(this)) { - if (subscription.getSubscriptionId() == subId) { - return subscription; - } - } + if (mCurSubscriptionId != SUB_ID_NULL) { + final SubscriptionInfo subInfo = + mSubscriptionAccess.getActiveSubscriptionInfo(mCurSubscriptionId); + if (subInfo != null) { + return subInfo; } } - - if (CollectionUtils.isEmpty(mSubscriptionInfos)) { + final List subInfos = mSubscriptionAccess.getActiveSubscriptionsInfo(); + if (CollectionUtils.isEmpty(subInfos)) { return null; } - return mSubscriptionInfos.get(0); + return subInfos.get(0); } - /** - * Get the current subId to display. - */ - @VisibleForTesting - int getSubscriptionId() { - final SubscriptionInfo subscription = getSubscription(); - if (subscription != null) { - return subscription.getSubscriptionId(); - } - return SubscriptionManager.INVALID_SUBSCRIPTION_ID; - } - - @VisibleForTesting - void updateBottomNavigationView() { + private void updateBottomNavigationView() { final BottomNavigationView navigation = findViewById(R.id.bottom_nav); - if (CollectionUtils.size(mSubscriptionInfos) <= 1) { + final List subInfos = mSubscriptionAccess.getActiveSubscriptionsInfo(); + if (CollectionUtils.size(subInfos) <= 1) { navigation.setVisibility(View.GONE); } else { final Menu menu = navigation.getMenu(); menu.clear(); - for (int i = 0, size = mSubscriptionInfos.size(); i < size; i++) { - final SubscriptionInfo subscriptionInfo = mSubscriptionInfos.get(i); + for (int i = 0, size = subInfos.size(); i < size; i++) { + final SubscriptionInfo subscriptionInfo = subInfos.get(i); menu.add(0, subscriptionInfo.getSubscriptionId(), i, subscriptionInfo.getDisplayName()) .setIcon(R.drawable.ic_settings_sim); } navigation.setOnNavigationItemSelectedListener(item -> { - switchFragment(new MobileNetworkSettings(), item.getItemId()); + final int subId = item.getItemId(); + if (!isSubscriptionChanged(subId)) { + return true; + } + final SubscriptionInfo subscriptionInfo = mSubscriptionAccess + .getActiveSubscriptionInfo(subId); + if (subscriptionInfo == null) { + return true; + } + updateSubscriptions(subscriptionInfo); return true; }); + navigation.setVisibility(View.VISIBLE); } } @VisibleForTesting - void switchFragment(Fragment fragment, int subscriptionId) { - switchFragment(fragment, subscriptionId, false /* forceUpdate */); - } - - @VisibleForTesting - void switchFragment(Fragment fragment, int subscriptionId, boolean forceUpdate) { - if (mCurSubscriptionId != SUB_ID_NULL && subscriptionId == mCurSubscriptionId - && !forceUpdate) { - return; - } + void switchFragment(SubscriptionInfo subInfo) { final FragmentManager fragmentManager = getSupportFragmentManager(); final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); - final Bundle bundle = new Bundle(); - bundle.putInt(Settings.EXTRA_SUB_ID, subscriptionId); + final int subId = subInfo.getSubscriptionId(); + final Bundle bundle = new Bundle(); + bundle.putInt(Settings.EXTRA_SUB_ID, subId); + + final Fragment fragment = new MobileNetworkSettings(); fragment.setArguments(bundle); - fragmentTransaction.replace(R.id.main_content, fragment, - buildFragmentTag(subscriptionId)); + fragmentTransaction.replace(R.id.main_content, fragment, buildFragmentTag(subId)); fragmentTransaction.commit(); - mCurSubscriptionId = subscriptionId; } - private String buildFragmentTag(int subscriptionId) { + @VisibleForTesting + String buildFragmentTag(int subscriptionId) { return MOBILE_SETTINGS_TAG + subscriptionId; } - @VisibleForTesting - static class PhoneChangeReceiver extends BroadcastReceiver { - private Context mContext; - private Client mClient; - - interface Client { - void onPhoneChange(); - int getSubscriptionId(); - } - - public PhoneChangeReceiver(Context context, Client client) { - mContext = context; - mClient = client; - } - - public void register() { - final IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); - intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); - mContext.registerReceiver(this, intentFilter); - } - - public void unregister() { - mContext.unregisterReceiver(this); - } - - @Override - public void onReceive(Context context, Intent intent) { - if (isInitialStickyBroadcast()) { - return; - } - if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { - if (!intent.hasExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX) || - intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, -1) - != mClient.getSubscriptionId()) { - return; - } - } - mClient.onPhoneChange(); - } + private boolean isSubscriptionChanged(int subscriptionId) { + return (subscriptionId == SUB_ID_NULL) || (subscriptionId != mCurSubscriptionId); } } diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp index 4920e111aaa..96372bebac0 100644 --- a/tests/robotests/Android.bp +++ b/tests/robotests/Android.bp @@ -31,6 +31,9 @@ android_app { "androidx-constraintlayout_constraintlayout-solver", "androidx.lifecycle_lifecycle-runtime", "androidx.lifecycle_lifecycle-extensions", + "androidx.test.core", + "androidx.test.runner", + "androidx.test.ext.junit", "guava", "jsr305", "settings-contextual-card-protos-lite", @@ -61,7 +64,7 @@ android_robolectric_test { "SettingsLib-robo-testutils", ], - java_resource_dirs: ["config"], + java_resource_dirs: ["config", "resources"], instrumentation_for: "SettingsRoboTestStub", diff --git a/tests/robotests/resources/META-INF/services/androidx.test.internal.platform.ThreadChecker b/tests/robotests/resources/META-INF/services/androidx.test.internal.platform.ThreadChecker new file mode 100644 index 00000000000..55104eac2a6 --- /dev/null +++ b/tests/robotests/resources/META-INF/services/androidx.test.internal.platform.ThreadChecker @@ -0,0 +1 @@ +org.robolectric.android.internal.NoOpThreadChecker diff --git a/tests/robotests/resources/META-INF/services/androidx.test.internal.platform.app.ActivityInvoker b/tests/robotests/resources/META-INF/services/androidx.test.internal.platform.app.ActivityInvoker new file mode 100644 index 00000000000..e9944b51e44 --- /dev/null +++ b/tests/robotests/resources/META-INF/services/androidx.test.internal.platform.app.ActivityInvoker @@ -0,0 +1 @@ +org.robolectric.android.internal.LocalActivityInvoker diff --git a/tests/robotests/resources/META-INF/services/androidx.test.internal.platform.content.PermissionGranter b/tests/robotests/resources/META-INF/services/androidx.test.internal.platform.content.PermissionGranter new file mode 100644 index 00000000000..3bcad1cab26 --- /dev/null +++ b/tests/robotests/resources/META-INF/services/androidx.test.internal.platform.content.PermissionGranter @@ -0,0 +1 @@ +org.robolectric.android.internal.LocalPermissionGranter diff --git a/tests/robotests/resources/META-INF/services/androidx.test.internal.platform.os.ControlledLooper b/tests/robotests/resources/META-INF/services/androidx.test.internal.platform.os.ControlledLooper new file mode 100644 index 00000000000..add33e61124 --- /dev/null +++ b/tests/robotests/resources/META-INF/services/androidx.test.internal.platform.os.ControlledLooper @@ -0,0 +1 @@ +org.robolectric.android.internal.LocalControlledLooper \ No newline at end of file diff --git a/tests/robotests/resources/META-INF/services/androidx.test.platform.ui.UiController b/tests/robotests/resources/META-INF/services/androidx.test.platform.ui.UiController new file mode 100644 index 00000000000..523d6eee211 --- /dev/null +++ b/tests/robotests/resources/META-INF/services/androidx.test.platform.ui.UiController @@ -0,0 +1 @@ +org.robolectric.android.internal.LocalUiController diff --git a/tests/robotests/src/com/android/settings/network/ActiveSubsciptionsListenerTest.java b/tests/robotests/src/com/android/settings/network/ActiveSubsciptionsListenerTest.java new file mode 100644 index 00000000000..a244f93280d --- /dev/null +++ b/tests/robotests/src/com/android/settings/network/ActiveSubsciptionsListenerTest.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 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.settings.network; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; + +import com.android.internal.telephony.TelephonyIntents; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +public class ActiveSubsciptionsListenerTest { + + @Mock + private SubscriptionManager mSubscriptionManager; + @Mock + private SubscriptionInfo mSubscriptionInfo1; + @Mock + private SubscriptionInfo mSubscriptionInfo2; + + private Context mContext; + private ActiveSubsciptionsListener mListener; + private List mActiveSubscriptions; + private BroadcastReceiver mSubscriptionChangeReceiver; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager); + mActiveSubscriptions = new ArrayList(); + } + + @Test + public void constructor_noListeningWasSetup() { + mListener = spy(new ActiveSubsciptionsListener(mContext) { + public void onChanged() {} + }); + verify(mSubscriptionManager, never()).addOnSubscriptionsChangedListener(any()); + verify(mContext, never()).registerReceiver(any(), any()); + verify(mListener, never()).onChanged(); + } + + @Test + public void start_onChangedShouldAlwaysBeCalled() { + mListener = spy(new ActiveSubsciptionsListener(mContext) { + public void onChanged() {} + }); + mSubscriptionChangeReceiver = spy(mListener.mSubscriptionChangeReceiver); + when(mSubscriptionChangeReceiver.isInitialStickyBroadcast()).thenReturn(false); + + mActiveSubscriptions.add(mSubscriptionInfo1); + mActiveSubscriptions.add(mSubscriptionInfo2); + when(mSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(mActiveSubscriptions); + + final Intent intentSubscription = + new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); + final Intent intentRadioTech = + new Intent(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); + + mSubscriptionChangeReceiver.onReceive(mContext, intentSubscription); + mSubscriptionChangeReceiver.onReceive(mContext, intentRadioTech); + verify(mListener, never()).onChanged(); + + mListener.start(); + + mSubscriptionChangeReceiver.onReceive(mContext, intentSubscription); + verify(mListener, atLeastOnce()).onChanged(); + + mSubscriptionChangeReceiver.onReceive(mContext, intentRadioTech); + verify(mListener, times(1)).onChanged(); + + mListener.stop(); + + mContext.sendStickyBroadcast(intentSubscription); + mContext.sendStickyBroadcast(intentRadioTech); + verify(mListener, times(1)).onChanged(); + } + + @Test + public void constructor_alwaysFetchAndCacheResult() { + mListener = spy(new ActiveSubsciptionsListener(mContext) { + public void onChanged() {} + }); + mActiveSubscriptions.add(mSubscriptionInfo1); + mActiveSubscriptions.add(mSubscriptionInfo2); + + List subInfoList = null; + int numberOfAccess = 0; + for (int numberOfSubInfo = mActiveSubscriptions.size(); numberOfSubInfo >= 0; + numberOfSubInfo--) { + if (mActiveSubscriptions.size() > numberOfSubInfo) { + mActiveSubscriptions.remove(numberOfSubInfo); + } + when(mSubscriptionManager.getActiveSubscriptionInfoList()) + .thenReturn(mActiveSubscriptions); + + // fetch twice and test if they generated access to SubscriptionManager only once + subInfoList = mListener.getActiveSubscriptionsInfo(); + subInfoList = mListener.getActiveSubscriptionsInfo(); + + numberOfAccess++; + verify(mSubscriptionManager, times(numberOfAccess)).getActiveSubscriptionInfoList(); + + mListener.clearCache(); + } + + when(mSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(null); + + // fetch twice and test if they generated access to SubscriptionManager only once + subInfoList = mListener.getActiveSubscriptionsInfo(); + subInfoList = mListener.getActiveSubscriptionsInfo(); + + numberOfAccess++; + verify(mSubscriptionManager, times(numberOfAccess)).getActiveSubscriptionInfoList(); + } +} diff --git a/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkActivityTest.java b/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkActivityTest.java index 59fd510d6fd..d1a5e428381 100644 --- a/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkActivityTest.java +++ b/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkActivityTest.java @@ -16,23 +16,18 @@ package com.android.settings.network.telephony; -import static com.android.settings.network.telephony.MobileNetworkActivity.MOBILE_SETTINGS_TAG; +import static androidx.lifecycle.Lifecycle.State; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; -import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.os.UserManager; import android.provider.Settings; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; @@ -41,12 +36,14 @@ import android.telephony.TelephonyManager; import android.view.Menu; import android.view.View; +import androidx.test.core.app.ActivityScenario; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + import com.android.internal.telephony.TelephonyIntents; -import com.android.internal.view.menu.ContextMenuBuilder; import com.android.settings.R; import com.android.settings.core.FeatureFlags; import com.android.settings.development.featureflags.FeatureFlagPersistent; -import com.android.settings.network.SubscriptionUtil; import com.google.android.material.bottomnavigation.BottomNavigationView; @@ -56,213 +53,193 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.robolectric.Robolectric; -import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowContextImpl; +import org.robolectric.shadows.ShadowSubscriptionManager; +import org.robolectric.shadows.ShadowSubscriptionManager.SubscriptionInfoBuilder; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; - -@RunWith(RobolectricTestRunner.class) +@RunWith(AndroidJUnit4.class) public class MobileNetworkActivityTest { private static final int CURRENT_SUB_ID = 3; private static final int PREV_SUB_ID = 1; private Context mContext; - private MobileNetworkActivity mMobileNetworkActivity; - private List mSubscriptionInfos; - private Fragment mShowFragment; - private Fragment mHideFragment; + private ShadowContextImpl mShadowContextImpl; + private Intent mTestIntent; @Mock - private SubscriptionManager mSubscriptionManager; + private UserManager mUserManager; @Mock private TelephonyManager mTelephonyManager; - @Mock - private SubscriptionInfo mSubscriptionInfo; - @Mock + + private ShadowSubscriptionManager mSubscriptionManager; + private SubscriptionInfo mSubscriptionInfo1; private SubscriptionInfo mSubscriptionInfo2; - @Mock - private FragmentManager mFragmentManager; - @Mock - private FragmentTransaction mFragmentTransaction; - @Mock - private BottomNavigationView mBottomNavigationView; + + private ActivityScenario mMobileNetworkActivity; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mContext = spy(RuntimeEnvironment.application); - mMobileNetworkActivity = spy(new MobileNetworkActivity()); - mSubscriptionInfos = new ArrayList<>(); - mShowFragment = new Fragment(); - mHideFragment = new Fragment(); - mMobileNetworkActivity.mSubscriptionInfos = mSubscriptionInfos; - mMobileNetworkActivity.mSubscriptionManager = mSubscriptionManager; - when(mSubscriptionInfo.getSubscriptionId()).thenReturn(PREV_SUB_ID); - when(mSubscriptionInfo2.getSubscriptionId()).thenReturn(CURRENT_SUB_ID); + mContext = ApplicationProvider.getApplicationContext(); + mShadowContextImpl = Shadow.extract(RuntimeEnvironment.application.getBaseContext()); - doReturn(mSubscriptionManager).when(mMobileNetworkActivity).getSystemService( - SubscriptionManager.class); - doReturn(mTelephonyManager).when(mMobileNetworkActivity).getSystemService( - TelephonyManager.class); - doReturn(mBottomNavigationView).when(mMobileNetworkActivity).findViewById(R.id.bottom_nav); - doReturn(mFragmentManager).when(mMobileNetworkActivity).getSupportFragmentManager(); - doReturn(mFragmentTransaction).when(mFragmentManager).beginTransaction(); - doReturn(mHideFragment).when(mFragmentManager).findFragmentByTag( - MOBILE_SETTINGS_TAG + PREV_SUB_ID); - doReturn(mShowFragment).when(mFragmentManager).findFragmentByTag( - MOBILE_SETTINGS_TAG + CURRENT_SUB_ID); + mShadowContextImpl.setSystemService(Context.USER_SERVICE, mUserManager); + doReturn(true).when(mUserManager).isAdminUser(); + + mShadowContextImpl.setSystemService(Context.TELEPHONY_SERVICE, mTelephonyManager); + doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt()); + + mTestIntent = new Intent(mContext, MockMobileNetworkActivity.class); + + mSubscriptionManager = shadowOf(mContext.getSystemService(SubscriptionManager.class)); + mSubscriptionInfo1 = SubscriptionInfoBuilder.newBuilder() + .setId(PREV_SUB_ID).buildSubscriptionInfo(); + mSubscriptionInfo2 = SubscriptionInfoBuilder.newBuilder() + .setId(CURRENT_SUB_ID).buildSubscriptionInfo(); } @After - public void tearDown() { - SubscriptionUtil.setAvailableSubscriptionsForTesting(null); + public void cleanUp() { + if (mMobileNetworkActivity != null) { + mMobileNetworkActivity.close(); + } + } + + private static class MockMobileNetworkActivity extends MobileNetworkActivity { + private MockMobileNetworkActivity() { + super(); + } + + private SubscriptionInfo mSubscriptionInFragment; + + @Override + void switchFragment(SubscriptionInfo subInfo) { + mSubscriptionInFragment = subInfo; + } + } + + private ActivityScenario createTargetActivity(Intent activityIntent, + boolean isInternetV2) { + FeatureFlagPersistent.setEnabled(mContext, FeatureFlags.NETWORK_INTERNET_V2, isInternetV2); + return ActivityScenario.launch(activityIntent); } @Test public void updateBottomNavigationView_oneSubscription_shouldBeGone() { - mSubscriptionInfos.add(mSubscriptionInfo); - doReturn(mSubscriptionInfos).when(mSubscriptionManager).getActiveSubscriptionInfoList( - eq(true)); + mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1); - mMobileNetworkActivity.updateBottomNavigationView(); + mMobileNetworkActivity = createTargetActivity(mTestIntent, false); - verify(mBottomNavigationView).setVisibility(View.GONE); + mMobileNetworkActivity.moveToState(State.STARTED); + + mMobileNetworkActivity.onActivity(activity -> { + final BottomNavigationView bottomNavigationView = + activity.findViewById(R.id.bottom_nav); + assertThat(bottomNavigationView.getVisibility()).isEqualTo(View.GONE); + }); + } + + @Test + public void updateBottomNavigationViewV2_oneSubscription_shouldNotCrash() { + mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1); + + mMobileNetworkActivity = createTargetActivity(mTestIntent, true); + + mMobileNetworkActivity.moveToState(State.STARTED); } @Test public void updateBottomNavigationView_twoSubscription_updateMenu() { - final Menu menu = new ContextMenuBuilder(mContext); - mSubscriptionInfos.add(mSubscriptionInfo); - mSubscriptionInfos.add(mSubscriptionInfo); - doReturn(mSubscriptionInfos).when(mSubscriptionManager).getActiveSubscriptionInfoList( - eq(true)); - doReturn(menu).when(mBottomNavigationView).getMenu(); + mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1, mSubscriptionInfo2); - mMobileNetworkActivity.updateBottomNavigationView(); + mMobileNetworkActivity = createTargetActivity(mTestIntent, false); - assertThat(menu.size()).isEqualTo(2); + mMobileNetworkActivity.moveToState(State.STARTED); + + mMobileNetworkActivity.onActivity(activity -> { + final BottomNavigationView bottomNavigationView = + activity.findViewById(R.id.bottom_nav); + final Menu menu = bottomNavigationView.getMenu(); + assertThat(menu.size()).isEqualTo(2); + }); } @Test - public void switchFragment_newFragment_replaceIt() { - mMobileNetworkActivity.mCurSubscriptionId = PREV_SUB_ID; + public void updateBottomNavigationViewV2_twoSubscription_shouldNotCrash() { + mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1, mSubscriptionInfo2); - mMobileNetworkActivity.switchFragment(mShowFragment, CURRENT_SUB_ID); + mMobileNetworkActivity = createTargetActivity(mTestIntent, true); - verify(mFragmentTransaction).replace(R.id.main_content, mShowFragment, - MOBILE_SETTINGS_TAG + CURRENT_SUB_ID); + mMobileNetworkActivity.moveToState(State.STARTED); } @Test - public void phoneChangeReceiver_ignoresStickyBroadcastFromBeforeRegistering() { - Context activity = mContext; - MobileNetworkActivity.PhoneChangeReceiver.Client client = mock( - MobileNetworkActivity.PhoneChangeReceiver.Client.class); - MobileNetworkActivity.PhoneChangeReceiver receiver = - new MobileNetworkActivity.PhoneChangeReceiver(activity, client); - Intent intent = new Intent(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); - activity.sendStickyBroadcast(intent); + public void switchFragment_switchBetweenTwoSubscriptions() { + mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1, mSubscriptionInfo2); - receiver.register(); - verify(client, never()).onPhoneChange(); + mTestIntent.putExtra(Settings.EXTRA_SUB_ID, PREV_SUB_ID); + mMobileNetworkActivity = createTargetActivity(mTestIntent, false); - activity.sendStickyBroadcast(intent); - verify(client, times(1)).onPhoneChange(); + mMobileNetworkActivity.moveToState(State.STARTED); + + mMobileNetworkActivity.onActivity(activity -> { + final MockMobileNetworkActivity mockActivity = (MockMobileNetworkActivity) activity; + assertThat(mockActivity.mSubscriptionInFragment).isEqualTo(mSubscriptionInfo1); + + final BottomNavigationView bottomNavigationView = + mockActivity.findViewById(R.id.bottom_nav); + bottomNavigationView.setSelectedItemId(CURRENT_SUB_ID); + assertThat(mockActivity.mSubscriptionInFragment).isEqualTo(mSubscriptionInfo2); + + bottomNavigationView.setSelectedItemId(PREV_SUB_ID); + assertThat(mockActivity.mSubscriptionInFragment).isEqualTo(mSubscriptionInfo1); + }); } @Test - public void phoneChangeReceiver_ignoresCarrierConfigChangeForWrongSubscriptionId() { - Context activity = mContext; + public void switchFragment_subscriptionsUpdate_notifyByIntent() { + mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1, mSubscriptionInfo2); - MobileNetworkActivity.PhoneChangeReceiver.Client client = mock( - MobileNetworkActivity.PhoneChangeReceiver.Client.class); - doReturn(2).when(client).getSubscriptionId(); + mTestIntent.putExtra(Settings.EXTRA_SUB_ID, PREV_SUB_ID); + mMobileNetworkActivity = createTargetActivity(mTestIntent, true); - MobileNetworkActivity.PhoneChangeReceiver receiver = - new MobileNetworkActivity.PhoneChangeReceiver(activity, client); + mMobileNetworkActivity.moveToState(State.STARTED); - receiver.register(); + mMobileNetworkActivity.onActivity(activity -> { + final MockMobileNetworkActivity mockActivity = (MockMobileNetworkActivity) activity; + assertThat(mockActivity.mSubscriptionInFragment).isEqualTo(mSubscriptionInfo1); - Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); - intent.putExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, 3); - activity.sendBroadcast(intent); - verify(client, never()).onPhoneChange(); - } + mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo2); + mContext.sendBroadcast(new Intent( + CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED), null); - @Test - public void phoneChangeReceiver_dispatchesCarrierConfigChangeForCorrectSubscriptionId() { - Context activity = mContext; + assertThat(mockActivity.mSubscriptionInFragment).isEqualTo(mSubscriptionInfo2); - MobileNetworkActivity.PhoneChangeReceiver.Client client = mock( - MobileNetworkActivity.PhoneChangeReceiver.Client.class); - doReturn(2).when(client).getSubscriptionId(); + mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1); + mContext.sendBroadcast(new Intent( + TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED), null); - MobileNetworkActivity.PhoneChangeReceiver receiver = - new MobileNetworkActivity.PhoneChangeReceiver(activity, client); - - receiver.register(); - - Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); - intent.putExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, 2); - activity.sendBroadcast(intent); - verify(client).onPhoneChange(); - } - - - @Test - public void getSubscriptionId_hasIntent_getIdFromIntent() { - final Intent intent = new Intent(); - intent.putExtra(Settings.EXTRA_SUB_ID, CURRENT_SUB_ID); - doReturn(intent).when(mMobileNetworkActivity).getIntent(); - mSubscriptionInfos.add(mSubscriptionInfo); - mSubscriptionInfos.add(mSubscriptionInfo2); - SubscriptionUtil.setAvailableSubscriptionsForTesting(mSubscriptionInfos); - doReturn(true).when(mSubscriptionManager).isActiveSubscriptionId(CURRENT_SUB_ID); - - assertThat(mMobileNetworkActivity.getSubscriptionId()).isEqualTo(CURRENT_SUB_ID); - } - - @Test - public void getSubscriptionId_noIntent_firstIdInList() { - doReturn(null).when(mMobileNetworkActivity).getIntent(); - mSubscriptionInfos.add(mSubscriptionInfo); - mSubscriptionInfos.add(mSubscriptionInfo2); - - assertThat(mMobileNetworkActivity.getSubscriptionId()).isEqualTo(PREV_SUB_ID); + assertThat(mockActivity.mSubscriptionInFragment).isEqualTo(mSubscriptionInfo1); + }); } @Test public void onSaveInstanceState_saveCurrentSubId() { - mMobileNetworkActivity = Robolectric.buildActivity(MobileNetworkActivity.class).get(); - mMobileNetworkActivity.mCurSubscriptionId = PREV_SUB_ID; - final Bundle bundle = new Bundle(); + mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1, mSubscriptionInfo2); - mMobileNetworkActivity.saveInstanceState(bundle); + mTestIntent.putExtra(Settings.EXTRA_SUB_ID, PREV_SUB_ID); + mMobileNetworkActivity = createTargetActivity(mTestIntent, false); - assertThat(bundle.getInt(Settings.EXTRA_SUB_ID)).isEqualTo(PREV_SUB_ID); - } + mMobileNetworkActivity.moveToState(State.STARTED); - @Test - public void onNewIntent_newSubscriptionId_fragmentReplaced() { - FeatureFlagPersistent.setEnabled(mContext, FeatureFlags.NETWORK_INTERNET_V2, true); - - mSubscriptionInfos.add(mSubscriptionInfo); - mSubscriptionInfos.add(mSubscriptionInfo2); - SubscriptionUtil.setAvailableSubscriptionsForTesting(mSubscriptionInfos); - mMobileNetworkActivity.mCurSubscriptionId = PREV_SUB_ID; - - final Intent newIntent = new Intent(); - newIntent.putExtra(Settings.EXTRA_SUB_ID, CURRENT_SUB_ID); - mMobileNetworkActivity.onNewIntent(newIntent); - assertThat(mMobileNetworkActivity.mCurSubscriptionId).isEqualTo(CURRENT_SUB_ID); + mMobileNetworkActivity.onActivity(activity -> { + final Bundle bundle = new Bundle(); + activity.saveInstanceState(bundle); + assertThat(bundle.getInt(Settings.EXTRA_SUB_ID)).isEqualTo(PREV_SUB_ID); + }); } }