[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
This commit is contained in:
Bonian Chen
2019-11-04 10:36:39 +08:00
parent e156734423
commit f1b43a970c
10 changed files with 574 additions and 300 deletions

View File

@@ -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",

View File

@@ -0,0 +1 @@
org.robolectric.android.internal.NoOpThreadChecker

View File

@@ -0,0 +1 @@
org.robolectric.android.internal.LocalActivityInvoker

View File

@@ -0,0 +1 @@
org.robolectric.android.internal.LocalPermissionGranter

View File

@@ -0,0 +1 @@
org.robolectric.android.internal.LocalControlledLooper

View File

@@ -0,0 +1 @@
org.robolectric.android.internal.LocalUiController

View File

@@ -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<SubscriptionInfo> 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<SubscriptionInfo>();
}
@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<SubscriptionInfo> 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();
}
}

View File

@@ -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<SubscriptionInfo> 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<MobileNetworkActivity> 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<MobileNetworkActivity> 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);
});
}
}