[Settings] Code refactor for SIM change detection

Code refactor for maintainance:
  Move subscription change monitor out of ResetnetworkConfirm

Bug: 259611847
Test: local test, auto testing
Change-Id: I97a251ced4435d0cc390b40028edef07fd71e24f
This commit is contained in:
Bonian Chen
2022-11-29 03:54:00 +00:00
parent 1439ba4200
commit 8a439d0a02
4 changed files with 294 additions and 63 deletions

View File

@@ -61,6 +61,7 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
@VisibleForTesting ResetNetworkRequest mResetNetworkRequest; @VisibleForTesting ResetNetworkRequest mResetNetworkRequest;
private ProgressDialog mProgressDialog; private ProgressDialog mProgressDialog;
private AlertDialog mAlertDialog; private AlertDialog mAlertDialog;
@VisibleForTesting ResetSubscriptionContract mResetSubscriptionContract;
private OnSubscriptionsChangedListener mSubscriptionsChangedListener; private OnSubscriptionsChangedListener mSubscriptionsChangedListener;
/** /**
@@ -130,16 +131,11 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
} }
// abandon execution if subscription no longer active // abandon execution if subscription no longer active
int subId = mResetNetworkRequest.getResetApnSubId(); Integer subId = mResetSubscriptionContract.getAnyMissingSubscriptionId();
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { if (subId != null) {
SubscriptionManager mgr = getSubscriptionManager(); Log.w(TAG, "subId " + subId + " no longer active");
// always remove listener getActivity().onBackPressed();
stopMonitorSubscriptionChange(mgr); return;
if (!isSubscriptionRemainActive(mgr, subId)) {
Log.w(TAG, "subId " + subId + " disappear when confirm");
mActivity.finish();
return;
}
} }
// Should dismiss the progress dialog firstly if it is showing // Should dismiss the progress dialog firstly if it is showing
@@ -186,7 +182,7 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
Bundle savedInstanceState) { Bundle savedInstanceState) {
View view = (new ResetNetworkRestrictionViewBuilder(mActivity)).build(); View view = (new ResetNetworkRestrictionViewBuilder(mActivity)).build();
if (view != null) { if (view != null) {
stopMonitorSubscriptionChange(getSubscriptionManager()); mResetSubscriptionContract.close();
Log.w(TAG, "Access deny."); Log.w(TAG, "Access deny.");
return view; return view;
} }
@@ -208,13 +204,15 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
mActivity = getActivity(); mActivity = getActivity();
if (mResetNetworkRequest.getResetApnSubId() mResetSubscriptionContract = new ResetSubscriptionContract(getContext(),
== ResetNetworkRequest.INVALID_SUBSCRIPTION_ID) { mResetNetworkRequest) {
return; @Override
} public void onSubscriptionInactive(int subscriptionId) {
// close confirmation dialog when reset specific subscription // close UI if subscription no longer active
// but removed priori to the confirmation button been pressed Log.w(TAG, "subId " + subscriptionId + " no longer active.");
startMonitorSubscriptionChange(getSubscriptionManager()); getActivity().onBackPressed();
}
};
} }
@Override @Override
@@ -223,63 +221,22 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
mResetNetworkRequest.writeIntoBundle(outState); mResetNetworkRequest.writeIntoBundle(outState);
} }
private SubscriptionManager getSubscriptionManager() {
SubscriptionManager mgr = mActivity.getSystemService(SubscriptionManager.class);
if (mgr == null) {
Log.w(TAG, "No SubscriptionManager");
}
return mgr;
}
private void startMonitorSubscriptionChange(SubscriptionManager mgr) {
if (mgr == null) {
return;
}
// update monitor listener
mSubscriptionsChangedListener = new OnSubscriptionsChangedListener(
Looper.getMainLooper()) {
@Override
public void onSubscriptionsChanged() {
int subId = mResetNetworkRequest.getResetApnSubId();
SubscriptionManager mgr = getSubscriptionManager();
if (isSubscriptionRemainActive(mgr, subId)) {
return;
}
// close UI if subscription no longer active
Log.w(TAG, "subId " + subId + " no longer active.");
stopMonitorSubscriptionChange(mgr);
mActivity.finish();
}
};
mgr.addOnSubscriptionsChangedListener(
mActivity.getMainExecutor(), mSubscriptionsChangedListener);
}
private boolean isSubscriptionRemainActive(SubscriptionManager mgr, int subscriptionId) {
return (mgr == null) ? false : (mgr.getActiveSubscriptionInfo(subscriptionId) != null);
}
private void stopMonitorSubscriptionChange(SubscriptionManager mgr) {
if ((mgr == null) || (mSubscriptionsChangedListener == null)) {
return;
}
mgr.removeOnSubscriptionsChangedListener(mSubscriptionsChangedListener);
mSubscriptionsChangedListener = null;
}
@Override @Override
public void onDestroy() { public void onDestroy() {
if (mResetNetworkTask != null) { if (mResetNetworkTask != null) {
mResetNetworkTask.cancel(true /* mayInterruptIfRunning */); mResetNetworkTask.cancel(true /* mayInterruptIfRunning */);
mResetNetworkTask = null; mResetNetworkTask = null;
} }
if (mResetSubscriptionContract != null) {
mResetSubscriptionContract.close();
mResetSubscriptionContract = null;
}
if (mProgressDialog != null) { if (mProgressDialog != null) {
mProgressDialog.dismiss(); mProgressDialog.dismiss();
} }
if (mAlertDialog != null) { if (mAlertDialog != null) {
mAlertDialog.dismiss(); mAlertDialog.dismiss();
} }
stopMonitorSubscriptionChange(getSubscriptionManager());
super.onDestroy(); super.onDestroy();
} }

