diff options
| author | 2017-10-25 14:07:34 +0000 | |
|---|---|---|
| committer | 2017-10-25 14:07:34 +0000 | |
| commit | bee33f843ea978970f563e2cbef8e3482aa20a7b (patch) | |
| tree | 933b5cc1ac5321f32b4e768ea98c06252e5c7842 | |
| parent | b83255ac5242b2f214a03c7bb585821d47f26ba2 (diff) | |
| parent | fc0b863b4423ca27fc01e5037af123c9e2792a20 (diff) | |
Merge "Switch to listening for CarrierConfig changes for provisioning rechecks" am: c21effd526 am: 166c27440d
am: fc0b863b44
Change-Id: I2e46e8271209798692d5074d336ef73eb96370bb
5 files changed, 318 insertions, 85 deletions
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index d7cd81ff3c5f..59870cb97fc9 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -28,6 +28,7 @@ import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY; 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 com.android.server.ConnectivityService.SHORT_ARG; import android.app.Notification; @@ -60,6 +61,7 @@ import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.util.PrefixUtils; import android.net.util.SharedLog; +import android.net.util.VersionedBroadcastListener; import android.net.wifi.WifiManager; import android.os.Binder; import android.os.Bundle; @@ -68,6 +70,7 @@ import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; import android.os.Parcel; +import android.os.PersistableBundle; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; @@ -184,6 +187,8 @@ public class Tethering extends BaseNetworkObserver { // TODO: Figure out how to merge this and other downstream-tracking objects // into a single coherent structure. private final HashSet<TetherInterfaceStateMachine> mForwardedDownstreams; + private final VersionedBroadcastListener mCarrierConfigChange; + // TODO: Delete SimChangeListener; it's obsolete. private final SimChangeListener mSimChange; private volatile TetheringConfiguration mConfig; @@ -224,11 +229,26 @@ public class Tethering extends BaseNetworkObserver { mUpstreamNetworkMonitor = new UpstreamNetworkMonitor( mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK); mForwardedDownstreams = new HashSet<>(); + + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_CARRIER_CONFIG_CHANGED); + mCarrierConfigChange = new VersionedBroadcastListener( + "CarrierConfigChangeListener", mContext, smHandler, filter, + (Intent ignored) -> { + mLog.log("OBSERVED carrier config change"); + reevaluateSimCardProvisioning(); + }); + // TODO: Remove SimChangeListener altogether. For now, we retain it + // for logging purposes in case we need to debug something that might + // be related to changing signals from ACTION_SIM_STATE_CHANGED to + // ACTION_CARRIER_CONFIG_CHANGED. mSimChange = new SimChangeListener( - mContext, smHandler, () -> reevaluateSimCardProvisioning()); + mContext, smHandler, () -> { + mLog.log("OBSERVED SIM card change"); + }); mStateReceiver = new StateReceiver(); - IntentFilter filter = new IntentFilter(); + filter = new IntentFilter(); filter.addAction(UsbManager.ACTION_USB_STATE); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); @@ -364,18 +384,30 @@ public class Tethering extends BaseNetworkObserver { return false; } + if (carrierConfigAffirmsEntitlementCheckNotRequired()) { + return false; + } + return (provisionApp.length == 2); + } + + // The logic here is aimed solely at confirming that a CarrierConfig exists + // and affirms that entitlement checks are not required. + // + // TODO: find a better way to express this, or alter the checking process + // entirely so that this is more intuitive. + private boolean carrierConfigAffirmsEntitlementCheckNotRequired() { // Check carrier config for entitlement checks final CarrierConfigManager configManager = (CarrierConfigManager) mContext .getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager != null && configManager.getConfig() != null) { - // we do have a CarrierConfigManager and it has a config. - boolean isEntitlementCheckRequired = configManager.getConfig().getBoolean( - CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL); - if (!isEntitlementCheckRequired) { - return false; - } - } - return (provisionApp.length == 2); + if (configManager == null) return false; + + final PersistableBundle carrierConfig = configManager.getConfig(); + if (carrierConfig == null) return false; + + // A CarrierConfigManager was found and it has a config. + final boolean isEntitlementCheckRequired = carrierConfig.getBoolean( + CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL); + return !isEntitlementCheckRequired; } // Used by the SIM card change observation code. @@ -818,6 +850,7 @@ public class Tethering extends BaseNetworkObserver { } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) { handleWifiApAction(intent); } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) { + mLog.log("OBSERVED configuration changed"); updateConfiguration(); } } @@ -1192,6 +1225,7 @@ public class Tethering extends BaseNetworkObserver { private void reevaluateSimCardProvisioning() { if (!hasMobileHotspotProvisionApp()) return; + if (carrierConfigAffirmsEntitlementCheckNotRequired()) return; ArrayList<Integer> tethered = new ArrayList<>(); synchronized (mPublicSync) { @@ -1559,6 +1593,7 @@ public class Tethering extends BaseNetworkObserver { return; } + mCarrierConfigChange.startListening(); mSimChange.startListening(); mUpstreamNetworkMonitor.start(); @@ -1576,6 +1611,7 @@ public class Tethering extends BaseNetworkObserver { mOffload.stop(); mUpstreamNetworkMonitor.stop(); mSimChange.stopListening(); + mCarrierConfigChange.stopListening(); notifyDownstreamsOfNewUpstreamIface(null); handleNewUpstreamNetworkState(null); } diff --git a/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java b/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java index 3e60f9f6c97a..33c9355ae64b 100644 --- a/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java +++ b/services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java @@ -23,12 +23,15 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.net.util.VersionedBroadcastListener; +import android.net.util.VersionedBroadcastListener.IntentCallback; import android.os.Handler; import android.util.Log; import com.android.internal.telephony.TelephonyIntents; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; /** @@ -37,88 +40,40 @@ import java.util.concurrent.atomic.AtomicInteger; * * @hide */ -public class SimChangeListener { +public class SimChangeListener extends VersionedBroadcastListener { private static final String TAG = SimChangeListener.class.getSimpleName(); private static final boolean DBG = false; - private final Context mContext; - private final Handler mTarget; - private final AtomicInteger mSimBcastGenerationNumber; - private final Runnable mCallback; - private BroadcastReceiver mBroadcastReceiver; - public SimChangeListener(Context ctx, Handler handler, Runnable onSimCardLoadedCallback) { - mContext = ctx; - mTarget = handler; - mCallback = onSimCardLoadedCallback; - mSimBcastGenerationNumber = new AtomicInteger(0); - } - - public int generationNumber() { - return mSimBcastGenerationNumber.get(); + super(TAG, ctx, handler, makeIntentFilter(), makeCallback(onSimCardLoadedCallback)); } - public void startListening() { - if (DBG) Log.d(TAG, "startListening for SIM changes"); - - if (mBroadcastReceiver != null) return; - - mBroadcastReceiver = new SimChangeBroadcastReceiver( - mSimBcastGenerationNumber.incrementAndGet()); + private static IntentFilter makeIntentFilter() { final IntentFilter filter = new IntentFilter(); filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); - - mContext.registerReceiver(mBroadcastReceiver, filter, null, mTarget); + return filter; } - public void stopListening() { - if (DBG) Log.d(TAG, "stopListening for SIM changes"); - - if (mBroadcastReceiver == null) return; - - mSimBcastGenerationNumber.incrementAndGet(); - mContext.unregisterReceiver(mBroadcastReceiver); - mBroadcastReceiver = null; - } - - private boolean isSimCardLoaded(String state) { - return INTENT_VALUE_ICC_LOADED.equals(state); - } - - private class SimChangeBroadcastReceiver extends BroadcastReceiver { - // used to verify this receiver is still current - final private int mGenerationNumber; - - // used to check the sim state transition from non-loaded to loaded - private boolean mSimNotLoadedSeen = false; - - public SimChangeBroadcastReceiver(int generationNumber) { - mGenerationNumber = generationNumber; - } - - @Override - public void onReceive(Context context, Intent intent) { - final int currentGenerationNumber = mSimBcastGenerationNumber.get(); - - if (DBG) { - Log.d(TAG, "simchange mGenerationNumber=" + mGenerationNumber + - ", current generationNumber=" + currentGenerationNumber); - } - if (mGenerationNumber != currentGenerationNumber) return; - - final String state = intent.getStringExtra(INTENT_KEY_ICC_STATE); - Log.d(TAG, "got Sim changed to state " + state + ", mSimNotLoadedSeen=" + - mSimNotLoadedSeen); - - if (!isSimCardLoaded(state)) { - mSimNotLoadedSeen = true; - return; - } - - if (mSimNotLoadedSeen) { - mSimNotLoadedSeen = false; - mCallback.run(); + private static Consumer<Intent> makeCallback(Runnable onSimCardLoadedCallback) { + return new Consumer<Intent>() { + private boolean mSimNotLoadedSeen = false; + + @Override + public void accept(Intent intent) { + final String state = intent.getStringExtra(INTENT_KEY_ICC_STATE); + Log.d(TAG, "got Sim changed to state " + state + ", mSimNotLoadedSeen=" + + mSimNotLoadedSeen); + + if (!INTENT_VALUE_ICC_LOADED.equals(state)) { + mSimNotLoadedSeen = true; + return; + } + + if (mSimNotLoadedSeen) { + mSimNotLoadedSeen = false; + onSimCardLoadedCallback.run(); + } } - } + }; } } diff --git a/services/net/java/android/net/util/VersionedBroadcastListener.java b/services/net/java/android/net/util/VersionedBroadcastListener.java new file mode 100644 index 000000000000..115aa464f4f1 --- /dev/null +++ b/services/net/java/android/net/util/VersionedBroadcastListener.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 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 android.net.util; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.util.Log; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + + +/** + * A utility class that runs the provided callback on the provided handler when + * intents matching the provided filter arrive. Intents received by a stale + * receiver are safely ignored. + * + * Calls to startListening() and stopListening() must happen on the same thread. + * + * @hide + */ +public class VersionedBroadcastListener { + private static final boolean DBG = false; + + public interface IntentCallback { + public void run(Intent intent); + } + + private final String mTag; + private final Context mContext; + private final Handler mHandler; + private final IntentFilter mFilter; + private final Consumer<Intent> mCallback; + private final AtomicInteger mGenerationNumber; + private BroadcastReceiver mReceiver; + + public VersionedBroadcastListener(String tag, Context ctx, Handler handler, + IntentFilter filter, Consumer<Intent> callback) { + mTag = tag; + mContext = ctx; + mHandler = handler; + mFilter = filter; + mCallback = callback; + mGenerationNumber = new AtomicInteger(0); + } + + public int generationNumber() { + return mGenerationNumber.get(); + } + + public void startListening() { + if (DBG) Log.d(mTag, "startListening"); + if (mReceiver != null) return; + + mReceiver = new Receiver(mTag, mGenerationNumber, mCallback); + mContext.registerReceiver(mReceiver, mFilter, null, mHandler); + } + + public void stopListening() { + if (DBG) Log.d(mTag, "stopListening"); + if (mReceiver == null) return; + + mGenerationNumber.incrementAndGet(); + mContext.unregisterReceiver(mReceiver); + mReceiver = null; + } + + private static class Receiver extends BroadcastReceiver { + public final String tag; + public final AtomicInteger atomicGenerationNumber; + public final Consumer<Intent> callback; + // Used to verify this receiver is still current. + public final int generationNumber; + + public Receiver( + String tag, AtomicInteger atomicGenerationNumber, Consumer<Intent> callback) { + this.tag = tag; + this.atomicGenerationNumber = atomicGenerationNumber; + this.callback = callback; + generationNumber = atomicGenerationNumber.incrementAndGet(); + } + + @Override + public void onReceive(Context context, Intent intent) { + final int currentGenerationNumber = atomicGenerationNumber.get(); + + if (DBG) { + Log.d(tag, "receiver generationNumber=" + generationNumber + + ", current generationNumber=" + currentGenerationNumber); + } + if (generationNumber != currentGenerationNumber) return; + + callback.accept(intent); + } + } +} diff --git a/tests/net/java/android/net/util/VersionedBroadcastListenerTest.java b/tests/net/java/android/net/util/VersionedBroadcastListenerTest.java new file mode 100644 index 000000000000..39f59f1c0d69 --- /dev/null +++ b/tests/net/java/android/net/util/VersionedBroadcastListenerTest.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2017 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 android.net.util; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.reset; + +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import com.android.internal.util.test.BroadcastInterceptingContext; + +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VersionedBroadcastListenerTest { + private static final String TAG = VersionedBroadcastListenerTest.class.getSimpleName(); + private static final String ACTION_TEST = "action.test.happy.broadcasts"; + + @Mock private Context mContext; + private BroadcastInterceptingContext mServiceContext; + private Handler mHandler; + private VersionedBroadcastListener mListener; + private int mCallbackCount; + + private void doCallback() { mCallbackCount++; } + + private class MockContext extends BroadcastInterceptingContext { + MockContext(Context base) { + super(base); + } + } + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + if (Looper.myLooper() == null) { + Looper.prepare(); + } + } + + @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + reset(mContext); + mServiceContext = new MockContext(mContext); + mHandler = new Handler(Looper.myLooper()); + mCallbackCount = 0; + final IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_TEST); + mListener = new VersionedBroadcastListener( + TAG, mServiceContext, mHandler, filter, (Intent intent) -> doCallback()); + } + + @After public void tearDown() throws Exception { + if (mListener != null) { + mListener.stopListening(); + mListener = null; + } + } + + private void sendBroadcast() { + final Intent intent = new Intent(ACTION_TEST); + mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + + @Test + public void testBasicListening() { + assertEquals(0, mCallbackCount); + mListener.startListening(); + for (int i = 0; i < 5; i++) { + sendBroadcast(); + assertEquals(i+1, mCallbackCount); + } + mListener.stopListening(); + } + + @Test + public void testBroadcastsBeforeStartAreIgnored() { + assertEquals(0, mCallbackCount); + for (int i = 0; i < 5; i++) { + sendBroadcast(); + assertEquals(0, mCallbackCount); + } + + mListener.startListening(); + sendBroadcast(); + assertEquals(1, mCallbackCount); + } + + @Test + public void testBroadcastsAfterStopAreIgnored() { + mListener.startListening(); + sendBroadcast(); + assertEquals(1, mCallbackCount); + mListener.stopListening(); + + for (int i = 0; i < 5; i++) { + sendBroadcast(); + assertEquals(1, mCallbackCount); + } + } +} diff --git a/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java b/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java index b5d333b8b230..f58ea7e9375f 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java @@ -48,8 +48,6 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) @SmallTest public class SimChangeListenerTest { - private static final int EVENT_UNM_UPDATE = 1; - @Mock private Context mContext; private BroadcastInterceptingContext mServiceContext; private Handler mHandler; |