[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:
@@ -61,6 +61,7 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
|
||||
@VisibleForTesting ResetNetworkRequest mResetNetworkRequest;
|
||||
private ProgressDialog mProgressDialog;
|
||||
private AlertDialog mAlertDialog;
|
||||
@VisibleForTesting ResetSubscriptionContract mResetSubscriptionContract;
|
||||
private OnSubscriptionsChangedListener mSubscriptionsChangedListener;
|
||||
|
||||
/**
|
||||
@@ -130,16 +131,11 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
|
||||
}
|
||||
|
||||
// abandon execution if subscription no longer active
|
||||
int subId = mResetNetworkRequest.getResetApnSubId();
|
||||
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
||||
SubscriptionManager mgr = getSubscriptionManager();
|
||||
// always remove listener
|
||||
stopMonitorSubscriptionChange(mgr);
|
||||
if (!isSubscriptionRemainActive(mgr, subId)) {
|
||||
Log.w(TAG, "subId " + subId + " disappear when confirm");
|
||||
mActivity.finish();
|
||||
return;
|
||||
}
|
||||
Integer subId = mResetSubscriptionContract.getAnyMissingSubscriptionId();
|
||||
if (subId != null) {
|
||||
Log.w(TAG, "subId " + subId + " no longer active");
|
||||
getActivity().onBackPressed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Should dismiss the progress dialog firstly if it is showing
|
||||
@@ -186,7 +182,7 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
|
||||
Bundle savedInstanceState) {
|
||||
View view = (new ResetNetworkRestrictionViewBuilder(mActivity)).build();
|
||||
if (view != null) {
|
||||
stopMonitorSubscriptionChange(getSubscriptionManager());
|
||||
mResetSubscriptionContract.close();
|
||||
Log.w(TAG, "Access deny.");
|
||||
return view;
|
||||
}
|
||||
@@ -208,13 +204,15 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
|
||||
|
||||
mActivity = getActivity();
|
||||
|
||||
if (mResetNetworkRequest.getResetApnSubId()
|
||||
== ResetNetworkRequest.INVALID_SUBSCRIPTION_ID) {
|
||||
return;
|
||||
}
|
||||
// close confirmation dialog when reset specific subscription
|
||||
// but removed priori to the confirmation button been pressed
|
||||
startMonitorSubscriptionChange(getSubscriptionManager());
|
||||
mResetSubscriptionContract = new ResetSubscriptionContract(getContext(),
|
||||
mResetNetworkRequest) {
|
||||
@Override
|
||||
public void onSubscriptionInactive(int subscriptionId) {
|
||||
// close UI if subscription no longer active
|
||||
Log.w(TAG, "subId " + subscriptionId + " no longer active.");
|
||||
getActivity().onBackPressed();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -223,63 +221,22 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
|
||||
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
|
||||
public void onDestroy() {
|
||||
if (mResetNetworkTask != null) {
|
||||
mResetNetworkTask.cancel(true /* mayInterruptIfRunning */);
|
||||
mResetNetworkTask = null;
|
||||
}
|
||||
if (mResetSubscriptionContract != null) {
|
||||
mResetSubscriptionContract.close();
|
||||
mResetSubscriptionContract = null;
|
||||
}
|
||||
if (mProgressDialog != null) {
|
||||
mProgressDialog.dismiss();
|
||||
}
|
||||
if (mAlertDialog != null) {
|
||||
mAlertDialog.dismiss();
|
||||
}
|
||||
stopMonitorSubscriptionChange(getSubscriptionManager());
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
|
157
src/com/android/settings/ResetSubscriptionContract.java
Normal file
157
src/com/android/settings/ResetSubscriptionContract.java
Normal 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;
|
||||
}
|
||||
}
|
@@ -74,6 +74,14 @@ public class ResetNetworkConfirmTest {
|
||||
public void testResetNetworkData_notResetEsim() {
|
||||
mResetNetworkConfirm.mResetNetworkRequest =
|
||||
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 */);
|
||||
Robolectric.getBackgroundThreadScheduler().advanceToLastPostedRunnable();
|
||||
|
@@ -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));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user