View File

@@ -0,0 +1,157 @@
/*
* Copyright (C) 2022 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;
import android.content.Context;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.IntStream;
/**
* A Class monitoring the availability of subscription IDs provided within reset request.
*
* This is to detect the situation when user changing SIM card during the presenting of
* confirmation UI.
*/
public class ResetSubscriptionContract implements AutoCloseable {
private static final String TAG = "ResetSubscriptionContract";
private final Context mContext;
private ExecutorService mExecutorService;
private final int [] mResetSubscriptionIds;
@VisibleForTesting
protected OnSubscriptionsChangedListener mSubscriptionsChangedListener;
private AtomicBoolean mSubscriptionsUpdateNotify = new AtomicBoolean();
/**
* Constructor
* @param context Context
* @param resetRequest the request object for perform network reset operation.
*/
public ResetSubscriptionContract(Context context, ResetNetworkRequest resetRequest) {
mContext = context;
// Only keeps specific subscription ID required to perform reset operation
IntStream subIdStream = IntStream.of(
resetRequest.getResetTelephonyAndNetworkPolicyManager()
, resetRequest.getResetApnSubId());
mResetSubscriptionIds = subIdStream.sorted().distinct()
.filter(id -> SubscriptionManager.isUsableSubscriptionId(id))
.toArray();
if (mResetSubscriptionIds.length <= 0) {
return;
}
// Monitoring callback through background thread
mExecutorService = Executors.newSingleThreadExecutor();
startMonitorSubscriptionChange();
}
/**
* A method for detecting if there's any subscription under monitor no longer active.
* @return subscription ID which is no longer active.
*/
public Integer getAnyMissingSubscriptionId() {
if (mResetSubscriptionIds.length <= 0) {
return null;
}
SubscriptionManager mgr = getSubscriptionManager();
if (mgr == null) {
Log.w(TAG, "Fail to access subscription manager");
return mResetSubscriptionIds[0];
}
for (int idx = 0; idx < mResetSubscriptionIds.length; idx++) {
int subId = mResetSubscriptionIds[idx];
if (mgr.getActiveSubscriptionInfo(subId) == null) {
Log.w(TAG, "SubId " + subId + " no longer active.");
return subId;
}
}
return null;
}
/**
* Async callback when detecting if there's any subscription under monitor no longer active.
* @param subscriptionId subscription ID which is no longer active.
*/
public void onSubscriptionInactive(int subscriptionId) {}
@VisibleForTesting
protected SubscriptionManager getSubscriptionManager() {
return mContext.getSystemService(SubscriptionManager.class);
}
@VisibleForTesting
protected OnSubscriptionsChangedListener getChangeListener() {
return new OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
/**
* Reducing the processing time on main UI thread through a flag.
* Once flag get into false, which means latest callback has been
* processed.
*/
mSubscriptionsUpdateNotify.set(true);
// Back to main UI thread
mContext.getMainExecutor().execute(() -> {
// Remove notifications and perform checking.
if (mSubscriptionsUpdateNotify.getAndSet(false)) {
Integer subId = getAnyMissingSubscriptionId();
if (subId != null) {
onSubscriptionInactive(subId);
}
}
});
}
};
}
private void startMonitorSubscriptionChange() {
SubscriptionManager mgr = getSubscriptionManager();
if (mgr == null) {
return;
}
// update monitor listener
mSubscriptionsChangedListener = getChangeListener();
mgr.addOnSubscriptionsChangedListener(
mExecutorService, mSubscriptionsChangedListener);
}
// Implementation of AutoCloseable
public void close() {
if (mExecutorService == null) {
return;
}
// Stop monitoring subscription change
SubscriptionManager mgr = getSubscriptionManager();
if (mgr != null) {
mgr.removeOnSubscriptionsChangedListener(mSubscriptionsChangedListener);
}
// Release Executor
mExecutorService.shutdownNow();
mExecutorService = null;
}
}

