From ff8f96c0be56b9b40fb8c850dd96b69de4bed146 Mon Sep 17 00:00:00 2001 From: Aswin Sankar Date: Fri, 3 Feb 2023 15:23:52 -0800 Subject: [PATCH] Attempt cross-SIM enablement when auto data switch enabled - For devices that have opted in with overlay config, enabling a 2nd SIM and modifying 'Switch mobile data automatically' will update cross-SIM calling enablement. - These updates will be logged as settings events. Bug: 260266794 Test: Live tests for cross-SIM calling across both subs. Change-Id: I64d25ca406877ca5f061064e0a3b23f54c125638 --- res/values/config.xml | 3 + .../network/ActiveSubscriptionsListener.java | 10 ++ .../network/ProxySubscriptionManager.java | 10 ++ .../AutoDataSwitchPreferenceController.java | 87 ++++++++++++++++- .../settings/sim/SimDialogActivity.java | 96 +++++++++++++++++++ 5 files changed, 205 insertions(+), 1 deletion(-) diff --git a/res/values/config.xml b/res/values/config.xml index df2bae38396..628699af963 100755 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -689,4 +689,7 @@ 58.0001 29.2229,56.9551 26.8945,55.195 + + false + diff --git a/src/com/android/settings/network/ActiveSubscriptionsListener.java b/src/com/android/settings/network/ActiveSubscriptionsListener.java index b23b7230e84..dce441be3dc 100644 --- a/src/com/android/settings/network/ActiveSubscriptionsListener.java +++ b/src/com/android/settings/network/ActiveSubscriptionsListener.java @@ -16,6 +16,7 @@ package com.android.settings.network; +import android.annotation.NonNull; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -277,6 +278,15 @@ public abstract class ActiveSubscriptionsListener return null; } + /** + * Gets a list of active, visible subscription Id(s) of the currently active SIM(s). + * + * @return the list of subId's that are active and visible; the length may be 0. + */ + public @NonNull int[] getActiveSubscriptionIdList() { + return getSubscriptionManager().getActiveSubscriptionIdList(); + } + /** * Clear data cached within listener */ diff --git a/src/com/android/settings/network/ProxySubscriptionManager.java b/src/com/android/settings/network/ProxySubscriptionManager.java index 614491a5016..51fafb17ee8 100644 --- a/src/com/android/settings/network/ProxySubscriptionManager.java +++ b/src/com/android/settings/network/ProxySubscriptionManager.java @@ -20,6 +20,7 @@ import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY; import static androidx.lifecycle.Lifecycle.Event.ON_START; import static androidx.lifecycle.Lifecycle.Event.ON_STOP; +import android.annotation.NonNull; import android.content.Context; import android.os.Looper; import android.provider.Settings; @@ -241,6 +242,15 @@ public class ProxySubscriptionManager implements LifecycleObserver { return mSubscriptionMonitor.getAccessibleSubscriptionInfo(subId); } + /** + * Gets a list of active, visible subscription Id(s) of the currently active SIM(s). + * + * @return the list of subId's that are active and visible; the length may be 0. + */ + public @NonNull int[] getActiveSubscriptionIdList() { + return mSubscriptionMonitor.getActiveSubscriptionIdList(); + } + /** * Clear data cached within proxy */ diff --git a/src/com/android/settings/network/telephony/AutoDataSwitchPreferenceController.java b/src/com/android/settings/network/telephony/AutoDataSwitchPreferenceController.java index f4543f5004d..1c20f5910bf 100644 --- a/src/com/android/settings/network/telephony/AutoDataSwitchPreferenceController.java +++ b/src/com/android/settings/network/telephony/AutoDataSwitchPreferenceController.java @@ -19,12 +19,20 @@ package com.android.settings.network.telephony; import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; +import android.app.settings.SettingsEnums; import android.content.Context; import android.os.Handler; import android.os.Looper; +import android.os.PersistableBundle; +import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.telephony.ims.ImsException; +import android.telephony.ims.ImsManager; +import android.telephony.ims.ImsMmTelManager; +import android.util.Log; +import androidx.annotation.NonNull; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; import androidx.preference.Preference; @@ -32,9 +40,14 @@ import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreference; import com.android.internal.annotations.VisibleForTesting; +import com.android.settings.R; import com.android.settings.datausage.DataUsageUtils; import com.android.settings.network.MobileDataContentObserver; +import com.android.settings.network.ProxySubscriptionManager; import com.android.settings.network.SubscriptionsChangeListener; +import com.android.settings.network.ims.WifiCallingQueryImsState; +import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; /** * Controls whether switch mobile data to the non-default SIM if the non-default SIM has better @@ -49,6 +62,7 @@ import com.android.settings.network.SubscriptionsChangeListener; public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenceController implements LifecycleObserver, SubscriptionsChangeListener.SubscriptionsChangeListenerClient { + private static final String LOG_TAG = "AutoDataSwitchPrefCtrl"; private SwitchPreference mPreference; private SubscriptionsChangeListener mChangeListener; @@ -56,9 +70,12 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc private MobileDataContentObserver mMobileDataContentObserver; private PreferenceScreen mScreen; + private final MetricsFeatureProvider mMetricsFeatureProvider; + public AutoDataSwitchPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); + mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); } void init(int subId) { @@ -103,11 +120,36 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH); } + private int getOtherSubId(@NonNull int[] subIds) { + if (subIds.length > 1) { + for (int subId : subIds) { + if (subId != mSubId) { + return subId; + } + } + } + return SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } + + private boolean isEnabled(int subId) { + if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + return false; + } + TelephonyManager telephonyManager = mContext.getSystemService( + TelephonyManager.class).createForSubscriptionId(subId); + return telephonyManager != null && telephonyManager.isMobileDataPolicyEnabled( + TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH); + } + @Override public boolean setChecked(boolean isChecked) { mManager.setMobileDataPolicyEnabled( TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, isChecked); + if (mContext.getResources().getBoolean( + R.bool.config_auto_data_switch_enables_cross_sim_calling)) { + trySetCrossSimCalling(mContext, getActiveSubscriptionIdList(), isChecked /* enabled */); + } return true; } @@ -116,6 +158,40 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc return DataUsageUtils.hasMobileData(mContext); } + private boolean isCrossSimCallingAllowedByPlatform(Context context, int subId) { + if ((new WifiCallingQueryImsState(context, subId)).isWifiCallingSupported()) { + PersistableBundle bundle = getCarrierConfigForSubId(subId); + return (bundle != null) && bundle.getBoolean( + CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL, + false /*default*/); + } + return false; + } + + protected ImsMmTelManager getImsMmTelManager(Context context, int subId) { + ImsManager imsMgr = context.getSystemService(ImsManager.class); + return (imsMgr == null) ? null : imsMgr.getImsMmTelManager(subId); + } + + private void trySetCrossSimCallingPerSub(Context context, int subId, boolean enabled) { + try { + getImsMmTelManager(context, subId).setCrossSimCallingEnabled(enabled); + } catch (ImsException | IllegalArgumentException | NullPointerException exception) { + Log.w(LOG_TAG, "failed to change cross SIM calling configuration to " + enabled + + " for subID " + subId + "with exception: ", exception); + } + } + + private void trySetCrossSimCalling(Context context, int[] subIds, boolean enabled) { + mMetricsFeatureProvider.action(mContext, + SettingsEnums.ACTION_UPDATE_CROSS_SIM_CALLING_ON_AUTO_DATA_SWITCH_EVENT, enabled); + for (int subId : subIds) { + if (isCrossSimCallingAllowedByPlatform(context, subId)) { + trySetCrossSimCallingPerSub(context, subId, enabled); + } + } + } + @Override public int getAvailabilityStatus(int subId) { if (!SubscriptionManager.isValidSubscriptionId(subId) @@ -143,11 +219,20 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc updateState(mPreference); } + private int[] getActiveSubscriptionIdList() { + return ProxySubscriptionManager.getInstance(mContext).getActiveSubscriptionIdList(); + } + /** - * Trigger displaying preference when Mobilde data content changed. + * Trigger displaying preference when Mobile data content changed. */ @VisibleForTesting public void refreshPreference() { + if (mContext.getResources().getBoolean( + R.bool.config_auto_data_switch_enables_cross_sim_calling)) { + int[] subIds = getActiveSubscriptionIdList(); + trySetCrossSimCalling(mContext, subIds, isEnabled(getOtherSubId(subIds))); + } if (mScreen != null) { super.displayPreference(mScreen); } diff --git a/src/com/android/settings/sim/SimDialogActivity.java b/src/com/android/settings/sim/SimDialogActivity.java index a1258be64b0..dcfa9832458 100644 --- a/src/com/android/settings/sim/SimDialogActivity.java +++ b/src/com/android/settings/sim/SimDialogActivity.java @@ -17,24 +17,35 @@ package com.android.settings.sim; import android.app.Activity; +import android.app.settings.SettingsEnums; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; +import android.os.PersistableBundle; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; +import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.telephony.ims.ImsException; +import android.telephony.ims.ImsManager; +import android.telephony.ims.ImsMmTelManager; import android.util.Log; import android.view.WindowManager; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import com.android.settings.R; +import com.android.settings.network.CarrierConfigCache; import com.android.settings.network.SubscriptionUtil; +import com.android.settings.network.ims.WifiCallingQueryImsState; import com.android.settings.network.telephony.SubscriptionActionDialogActivity; +import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import java.util.List; @@ -62,6 +73,7 @@ public class SimDialogActivity extends FragmentActivity { // Show auto data switch dialog(when user enables multi-SIM) public static final int ENABLE_AUTO_DATA_SWITCH = 6; + private MetricsFeatureProvider mMetricsFeatureProvider; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -73,6 +85,7 @@ public class SimDialogActivity extends FragmentActivity { } SimDialogProhibitService.supportDismiss(this); + mMetricsFeatureProvider = FeatureFactory.getFactory(this).getMetricsFeatureProvider(); getWindow().addSystemFlags( WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); showOrUpdateDialog(); @@ -192,6 +205,61 @@ public class SimDialogActivity extends FragmentActivity { } } + private PersistableBundle getCarrierConfigForSubId(int subId) { + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + return null; + } + return CarrierConfigCache.getInstance(this).getConfigForSubId(subId); + } + + private boolean isCrossSimCallingAllowedByPlatform(int subId) { + if ((new WifiCallingQueryImsState(this, subId)).isWifiCallingSupported()) { + PersistableBundle bundle = getCarrierConfigForSubId(subId); + return (bundle != null) && bundle.getBoolean( + CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL, + false /*default*/); + } + return false; + } + + private ImsMmTelManager getImsMmTelManager(int subId) { + ImsManager imsMgr = getSystemService(ImsManager.class); + return (imsMgr == null) ? null : imsMgr.getImsMmTelManager(subId); + } + + private void trySetCrossSimCallingPerSub(int subId, boolean enabled) { + try { + getImsMmTelManager(subId).setCrossSimCallingEnabled(enabled); + } catch (ImsException | IllegalArgumentException | NullPointerException exception) { + Log.w(TAG, "failed to change cross SIM calling configuration to " + enabled + + " for subID " + subId + "with exception: ", exception); + } + } + + private boolean autoDataSwitchEnabledOnNonDataSub(@NonNull int[] subIds, int defaultDataSub) { + for (int subId : subIds) { + if (subId != defaultDataSub) { + final TelephonyManager telephonyManager = getSystemService( + TelephonyManager.class).createForSubscriptionId(subId); + if (telephonyManager.isMobileDataPolicyEnabled( + TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)) { + return true; + } + } + } + return false; + } + + private void trySetCrossSimCalling(int[] subIds, boolean enabled) { + mMetricsFeatureProvider.action(this, + SettingsEnums.ACTION_UPDATE_CROSS_SIM_CALLING_ON_2ND_SIM_ENABLE, enabled); + for (int subId : subIds) { + if (isCrossSimCallingAllowedByPlatform(subId)) { + trySetCrossSimCallingPerSub(subId, enabled); + } + } + } + /** * Show dialog prompting the user to enable auto data switch */ @@ -199,6 +267,26 @@ public class SimDialogActivity extends FragmentActivity { final FragmentManager fragmentManager = getSupportFragmentManager(); SimDialogFragment fragment = createFragment(ENABLE_AUTO_DATA_SWITCH); fragment.show(fragmentManager, Integer.toString(ENABLE_AUTO_DATA_SWITCH)); + + if (getResources().getBoolean( + R.bool.config_auto_data_switch_enables_cross_sim_calling)) { + // If auto data switch is already enabled on the non-DDS, the dialog for enabling it + // is suppressed (no onEnableAutoDataSwitch()). so we ensure cross-SIM calling is + // enabled. + + // OTOH, if auto data switch is disabled on the new non-DDS, the user may still not + // enable it in the dialog. So we ensure cross-SIM calling is disabled before the + // dialog. If the user does enable auto data switch, we will re-enable cross-SIM calling + // through onEnableAutoDataSwitch()- a minor redundancy to ensure correctness. + final SubscriptionManager subscriptionManager = + getSystemService(SubscriptionManager.class); + int[] subIds = subscriptionManager.getActiveSubscriptionIdList(); + int defaultDataSub = subscriptionManager.getDefaultDataSubscriptionId(); + if (subIds.length > 1) { + trySetCrossSimCalling(subIds, + autoDataSwitchEnabledOnNonDataSub(subIds, defaultDataSub)); + } + } } /** @@ -210,6 +298,14 @@ public class SimDialogActivity extends FragmentActivity { TelephonyManager.class).createForSubscriptionId(subId); telephonyManager.setMobileDataPolicyEnabled( TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, true); + + if (getResources().getBoolean( + R.bool.config_auto_data_switch_enables_cross_sim_calling)) { + final SubscriptionManager subscriptionManager = + getSystemService(SubscriptionManager.class); + trySetCrossSimCalling(subscriptionManager.getActiveSubscriptionIdList(), + true /* enabled */); + } } public void onFragmentDismissed(SimDialogFragment simDialogFragment) {