[Settings] fix for Wifi calling settings page crash

UI crash reported due to accessing telephony framework API with invalid
subscription ID from SIM. Which comes with multiple reason.

Besides of avoidance of crash, following updates are also required:
1. Close wifi calling settings UI when pSIM or eSIM not available or
inserted.
2. Keep the selection of tab (under multi-SIM) when screen rotate
3. Only show 1 SIM when multi-SIM are grouped together

Bug: 149190278
Test: local
Change-Id: Ied9a088b6ee5da66972f27649cc07f4a31304f17
(cherry picked from commit fbe1293a54)
(cherry picked from commit 05f29efe34)
This commit is contained in:
Bonian Chen
2021-06-17 11:18:14 +08:00
parent c09f986580
commit e530d93382
2 changed files with 155 additions and 26 deletions

View File

@@ -36,6 +36,7 @@ import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Lifecycle;
import com.android.internal.util.CollectionUtils;
import com.android.settings.R;
@@ -68,6 +69,7 @@ public class MobileNetworkActivity extends SettingsBaseActivity
// Set initial value to true allows subscription information fragment to be re-created when
// Activity re-create occur.
private boolean mFragmentForceReload = true;
private boolean mPendingSubscriptionChange = false;
@Override
protected void onNewIntent(Intent intent) {
@@ -155,6 +157,10 @@ public class MobileNetworkActivity extends SettingsBaseActivity
* Implementation of ProxySubscriptionManager.OnActiveSubscriptionChangedListener
*/
public void onChanged() {
if (!getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
mPendingSubscriptionChange = true;
return;
}
SubscriptionInfo info = getSubscription();
int oldSubIndex = mCurSubscriptionId;
updateSubscriptions(info, null);
@@ -180,6 +186,10 @@ public class MobileNetworkActivity extends SettingsBaseActivity
super.onStart();
// updateSubscriptions doesn't need to be called, onChanged will always be called after we
// register a listener.
if (mPendingSubscriptionChange) {
mPendingSubscriptionChange = false;
onChanged();
}
}
@Override

View File

@@ -17,6 +17,7 @@
package com.android.settings.wifi.calling;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
@@ -29,12 +30,14 @@ import android.view.ViewGroup;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import com.android.internal.util.CollectionUtils;
import com.android.settings.R;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.network.ActiveSubscriptionsListener;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.network.ims.WifiCallingQueryImsState;
import com.android.settings.search.actionbar.SearchMenuController;
@@ -42,6 +45,9 @@ import com.android.settings.support.actionbar.HelpResourceProvider;
import com.android.settings.widget.RtlCompatibleViewPager;
import com.android.settings.widget.SlidingTabLayout;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
@@ -50,7 +56,10 @@ import java.util.List;
*/
public class WifiCallingSettings extends InstrumentedFragment implements HelpResourceProvider {
private static final String TAG = "WifiCallingSettings";
private int mConstructionSubId;
private List<SubscriptionInfo> mSil;
private ActiveSubscriptionsListener mSubscriptionChangeListener;
private static final int [] EMPTY_SUB_ID_LIST = new int[0];
//UI objects
private RtlCompatibleViewPager mViewPager;
@@ -95,16 +104,26 @@ public class WifiCallingSettings extends InstrumentedFragment implements HelpRes
return view;
}
private int getConstructionSubId(Bundle bundle) {
int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
Intent intent = getActivity().getIntent();
if (intent != null) {
subId = intent.getIntExtra(Settings.EXTRA_SUB_ID,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
}
if ((subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) && (bundle != null)) {
subId = bundle.getInt(Settings.EXTRA_SUB_ID,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
}
return subId;
}
private void maybeSetViewForSubId() {
if (mSil == null) {
return;
}
final Intent intent = getActivity().getIntent();
if (intent == null) {
return;
}
final int subId = intent.getIntExtra(Settings.EXTRA_SUB_ID,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
int subId = mConstructionSubId;
if (SubscriptionManager.isValidSubscriptionId(subId)) {
for (SubscriptionInfo subInfo : mSil) {
if (subId == subInfo.getSubscriptionId()) {
@@ -117,11 +136,15 @@ public class WifiCallingSettings extends InstrumentedFragment implements HelpRes
@Override
public void onCreate(Bundle icicle) {
mConstructionSubId = getConstructionSubId(icicle);
super.onCreate(icicle);
Log.d(TAG, "SubId=" + mConstructionSubId);
// TODO: besides in onCreate, we should also update subList when SIM / Sub status
// changes.
updateSubList();
if (mConstructionSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
// Only config Wfc if it's enabled by platform.
mSubscriptionChangeListener = getSubscriptionChangeListener(getContext());
}
mSil = updateSubList();
}
@Override
@@ -135,6 +158,26 @@ public class WifiCallingSettings extends InstrumentedFragment implements HelpRes
}
updateTitleForCurrentSub();
if (mSubscriptionChangeListener != null) {
mSubscriptionChangeListener.start();
}
}
@Override
public void onStop() {
if (mSubscriptionChangeListener != null) {
mSubscriptionChangeListener.stop();
}
super.onStop();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// keep subscription ID for recreation
outState.putInt(Settings.EXTRA_SUB_ID, mConstructionSubId);
}
@Override
@@ -160,11 +203,11 @@ public class WifiCallingSettings extends InstrumentedFragment implements HelpRes
@Override
public Fragment getItem(int position) {
Log.d(TAG, "Adapter getItem " + position);
int subId = mSil.get(position).getSubscriptionId();
Log.d(TAG, "Adapter getItem " + position + " for subId=" + subId);
final Bundle args = new Bundle();
args.putBoolean(SearchMenuController.NEED_SEARCH_ICON_IN_ACTION_BAR, false);
args.putInt(WifiCallingSettingsForSub.FRAGMENT_BUNDLE_SUBID,
mSil.get(position).getSubscriptionId());
args.putInt(WifiCallingSettingsForSub.FRAGMENT_BUNDLE_SUBID, subId);
final WifiCallingSettingsForSub fragment = new WifiCallingSettingsForSub();
fragment.setArguments(args);
@@ -190,22 +233,27 @@ public class WifiCallingSettings extends InstrumentedFragment implements HelpRes
}
}
private void updateSubList() {
mSil = SubscriptionUtil.getActiveSubscriptions(
getContext().getSystemService(SubscriptionManager.class));
@VisibleForTesting
protected List<SubscriptionInfo> getSelectableSubscriptions(Context context) {
return SubscriptionUtil.getSelectableSubscriptionInfoList(context);
}
// Only config Wfc if it's enabled by platform.
if (mSil == null) {
return;
private List<SubscriptionInfo> updateSubList() {
List<SubscriptionInfo> subInfoList = getSelectableSubscriptions(getContext());
if (subInfoList == null) {
return Collections.emptyList();
}
for (int i = 0; i < mSil.size(); ) {
final SubscriptionInfo info = mSil.get(i);
if (!queryImsState(info.getSubscriptionId()).isWifiCallingProvisioned()) {
mSil.remove(i);
} else {
i++;
List<SubscriptionInfo> selectedList = new ArrayList<SubscriptionInfo>();
for (SubscriptionInfo subInfo : subInfoList) {
int subId = subInfo.getSubscriptionId();
try {
if (queryImsState(subId).isWifiCallingProvisioned()) {
selectedList.add(subInfo);
}
} catch (Exception exception) {}
}
return selectedList;
}
private void updateTitleForCurrentSub() {
@@ -218,7 +266,78 @@ public class WifiCallingSettings extends InstrumentedFragment implements HelpRes
}
@VisibleForTesting
WifiCallingQueryImsState queryImsState(int subId) {
protected WifiCallingQueryImsState queryImsState(int subId) {
return new WifiCallingQueryImsState(getContext(), subId);
}
@VisibleForTesting
protected ActiveSubscriptionsListener getSubscriptionChangeListener(Context context) {
return new ActiveSubscriptionsListener(context.getMainLooper(), context) {
public void onChanged() {
onSubscriptionChange(context);
}
};
}
protected void onSubscriptionChange(Context context) {
if (mSubscriptionChangeListener == null) {
return;
}
int [] previousSubIdList = subscriptionIdList(mSil);
List<SubscriptionInfo> updateList = updateSubList();
int [] currentSubIdList = subscriptionIdList(updateList);
if (currentSubIdList.length > 0) {
// only keep fragment when any provisioned subscription is available
if (previousSubIdList.length == 0) {
// initial loading of list
mSil = updateList;
return;
}
if (previousSubIdList.length == currentSubIdList.length) {
// same number of subscriptions
if ( (!containsSubId(previousSubIdList, mConstructionSubId))
// original request not yet appears in list
|| containsSubId(currentSubIdList, mConstructionSubId) )
// original request appears in list
{
mSil = updateList;
return;
}
}
}
Log.d(TAG, "Closed subId=" + mConstructionSubId
+ " due to subscription change: " + Arrays.toString(previousSubIdList)
+ " -> " + Arrays.toString(currentSubIdList));
// close this fragment when no provisioned subscriptions available
if (mSubscriptionChangeListener != null) {
mSubscriptionChangeListener.stop();
mSubscriptionChangeListener = null;
}
// close this fragment
finish();
}
protected void finish() {
FragmentActivity activity = getActivity();
if (activity == null) return;
if (getFragmentManager().getBackStackEntryCount() > 0) {
getFragmentManager().popBackStack();
} else {
activity.finish();
}
}
protected int [] subscriptionIdList(List<SubscriptionInfo> subInfoList) {
return (subInfoList == null) ? EMPTY_SUB_ID_LIST :
subInfoList.stream().mapToInt(subInfo -> (subInfo == null) ?
SubscriptionManager.INVALID_SUBSCRIPTION_ID : subInfo.getSubscriptionId())
.toArray();
}
protected boolean containsSubId(int [] subIdArray, int subIdLookUp) {
return Arrays.stream(subIdArray).anyMatch(subId -> (subId == subIdLookUp));
}
}