View File

@@ -74,6 +74,14 @@ public class ResetNetworkConfirmTest {
public void testResetNetworkData_notResetEsim() { public void testResetNetworkData_notResetEsim() {
mResetNetworkConfirm.mResetNetworkRequest = mResetNetworkConfirm.mResetNetworkRequest =
new ResetNetworkRequest(ResetNetworkRequest.RESET_NONE); new ResetNetworkRequest(ResetNetworkRequest.RESET_NONE);
mResetNetworkConfirm.mResetSubscriptionContract =
new ResetSubscriptionContract(mActivity,
mResetNetworkConfirm.mResetNetworkRequest) {
@Override
public void onSubscriptionInactive(int subscriptionId) {
mActivity.onBackPressed();
}
};
mResetNetworkConfirm.mFinalClickListener.onClick(null /* View */); mResetNetworkConfirm.mFinalClickListener.onClick(null /* View */);
Robolectric.getBackgroundThreadScheduler().advanceToLastPostedRunnable(); Robolectric.getBackgroundThreadScheduler().advanceToLastPostedRunnable();

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2022 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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.os.Bundle;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class ResetSubscriptionContractTest {
private static final int SUB_ID_1 = 3;
private static final int SUB_ID_2 = 8;
@Mock
private SubscriptionManager mSubscriptionManager;
@Mock
private OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;
@Mock
private SubscriptionInfo mSubscriptionInfo1;
@Mock
private SubscriptionInfo mSubscriptionInfo2;
private Context mContext;
private ResetNetworkRequest mRequestArgs;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
mRequestArgs = new ResetNetworkRequest(new Bundle());
}
private ResetSubscriptionContract createTestObject() {
return new ResetSubscriptionContract(mContext, mRequestArgs) {
@Override
protected SubscriptionManager getSubscriptionManager() {
return mSubscriptionManager;
}
@Override
protected OnSubscriptionsChangedListener getChangeListener() {
return mOnSubscriptionsChangedListener;
}
};
}
@Test
public void getAnyMissingSubscriptionId_returnNull_whenNoSubscriptionChange() {
mRequestArgs.setResetTelephonyAndNetworkPolicyManager(SUB_ID_1);
doReturn(mSubscriptionInfo1).when(mSubscriptionManager)
.getActiveSubscriptionInfo(SUB_ID_1);
mRequestArgs.setResetApn(SUB_ID_2);
doReturn(mSubscriptionInfo2).when(mSubscriptionManager)
.getActiveSubscriptionInfo(SUB_ID_2);
ResetSubscriptionContract target = createTestObject();
verify(mSubscriptionManager).addOnSubscriptionsChangedListener(any(), any());
assertNull(target.getAnyMissingSubscriptionId());
}
@Test
public void getAnyMissingSubscriptionId_returnSubId_whenSubscriptionNotActive() {
mRequestArgs.setResetTelephonyAndNetworkPolicyManager(SUB_ID_1);
doReturn(mSubscriptionInfo1).when(mSubscriptionManager)
.getActiveSubscriptionInfo(SUB_ID_1);
mRequestArgs.setResetApn(SUB_ID_2);
doReturn(null).when(mSubscriptionManager)
.getActiveSubscriptionInfo(SUB_ID_2);
ResetSubscriptionContract target = createTestObject();
verify(mSubscriptionManager).addOnSubscriptionsChangedListener(any(), any());
assertEquals(target.getAnyMissingSubscriptionId(), new Integer(SUB_ID_2));
}
}