Loading src/com/android/settings/network/ProxySubscriptionManager.java +103 −20 Original line number Diff line number Diff line Loading @@ -25,19 +25,30 @@ import android.os.Looper; import android.provider.Settings; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.util.Log; import androidx.annotation.Keep; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * A proxy to the subscription manager */ public class ProxySubscriptionManager implements LifecycleObserver { private static final String LOG_TAG = "ProxySubscriptionManager"; private static final int LISTENER_END_OF_LIFE = -1; private static final int LISTENER_IS_INACTIVE = 0; private static final int LISTENER_IS_ACTIVE = 1; /** * Interface for monitor active subscriptions list changing */ Loading Loading @@ -74,22 +85,36 @@ public class ProxySubscriptionManager implements LifecycleObserver { private ProxySubscriptionManager(Context context) { final Looper looper = context.getMainLooper(); mActiveSubscriptionsListeners = new ArrayList<OnActiveSubscriptionChangedListener>(); mSubscriptionMonitor = new ActiveSubscriptionsListener(looper, context) { ActiveSubscriptionsListener subscriptionMonitor = new ActiveSubscriptionsListener( looper, context) { public void onChanged() { notifyAllListeners(); notifySubscriptionInfoMightChanged(); } }; mAirplaneModeMonitor = new GlobalSettingsChangeListener(looper, context, Settings.Global.AIRPLANE_MODE_ON) { GlobalSettingsChangeListener airplaneModeMonitor = new GlobalSettingsChangeListener( looper, context, Settings.Global.AIRPLANE_MODE_ON) { public void onChanged(String field) { mSubscriptionMonitor.clearCache(); notifyAllListeners(); subscriptionMonitor.clearCache(); notifySubscriptionInfoMightChanged(); } }; init(context, subscriptionMonitor, airplaneModeMonitor); } @Keep @VisibleForTesting protected void init(Context context, ActiveSubscriptionsListener activeSubscriptionsListener, GlobalSettingsChangeListener airplaneModeOnSettingsChangeListener) { mActiveSubscriptionsListeners = new ArrayList<OnActiveSubscriptionChangedListener>(); mPendingNotifyListeners = new ArrayList<OnActiveSubscriptionChangedListener>(); mSubscriptionMonitor = activeSubscriptionsListener; mAirplaneModeMonitor = airplaneModeOnSettingsChangeListener; mSubscriptionMonitor.start(); } Loading @@ -98,15 +123,19 @@ public class ProxySubscriptionManager implements LifecycleObserver { private GlobalSettingsChangeListener mAirplaneModeMonitor; private List<OnActiveSubscriptionChangedListener> mActiveSubscriptionsListeners; private List<OnActiveSubscriptionChangedListener> mPendingNotifyListeners; private void notifyAllListeners() { for (OnActiveSubscriptionChangedListener listener : mActiveSubscriptionsListeners) { final Lifecycle lifecycle = listener.getLifecycle(); if ((lifecycle == null) || (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED))) { listener.onChanged(); } } @Keep @VisibleForTesting protected void notifySubscriptionInfoMightChanged() { // create a merged list for processing all listeners List<OnActiveSubscriptionChangedListener> listeners = new ArrayList<OnActiveSubscriptionChangedListener>(mPendingNotifyListeners); listeners.addAll(mActiveSubscriptionsListeners); mActiveSubscriptionsListeners.clear(); mPendingNotifyListeners.clear(); processStatusChangeOnListeners(listeners); } /** Loading @@ -131,6 +160,11 @@ public class ProxySubscriptionManager implements LifecycleObserver { @OnLifecycleEvent(ON_START) void onStart() { mSubscriptionMonitor.start(); // callback notify those listener(s) which back to active state List<OnActiveSubscriptionChangedListener> listeners = mPendingNotifyListeners; mPendingNotifyListeners = new ArrayList<OnActiveSubscriptionChangedListener>(); processStatusChangeOnListeners(listeners); } @OnLifecycleEvent(ON_STOP) Loading Loading @@ -215,12 +249,17 @@ public class ProxySubscriptionManager implements LifecycleObserver { } /** * Add listener to active subscriptions monitor list * Add listener to active subscriptions monitor list. * Note: listener only take place when change happens. * No immediate callback performed after the invoke of this method. * * @param listener listener to active subscriptions change */ @Keep public void addActiveSubscriptionsListener(OnActiveSubscriptionChangedListener listener) { if (mActiveSubscriptionsListeners.contains(listener)) { removeSpecificListenerAndCleanList(listener, mPendingNotifyListeners); removeSpecificListenerAndCleanList(listener, mActiveSubscriptionsListeners); if ((listener == null) || (getListenerState(listener) == LISTENER_END_OF_LIFE)) { return; } mActiveSubscriptionsListeners.add(listener); Loading @@ -231,7 +270,51 @@ public class ProxySubscriptionManager implements LifecycleObserver { * * @param listener listener to active subscriptions change */ @Keep public void removeActiveSubscriptionsListener(OnActiveSubscriptionChangedListener listener) { mActiveSubscriptionsListeners.remove(listener); removeSpecificListenerAndCleanList(listener, mPendingNotifyListeners); removeSpecificListenerAndCleanList(listener, mActiveSubscriptionsListeners); } private int getListenerState(OnActiveSubscriptionChangedListener listener) { Lifecycle lifecycle = listener.getLifecycle(); if (lifecycle == null) { return LISTENER_IS_ACTIVE; } Lifecycle.State lifecycleState = lifecycle.getCurrentState(); if (lifecycleState == Lifecycle.State.DESTROYED) { Log.d(LOG_TAG, "Listener dead detected - " + listener); return LISTENER_END_OF_LIFE; } return lifecycleState.isAtLeast(Lifecycle.State.STARTED) ? LISTENER_IS_ACTIVE : LISTENER_IS_INACTIVE; } private void removeSpecificListenerAndCleanList(OnActiveSubscriptionChangedListener listener, List<OnActiveSubscriptionChangedListener> list) { // also drop listener(s) which is end of life list.removeIf(it -> (it == listener) || (getListenerState(it) == LISTENER_END_OF_LIFE)); } private void processStatusChangeOnListeners( List<OnActiveSubscriptionChangedListener> listeners) { // categorize listener(s), and end of life listener(s) been ignored Map<Integer, List<OnActiveSubscriptionChangedListener>> categorizedListeners = listeners.stream() .collect(Collectors.groupingBy(it -> getListenerState(it))); // have inactive listener(s) in pending list categorizedListeners.computeIfPresent(LISTENER_IS_INACTIVE, (category, list) -> { mPendingNotifyListeners.addAll(list); return list; }); // get active listener(s) categorizedListeners.computeIfPresent(LISTENER_IS_ACTIVE, (category, list) -> { mActiveSubscriptionsListeners.addAll(list); // notify each one of them list.stream().forEach(it -> it.onChanged()); return list; }); } } src/com/android/settings/network/telephony/MobileNetworkActivity.java +4 −6 Original line number Diff line number Diff line Loading @@ -132,15 +132,13 @@ public class MobileNetworkActivity extends SettingsBaseActivity : ((startIntent != null) ? startIntent.getIntExtra(Settings.EXTRA_SUB_ID, SUB_ID_NULL) : SUB_ID_NULL); // perform registration after mCurSubscriptionId been configured. registerActiveSubscriptionsListener(); final SubscriptionInfo subscription = getSubscription(); maybeShowContactDiscoveryDialog(subscription); // Since onChanged() will take place immediately when addActiveSubscriptionsListener(), // perform registration after mCurSubscriptionId been configured. registerActiveSubscriptionsListener(); updateSubscriptions(subscription, savedInstanceState); updateSubscriptions(subscription, null); } @VisibleForTesting Loading Loading @@ -296,7 +294,7 @@ public class MobileNetworkActivity extends SettingsBaseActivity final Fragment fragment = new MobileNetworkSettings(); fragment.setArguments(bundle); fragmentTransaction.replace(R.id.content_frame, fragment, fragmentTag); fragmentTransaction.commit(); fragmentTransaction.commitAllowingStateLoss(); } private void removeContactDiscoveryDialog(int subId) { Loading tests/unit/src/com/android/settings/network/ProxySubscriptionManagerTest.java 0 → 100644 +228 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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 com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.content.Context; import androidx.lifecycle.Lifecycle; import androidx.test.annotation.UiThreadTest; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class ProxySubscriptionManagerTest { private Context mContext; @Mock private ActiveSubscriptionsListener mActiveSubscriptionsListener; @Mock private GlobalSettingsChangeListener mAirplaneModeOnSettingsChangeListener; @Mock private Lifecycle mLifecycle_ON_PAUSE; @Mock private Lifecycle mLifecycle_ON_RESUME; @Mock private Lifecycle mLifecycle_ON_DESTROY; private Client mClient1; private Client mClient2; @Before @UiThreadTest public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(ApplicationProvider.getApplicationContext()); doReturn(Lifecycle.State.CREATED).when(mLifecycle_ON_PAUSE).getCurrentState(); doReturn(Lifecycle.State.STARTED).when(mLifecycle_ON_RESUME).getCurrentState(); doReturn(Lifecycle.State.DESTROYED).when(mLifecycle_ON_DESTROY).getCurrentState(); mClient1 = new Client(); mClient1.setLifecycle(mLifecycle_ON_RESUME); mClient2 = new Client(); mClient2.setLifecycle(mLifecycle_ON_RESUME); } private ProxySubscriptionManager getInstance(Context context) { ProxySubscriptionManager proxy = Mockito.mock(ProxySubscriptionManager.class, Mockito.CALLS_REAL_METHODS); proxy.init(context, mActiveSubscriptionsListener, mAirplaneModeOnSettingsChangeListener); proxy.notifySubscriptionInfoMightChanged(); return proxy; } public class Client implements ProxySubscriptionManager.OnActiveSubscriptionChangedListener { private Lifecycle lifeCycle; private int numberOfCallback; public void onChanged() { numberOfCallback++; } public Lifecycle getLifecycle() { return lifeCycle; } public int getCallbackCount() { return numberOfCallback; } public void setLifecycle(Lifecycle lifecycle) { lifeCycle = lifecycle; } } @Test @UiThreadTest public void addActiveSubscriptionsListener_addOneClient_getNoCallback() { ProxySubscriptionManager proxy = getInstance(mContext); proxy.addActiveSubscriptionsListener(mClient1); assertThat(mClient1.getCallbackCount()).isEqualTo(0); } @Test @UiThreadTest public void addActiveSubscriptionsListener_addOneClient_changeOnSimGetCallback() { ProxySubscriptionManager proxy = getInstance(mContext); proxy.addActiveSubscriptionsListener(mClient1); assertThat(mClient1.getCallbackCount()).isEqualTo(0); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(1); } @Test @UiThreadTest public void addActiveSubscriptionsListener_addOneClient_noCallbackUntilUiResume() { ProxySubscriptionManager proxy = getInstance(mContext); mClient1.setLifecycle(mLifecycle_ON_PAUSE); proxy.addActiveSubscriptionsListener(mClient1); assertThat(mClient1.getCallbackCount()).isEqualTo(0); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(0); mClient1.setLifecycle(mLifecycle_ON_RESUME); proxy.onStart(); Assert.assertTrue(mClient1.getCallbackCount() > 0); mClient1.setLifecycle(mLifecycle_ON_PAUSE); proxy.onStop(); int latestCallbackCount = mClient1.getCallbackCount(); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(latestCallbackCount); } @Test @UiThreadTest public void addActiveSubscriptionsListener_addTwoClient_eachClientGetNoCallback() { ProxySubscriptionManager proxy = getInstance(mContext); proxy.addActiveSubscriptionsListener(mClient1); assertThat(mClient1.getCallbackCount()).isEqualTo(0); proxy.addActiveSubscriptionsListener(mClient2); assertThat(mClient1.getCallbackCount()).isEqualTo(0); assertThat(mClient2.getCallbackCount()).isEqualTo(0); } @Test @UiThreadTest public void addActiveSubscriptionsListener_addTwoClient_callbackOnlyWhenResume() { ProxySubscriptionManager proxy = getInstance(mContext); proxy.addActiveSubscriptionsListener(mClient1); assertThat(mClient1.getCallbackCount()).isEqualTo(0); proxy.addActiveSubscriptionsListener(mClient2); assertThat(mClient1.getCallbackCount()).isEqualTo(0); assertThat(mClient2.getCallbackCount()).isEqualTo(0); mClient1.setLifecycle(mLifecycle_ON_PAUSE); proxy.onStop(); assertThat(mClient1.getCallbackCount()).isEqualTo(0); assertThat(mClient2.getCallbackCount()).isEqualTo(0); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(0); assertThat(mClient2.getCallbackCount()).isEqualTo(1); mClient1.setLifecycle(mLifecycle_ON_RESUME); proxy.onStart(); Assert.assertTrue(mClient1.getCallbackCount() > 0); assertThat(mClient2.getCallbackCount()).isEqualTo(1); } @Test @UiThreadTest public void removeActiveSubscriptionsListener_removedClient_noCallback() { ProxySubscriptionManager proxy = getInstance(mContext); proxy.addActiveSubscriptionsListener(mClient1); assertThat(mClient1.getCallbackCount()).isEqualTo(0); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(1); proxy.removeActiveSubscriptionsListener(mClient1); assertThat(mClient1.getCallbackCount()).isEqualTo(1); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(1); } @Test @UiThreadTest public void notifySubscriptionInfoMightChanged_destroyedClient_autoRemove() { ProxySubscriptionManager proxy = getInstance(mContext); proxy.addActiveSubscriptionsListener(mClient1); assertThat(mClient1.getCallbackCount()).isEqualTo(0); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(1); mClient1.setLifecycle(mLifecycle_ON_DESTROY); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(1); mClient1.setLifecycle(mLifecycle_ON_RESUME); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(1); } } Loading
src/com/android/settings/network/ProxySubscriptionManager.java +103 −20 Original line number Diff line number Diff line Loading @@ -25,19 +25,30 @@ import android.os.Looper; import android.provider.Settings; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.util.Log; import androidx.annotation.Keep; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * A proxy to the subscription manager */ public class ProxySubscriptionManager implements LifecycleObserver { private static final String LOG_TAG = "ProxySubscriptionManager"; private static final int LISTENER_END_OF_LIFE = -1; private static final int LISTENER_IS_INACTIVE = 0; private static final int LISTENER_IS_ACTIVE = 1; /** * Interface for monitor active subscriptions list changing */ Loading Loading @@ -74,22 +85,36 @@ public class ProxySubscriptionManager implements LifecycleObserver { private ProxySubscriptionManager(Context context) { final Looper looper = context.getMainLooper(); mActiveSubscriptionsListeners = new ArrayList<OnActiveSubscriptionChangedListener>(); mSubscriptionMonitor = new ActiveSubscriptionsListener(looper, context) { ActiveSubscriptionsListener subscriptionMonitor = new ActiveSubscriptionsListener( looper, context) { public void onChanged() { notifyAllListeners(); notifySubscriptionInfoMightChanged(); } }; mAirplaneModeMonitor = new GlobalSettingsChangeListener(looper, context, Settings.Global.AIRPLANE_MODE_ON) { GlobalSettingsChangeListener airplaneModeMonitor = new GlobalSettingsChangeListener( looper, context, Settings.Global.AIRPLANE_MODE_ON) { public void onChanged(String field) { mSubscriptionMonitor.clearCache(); notifyAllListeners(); subscriptionMonitor.clearCache(); notifySubscriptionInfoMightChanged(); } }; init(context, subscriptionMonitor, airplaneModeMonitor); } @Keep @VisibleForTesting protected void init(Context context, ActiveSubscriptionsListener activeSubscriptionsListener, GlobalSettingsChangeListener airplaneModeOnSettingsChangeListener) { mActiveSubscriptionsListeners = new ArrayList<OnActiveSubscriptionChangedListener>(); mPendingNotifyListeners = new ArrayList<OnActiveSubscriptionChangedListener>(); mSubscriptionMonitor = activeSubscriptionsListener; mAirplaneModeMonitor = airplaneModeOnSettingsChangeListener; mSubscriptionMonitor.start(); } Loading @@ -98,15 +123,19 @@ public class ProxySubscriptionManager implements LifecycleObserver { private GlobalSettingsChangeListener mAirplaneModeMonitor; private List<OnActiveSubscriptionChangedListener> mActiveSubscriptionsListeners; private List<OnActiveSubscriptionChangedListener> mPendingNotifyListeners; private void notifyAllListeners() { for (OnActiveSubscriptionChangedListener listener : mActiveSubscriptionsListeners) { final Lifecycle lifecycle = listener.getLifecycle(); if ((lifecycle == null) || (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED))) { listener.onChanged(); } } @Keep @VisibleForTesting protected void notifySubscriptionInfoMightChanged() { // create a merged list for processing all listeners List<OnActiveSubscriptionChangedListener> listeners = new ArrayList<OnActiveSubscriptionChangedListener>(mPendingNotifyListeners); listeners.addAll(mActiveSubscriptionsListeners); mActiveSubscriptionsListeners.clear(); mPendingNotifyListeners.clear(); processStatusChangeOnListeners(listeners); } /** Loading @@ -131,6 +160,11 @@ public class ProxySubscriptionManager implements LifecycleObserver { @OnLifecycleEvent(ON_START) void onStart() { mSubscriptionMonitor.start(); // callback notify those listener(s) which back to active state List<OnActiveSubscriptionChangedListener> listeners = mPendingNotifyListeners; mPendingNotifyListeners = new ArrayList<OnActiveSubscriptionChangedListener>(); processStatusChangeOnListeners(listeners); } @OnLifecycleEvent(ON_STOP) Loading Loading @@ -215,12 +249,17 @@ public class ProxySubscriptionManager implements LifecycleObserver { } /** * Add listener to active subscriptions monitor list * Add listener to active subscriptions monitor list. * Note: listener only take place when change happens. * No immediate callback performed after the invoke of this method. * * @param listener listener to active subscriptions change */ @Keep public void addActiveSubscriptionsListener(OnActiveSubscriptionChangedListener listener) { if (mActiveSubscriptionsListeners.contains(listener)) { removeSpecificListenerAndCleanList(listener, mPendingNotifyListeners); removeSpecificListenerAndCleanList(listener, mActiveSubscriptionsListeners); if ((listener == null) || (getListenerState(listener) == LISTENER_END_OF_LIFE)) { return; } mActiveSubscriptionsListeners.add(listener); Loading @@ -231,7 +270,51 @@ public class ProxySubscriptionManager implements LifecycleObserver { * * @param listener listener to active subscriptions change */ @Keep public void removeActiveSubscriptionsListener(OnActiveSubscriptionChangedListener listener) { mActiveSubscriptionsListeners.remove(listener); removeSpecificListenerAndCleanList(listener, mPendingNotifyListeners); removeSpecificListenerAndCleanList(listener, mActiveSubscriptionsListeners); } private int getListenerState(OnActiveSubscriptionChangedListener listener) { Lifecycle lifecycle = listener.getLifecycle(); if (lifecycle == null) { return LISTENER_IS_ACTIVE; } Lifecycle.State lifecycleState = lifecycle.getCurrentState(); if (lifecycleState == Lifecycle.State.DESTROYED) { Log.d(LOG_TAG, "Listener dead detected - " + listener); return LISTENER_END_OF_LIFE; } return lifecycleState.isAtLeast(Lifecycle.State.STARTED) ? LISTENER_IS_ACTIVE : LISTENER_IS_INACTIVE; } private void removeSpecificListenerAndCleanList(OnActiveSubscriptionChangedListener listener, List<OnActiveSubscriptionChangedListener> list) { // also drop listener(s) which is end of life list.removeIf(it -> (it == listener) || (getListenerState(it) == LISTENER_END_OF_LIFE)); } private void processStatusChangeOnListeners( List<OnActiveSubscriptionChangedListener> listeners) { // categorize listener(s), and end of life listener(s) been ignored Map<Integer, List<OnActiveSubscriptionChangedListener>> categorizedListeners = listeners.stream() .collect(Collectors.groupingBy(it -> getListenerState(it))); // have inactive listener(s) in pending list categorizedListeners.computeIfPresent(LISTENER_IS_INACTIVE, (category, list) -> { mPendingNotifyListeners.addAll(list); return list; }); // get active listener(s) categorizedListeners.computeIfPresent(LISTENER_IS_ACTIVE, (category, list) -> { mActiveSubscriptionsListeners.addAll(list); // notify each one of them list.stream().forEach(it -> it.onChanged()); return list; }); } }
src/com/android/settings/network/telephony/MobileNetworkActivity.java +4 −6 Original line number Diff line number Diff line Loading @@ -132,15 +132,13 @@ public class MobileNetworkActivity extends SettingsBaseActivity : ((startIntent != null) ? startIntent.getIntExtra(Settings.EXTRA_SUB_ID, SUB_ID_NULL) : SUB_ID_NULL); // perform registration after mCurSubscriptionId been configured. registerActiveSubscriptionsListener(); final SubscriptionInfo subscription = getSubscription(); maybeShowContactDiscoveryDialog(subscription); // Since onChanged() will take place immediately when addActiveSubscriptionsListener(), // perform registration after mCurSubscriptionId been configured. registerActiveSubscriptionsListener(); updateSubscriptions(subscription, savedInstanceState); updateSubscriptions(subscription, null); } @VisibleForTesting Loading Loading @@ -296,7 +294,7 @@ public class MobileNetworkActivity extends SettingsBaseActivity final Fragment fragment = new MobileNetworkSettings(); fragment.setArguments(bundle); fragmentTransaction.replace(R.id.content_frame, fragment, fragmentTag); fragmentTransaction.commit(); fragmentTransaction.commitAllowingStateLoss(); } private void removeContactDiscoveryDialog(int subId) { Loading
tests/unit/src/com/android/settings/network/ProxySubscriptionManagerTest.java 0 → 100644 +228 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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 com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.content.Context; import androidx.lifecycle.Lifecycle; import androidx.test.annotation.UiThreadTest; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class ProxySubscriptionManagerTest { private Context mContext; @Mock private ActiveSubscriptionsListener mActiveSubscriptionsListener; @Mock private GlobalSettingsChangeListener mAirplaneModeOnSettingsChangeListener; @Mock private Lifecycle mLifecycle_ON_PAUSE; @Mock private Lifecycle mLifecycle_ON_RESUME; @Mock private Lifecycle mLifecycle_ON_DESTROY; private Client mClient1; private Client mClient2; @Before @UiThreadTest public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(ApplicationProvider.getApplicationContext()); doReturn(Lifecycle.State.CREATED).when(mLifecycle_ON_PAUSE).getCurrentState(); doReturn(Lifecycle.State.STARTED).when(mLifecycle_ON_RESUME).getCurrentState(); doReturn(Lifecycle.State.DESTROYED).when(mLifecycle_ON_DESTROY).getCurrentState(); mClient1 = new Client(); mClient1.setLifecycle(mLifecycle_ON_RESUME); mClient2 = new Client(); mClient2.setLifecycle(mLifecycle_ON_RESUME); } private ProxySubscriptionManager getInstance(Context context) { ProxySubscriptionManager proxy = Mockito.mock(ProxySubscriptionManager.class, Mockito.CALLS_REAL_METHODS); proxy.init(context, mActiveSubscriptionsListener, mAirplaneModeOnSettingsChangeListener); proxy.notifySubscriptionInfoMightChanged(); return proxy; } public class Client implements ProxySubscriptionManager.OnActiveSubscriptionChangedListener { private Lifecycle lifeCycle; private int numberOfCallback; public void onChanged() { numberOfCallback++; } public Lifecycle getLifecycle() { return lifeCycle; } public int getCallbackCount() { return numberOfCallback; } public void setLifecycle(Lifecycle lifecycle) { lifeCycle = lifecycle; } } @Test @UiThreadTest public void addActiveSubscriptionsListener_addOneClient_getNoCallback() { ProxySubscriptionManager proxy = getInstance(mContext); proxy.addActiveSubscriptionsListener(mClient1); assertThat(mClient1.getCallbackCount()).isEqualTo(0); } @Test @UiThreadTest public void addActiveSubscriptionsListener_addOneClient_changeOnSimGetCallback() { ProxySubscriptionManager proxy = getInstance(mContext); proxy.addActiveSubscriptionsListener(mClient1); assertThat(mClient1.getCallbackCount()).isEqualTo(0); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(1); } @Test @UiThreadTest public void addActiveSubscriptionsListener_addOneClient_noCallbackUntilUiResume() { ProxySubscriptionManager proxy = getInstance(mContext); mClient1.setLifecycle(mLifecycle_ON_PAUSE); proxy.addActiveSubscriptionsListener(mClient1); assertThat(mClient1.getCallbackCount()).isEqualTo(0); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(0); mClient1.setLifecycle(mLifecycle_ON_RESUME); proxy.onStart(); Assert.assertTrue(mClient1.getCallbackCount() > 0); mClient1.setLifecycle(mLifecycle_ON_PAUSE); proxy.onStop(); int latestCallbackCount = mClient1.getCallbackCount(); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(latestCallbackCount); } @Test @UiThreadTest public void addActiveSubscriptionsListener_addTwoClient_eachClientGetNoCallback() { ProxySubscriptionManager proxy = getInstance(mContext); proxy.addActiveSubscriptionsListener(mClient1); assertThat(mClient1.getCallbackCount()).isEqualTo(0); proxy.addActiveSubscriptionsListener(mClient2); assertThat(mClient1.getCallbackCount()).isEqualTo(0); assertThat(mClient2.getCallbackCount()).isEqualTo(0); } @Test @UiThreadTest public void addActiveSubscriptionsListener_addTwoClient_callbackOnlyWhenResume() { ProxySubscriptionManager proxy = getInstance(mContext); proxy.addActiveSubscriptionsListener(mClient1); assertThat(mClient1.getCallbackCount()).isEqualTo(0); proxy.addActiveSubscriptionsListener(mClient2); assertThat(mClient1.getCallbackCount()).isEqualTo(0); assertThat(mClient2.getCallbackCount()).isEqualTo(0); mClient1.setLifecycle(mLifecycle_ON_PAUSE); proxy.onStop(); assertThat(mClient1.getCallbackCount()).isEqualTo(0); assertThat(mClient2.getCallbackCount()).isEqualTo(0); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(0); assertThat(mClient2.getCallbackCount()).isEqualTo(1); mClient1.setLifecycle(mLifecycle_ON_RESUME); proxy.onStart(); Assert.assertTrue(mClient1.getCallbackCount() > 0); assertThat(mClient2.getCallbackCount()).isEqualTo(1); } @Test @UiThreadTest public void removeActiveSubscriptionsListener_removedClient_noCallback() { ProxySubscriptionManager proxy = getInstance(mContext); proxy.addActiveSubscriptionsListener(mClient1); assertThat(mClient1.getCallbackCount()).isEqualTo(0); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(1); proxy.removeActiveSubscriptionsListener(mClient1); assertThat(mClient1.getCallbackCount()).isEqualTo(1); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(1); } @Test @UiThreadTest public void notifySubscriptionInfoMightChanged_destroyedClient_autoRemove() { ProxySubscriptionManager proxy = getInstance(mContext); proxy.addActiveSubscriptionsListener(mClient1); assertThat(mClient1.getCallbackCount()).isEqualTo(0); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(1); mClient1.setLifecycle(mLifecycle_ON_DESTROY); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(1); mClient1.setLifecycle(mLifecycle_ON_RESUME); proxy.notifySubscriptionInfoMightChanged(); assertThat(mClient1.getCallbackCount()).isEqualTo(1); } }