Loading services/core/java/com/android/server/connectivity/Tethering.java +47 −11 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -180,6 +183,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; Loading Loading @@ -220,11 +225,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); Loading Loading @@ -353,18 +373,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( 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); if (!isEntitlementCheckRequired) { return false; } } return (provisionApp.length == 2); return !isEntitlementCheckRequired; } // Used by the SIM card change observation code. Loading Loading @@ -794,6 +826,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(); } } Loading Loading @@ -1136,6 +1169,7 @@ public class Tethering extends BaseNetworkObserver { private void reevaluateSimCardProvisioning() { if (!hasMobileHotspotProvisionApp()) return; if (carrierConfigAffirmsEntitlementCheckNotRequired()) return; ArrayList<Integer> tethered = new ArrayList<>(); synchronized (mPublicSync) { Loading Loading @@ -1503,6 +1537,7 @@ public class Tethering extends BaseNetworkObserver { return; } mCarrierConfigChange.startListening(); mSimChange.startListening(); mUpstreamNetworkMonitor.start(); Loading @@ -1520,6 +1555,7 @@ public class Tethering extends BaseNetworkObserver { mOffload.stop(); mUpstreamNetworkMonitor.stop(); mSimChange.stopListening(); mCarrierConfigChange.stopListening(); notifyDownstreamsOfNewUpstreamIface(null); handleNewUpstreamNetworkState(null); } Loading services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java +27 −72 Original line number Diff line number Diff line Loading @@ -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; /** Loading @@ -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); } 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); return filter; } 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 static Consumer<Intent> makeCallback(Runnable onSimCardLoadedCallback) { return new Consumer<Intent>() { 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; 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 (!isSimCardLoaded(state)) { if (!INTENT_VALUE_ICC_LOADED.equals(state)) { mSimNotLoadedSeen = true; return; } if (mSimNotLoadedSeen) { mSimNotLoadedSeen = false; mCallback.run(); onSimCardLoadedCallback.run(); } } }; } } services/net/java/android/net/util/VersionedBroadcastListener.java 0 → 100644 +113 −0 Original line number Diff line number Diff line /* * 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); } } } tests/net/java/android/net/util/VersionedBroadcastListenerTest.java 0 → 100644 +131 −0 Original line number Diff line number Diff line /* * 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); } } } tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java +0 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading
services/core/java/com/android/server/connectivity/Tethering.java +47 −11 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -180,6 +183,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; Loading Loading @@ -220,11 +225,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); Loading Loading @@ -353,18 +373,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( 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); if (!isEntitlementCheckRequired) { return false; } } return (provisionApp.length == 2); return !isEntitlementCheckRequired; } // Used by the SIM card change observation code. Loading Loading @@ -794,6 +826,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(); } } Loading Loading @@ -1136,6 +1169,7 @@ public class Tethering extends BaseNetworkObserver { private void reevaluateSimCardProvisioning() { if (!hasMobileHotspotProvisionApp()) return; if (carrierConfigAffirmsEntitlementCheckNotRequired()) return; ArrayList<Integer> tethered = new ArrayList<>(); synchronized (mPublicSync) { Loading Loading @@ -1503,6 +1537,7 @@ public class Tethering extends BaseNetworkObserver { return; } mCarrierConfigChange.startListening(); mSimChange.startListening(); mUpstreamNetworkMonitor.start(); Loading @@ -1520,6 +1555,7 @@ public class Tethering extends BaseNetworkObserver { mOffload.stop(); mUpstreamNetworkMonitor.stop(); mSimChange.stopListening(); mCarrierConfigChange.stopListening(); notifyDownstreamsOfNewUpstreamIface(null); handleNewUpstreamNetworkState(null); } Loading
services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java +27 −72 Original line number Diff line number Diff line Loading @@ -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; /** Loading @@ -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); } 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); return filter; } 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 static Consumer<Intent> makeCallback(Runnable onSimCardLoadedCallback) { return new Consumer<Intent>() { 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; 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 (!isSimCardLoaded(state)) { if (!INTENT_VALUE_ICC_LOADED.equals(state)) { mSimNotLoadedSeen = true; return; } if (mSimNotLoadedSeen) { mSimNotLoadedSeen = false; mCallback.run(); onSimCardLoadedCallback.run(); } } }; } }
services/net/java/android/net/util/VersionedBroadcastListener.java 0 → 100644 +113 −0 Original line number Diff line number Diff line /* * 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); } } }
tests/net/java/android/net/util/VersionedBroadcastListenerTest.java 0 → 100644 +131 −0 Original line number Diff line number Diff line /* * 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); } } }
tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java +0 −2 Original line number Diff line number Diff line Loading @@ -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; Loading