From 04bdf8738b37e47b1d56993b2dcfb97c51f80191 Mon Sep 17 00:00:00 2001 From: markchien Date: Mon, 17 Jun 2019 21:05:34 +0800 Subject: Fix entitlement failed when device is on CBRS Tethering may use wrong sub id for entitlement when data subscription is changed from preferred data sub id to CBRS sub id. Fix by using the active data sub id for tethering. Bug: 134994718 Test: -build, flash, boot -FrameworkNetTests -manual test with carrier SIMs Change-Id: Icb3f5eeb2319e50b2dc98369ad152988c934da57 Merged-In: Icb3f5eeb2319e50b2dc98369ad152988c934da57 --- .../com/android/server/connectivity/Tethering.java | 63 +++++++++++----------- .../tethering/TetheringConfiguration.java | 6 +-- .../tethering/TetheringDependencies.java | 8 +-- .../android/server/connectivity/TetheringTest.java | 39 +++++++++++++- .../tethering/TetheringConfigurationTest.java | 11 ++-- 5 files changed, 80 insertions(+), 47 deletions(-) diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 33c84d161a90..4957eed21c70 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -44,6 +44,7 @@ import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.IFACE_IP_MODE_UNSPECIFIED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static com.android.server.ConnectivityService.SHORT_ARG; @@ -89,6 +90,8 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; +import android.telephony.PhoneStateListener; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; @@ -97,7 +100,6 @@ import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; -import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.MessageUtils; @@ -182,12 +184,13 @@ public class Tethering extends BaseNetworkObserver { // into a single coherent structure. private final HashSet mForwardedDownstreams; private final VersionedBroadcastListener mCarrierConfigChange; - private final VersionedBroadcastListener mDefaultSubscriptionChange; private final TetheringDependencies mDeps; private final EntitlementManager mEntitlementMgr; private final Handler mHandler; private final RemoteCallbackList mTetheringEventCallbacks = new RemoteCallbackList<>(); + private final PhoneStateListener mPhoneStateListener; + private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID; private volatile TetheringConfiguration mConfig; private InterfaceSet mCurrentUpstreamIfaceSet; @@ -238,7 +241,6 @@ public class Tethering extends BaseNetworkObserver { stopTethering(downstream); }); mEntitlementMgr.setTetheringConfigurationFetcher(() -> { - maybeDefaultDataSubChanged(); return mConfig; }); @@ -250,22 +252,26 @@ public class Tethering extends BaseNetworkObserver { mEntitlementMgr.reevaluateSimCardProvisioning(mConfig); }); - filter = new IntentFilter(); - filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); - mDefaultSubscriptionChange = new VersionedBroadcastListener( - "DefaultSubscriptionChangeListener", mContext, mHandler, filter, - (Intent ignored) -> { - mLog.log("OBSERVED default data subscription change"); - maybeDefaultDataSubChanged(); - // To avoid launch unexpected provisioning checks, ignore re-provisioning when - // no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning() will be - // triggered again when CarrierConfig is loaded. - if (mEntitlementMgr.getCarrierConfig(mConfig) != null) { - mEntitlementMgr.reevaluateSimCardProvisioning(mConfig); - } else { - mLog.log("IGNORED reevaluate provisioning due to no carrier config loaded"); - } - }); + mPhoneStateListener = new PhoneStateListener(mLooper) { + @Override + public void onActiveDataSubscriptionIdChanged(int subId) { + mLog.log("OBSERVED active data subscription change, from " + mActiveDataSubId + + " to " + subId); + if (subId == mActiveDataSubId) return; + + mActiveDataSubId = subId; + updateConfiguration(); + // To avoid launching unexpected provisioning checks, ignore re-provisioning when + // no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning() will be + // triggered again when CarrierConfig is loaded. + if (mEntitlementMgr.getCarrierConfig(mConfig) != null) { + mEntitlementMgr.reevaluateSimCardProvisioning(mConfig); + } else { + mLog.log("IGNORED reevaluate provisioning due to no carrier config loaded"); + } + } + }; + mStateReceiver = new StateReceiver(); // Load tethering configuration. @@ -276,7 +282,8 @@ public class Tethering extends BaseNetworkObserver { private void startStateMachineUpdaters(Handler handler) { mCarrierConfigChange.startListening(); - mDefaultSubscriptionChange.startListening(); + TelephonyManager.from(mContext).listen(mPhoneStateListener, + PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); IntentFilter filter = new IntentFilter(); filter.addAction(UsbManager.ACTION_USB_STATE); @@ -304,27 +311,17 @@ public class Tethering extends BaseNetworkObserver { // NOTE: This is always invoked on the mLooper thread. private void updateConfiguration() { - final int subId = mDeps.getDefaultDataSubscriptionId(); - updateConfiguration(subId); - } - - private void updateConfiguration(final int subId) { - mConfig = new TetheringConfiguration(mContext, mLog, subId); + mConfig = mDeps.generateTetheringConfiguration(mContext, mLog, mActiveDataSubId); mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired); } private void maybeDunSettingChanged() { - final boolean isDunRequired = TetheringConfiguration.checkDunRequired(mContext); + final boolean isDunRequired = TetheringConfiguration.checkDunRequired( + mContext, mActiveDataSubId); if (isDunRequired == mConfig.isDunRequired) return; updateConfiguration(); } - private void maybeDefaultDataSubChanged() { - final int subId = mDeps.getDefaultDataSubscriptionId(); - if (subId == mConfig.subId) return; - updateConfiguration(subId); - } - @Override public void interfaceStatusChanged(String iface, boolean up) { // Never called directly: only called from interfaceLinkStateChanged. diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java index 8427b6eceab9..1907892c4d87 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -112,7 +112,7 @@ public class TetheringConfiguration { tetherableWifiRegexs = getResourceStringArray(res, config_tether_wifi_regexs); tetherableBluetoothRegexs = getResourceStringArray(res, config_tether_bluetooth_regexs); - isDunRequired = checkDunRequired(ctx); + isDunRequired = checkDunRequired(ctx, subId); chooseUpstreamAutomatically = getResourceBoolean(res, config_tether_upstream_automatic); preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired); @@ -228,9 +228,9 @@ public class TetheringConfiguration { } /** Check whether dun is required. */ - public static boolean checkDunRequired(Context ctx) { + public static boolean checkDunRequired(Context ctx, int id) { final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE); - return (tm != null) ? tm.getTetherApnRequired() : false; + return (tm != null) ? tm.getTetherApnRequired(id) : false; } private static Collection getUpstreamIfaceTypes(Resources res, boolean dunRequired) { diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java index a0aad7c50481..4ad7ac4bead0 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java @@ -21,7 +21,6 @@ import android.net.NetworkRequest; import android.net.ip.IpServer; import android.net.util.SharedLog; import android.os.Handler; -import android.telephony.SubscriptionManager; import com.android.internal.util.StateMachine; import com.android.server.connectivity.MockableSystemProperties; @@ -88,9 +87,10 @@ public class TetheringDependencies { } /** - * Get default data subscription id to build TetheringConfiguration. + * Generate a new TetheringConfiguration according to input sub Id. */ - public int getDefaultDataSubscriptionId() { - return SubscriptionManager.getDefaultDataSubscriptionId(); + public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log, + int subId) { + return new TetheringConfiguration(ctx, log, subId); } } diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java index 6c42ac398b47..8c522f48cfd2 100644 --- a/tests/net/java/com/android/server/connectivity/TetheringTest.java +++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java @@ -100,6 +100,8 @@ import android.os.UserManager; import android.os.test.TestLooper; import android.provider.Settings; import android.telephony.CarrierConfigManager; +import android.telephony.PhoneStateListener; +import android.telephony.TelephonyManager; import android.test.mock.MockContentResolver; import androidx.test.filters.SmallTest; @@ -111,6 +113,7 @@ import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.connectivity.tethering.IPv6TetheringCoordinator; import com.android.server.connectivity.tethering.OffloadHardwareInterface; +import com.android.server.connectivity.tethering.TetheringConfiguration; import com.android.server.connectivity.tethering.TetheringDependencies; import com.android.server.connectivity.tethering.UpstreamNetworkMonitor; @@ -118,6 +121,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -147,6 +151,7 @@ public class TetheringTest { @Mock private MockableSystemProperties mSystemProperties; @Mock private OffloadHardwareInterface mOffloadHardwareInterface; @Mock private Resources mResources; + @Mock private TelephonyManager mTelephonyManager; @Mock private UsbManager mUsbManager; @Mock private WifiManager mWifiManager; @Mock private CarrierConfigManager mCarrierConfigManager; @@ -171,6 +176,7 @@ public class TetheringTest { private MockContentResolver mContentResolver; private BroadcastReceiver mBroadcastReceiver; private Tethering mTethering; + private PhoneStateListener mPhoneStateListener; private class MockContext extends BroadcastInterceptingContext { MockContext(Context base) { @@ -193,6 +199,7 @@ public class TetheringTest { public Object getSystemService(String name) { if (Context.WIFI_SERVICE.equals(name)) return mWifiManager; if (Context.USB_SERVICE.equals(name)) return mUsbManager; + if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager; return super.getSystemService(name); } } @@ -234,6 +241,17 @@ public class TetheringTest { } } + private class MockTetheringConfiguration extends TetheringConfiguration { + MockTetheringConfiguration(Context ctx, SharedLog log, int id) { + super(ctx, log, id); + } + + @Override + protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) { + return mResources; + } + } + public class MockTetheringDependencies extends TetheringDependencies { StateMachine upstreamNetworkMonitorMasterSM; ArrayList ipv6CoordinatorNotifyList; @@ -276,8 +294,9 @@ public class TetheringTest { } @Override - public int getDefaultDataSubscriptionId() { - return INVALID_SUBSCRIPTION_ID; + public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log, + int subId) { + return new MockTetheringConfiguration(ctx, log, subId); } } @@ -372,6 +391,11 @@ public class TetheringTest { mTetheringDependencies.reset(); mTethering = makeTethering(); verify(mNMService).registerTetheringStatsProvider(any(), anyString()); + final ArgumentCaptor phoneListenerCaptor = + ArgumentCaptor.forClass(PhoneStateListener.class); + verify(mTelephonyManager).listen(phoneListenerCaptor.capture(), + eq(PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE)); + mPhoneStateListener = phoneListenerCaptor.getValue(); } private Tethering makeTethering() { @@ -982,6 +1006,17 @@ public class TetheringTest { callback2.expectUpstreamChanged(upstreamState.network); } + @Test + public void testMultiSimAware() throws Exception { + final TetheringConfiguration initailConfig = mTethering.getTetheringConfiguration(); + assertEquals(INVALID_SUBSCRIPTION_ID, initailConfig.subId); + + final int fakeSubId = 1234; + mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId); + final TetheringConfiguration newConfig = mTethering.getTetheringConfiguration(); + assertEquals(fakeSubId, newConfig.subId); + } + // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. } diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java index 21403225c600..e28296354d2a 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java @@ -29,6 +29,7 @@ import static com.android.internal.R.array.config_tether_upstream_types; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.when; import android.content.ContentResolver; @@ -141,7 +142,7 @@ public class TetheringConfigurationTest { @Test public void testDunFromTelephonyManagerMeansDun() { - when(mTelephonyManager.getTetherApnRequired()).thenReturn(true); + when(mTelephonyManager.getTetherApnRequired(anyInt())).thenReturn(true); final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI); final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration( @@ -165,7 +166,7 @@ public class TetheringConfigurationTest { @Test public void testDunNotRequiredFromTelephonyManagerMeansNoDun() { - when(mTelephonyManager.getTetherApnRequired()).thenReturn(false); + when(mTelephonyManager.getTetherApnRequired(anyInt())).thenReturn(false); final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI); final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration( @@ -208,7 +209,7 @@ public class TetheringConfigurationTest { @Test public void testNoDefinedUpstreamTypesAddsEthernet() { when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[]{}); - when(mTelephonyManager.getTetherApnRequired()).thenReturn(false); + when(mTelephonyManager.getTetherApnRequired(anyInt())).thenReturn(false); final TetheringConfiguration cfg = new TetheringConfiguration( mMockContext, mLog, INVALID_SUBSCRIPTION_ID); @@ -231,7 +232,7 @@ public class TetheringConfigurationTest { public void testDefinedUpstreamTypesSansEthernetAddsEthernet() { when(mResources.getIntArray(config_tether_upstream_types)).thenReturn( new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI}); - when(mTelephonyManager.getTetherApnRequired()).thenReturn(false); + when(mTelephonyManager.getTetherApnRequired(anyInt())).thenReturn(false); final TetheringConfiguration cfg = new TetheringConfiguration( mMockContext, mLog, INVALID_SUBSCRIPTION_ID); @@ -249,7 +250,7 @@ public class TetheringConfigurationTest { public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() { when(mResources.getIntArray(config_tether_upstream_types)) .thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI}); - when(mTelephonyManager.getTetherApnRequired()).thenReturn(false); + when(mTelephonyManager.getTetherApnRequired(anyInt())).thenReturn(false); final TetheringConfiguration cfg = new TetheringConfiguration( mMockContext, mLog, INVALID_SUBSCRIPTION_ID); -- cgit v1.2.3-59-g8ed1b