summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Erik Kline <ek@google.com> 2017-10-25 14:07:34 +0000
committer android-build-merger <android-build-merger@google.com> 2017-10-25 14:07:34 +0000
commitbee33f843ea978970f563e2cbef8e3482aa20a7b (patch)
tree933b5cc1ac5321f32b4e768ea98c06252e5c7842
parentb83255ac5242b2f214a03c7bb585821d47f26ba2 (diff)
parentfc0b863b4423ca27fc01e5037af123c9e2792a20 (diff)
Merge "Switch to listening for CarrierConfig changes for provisioning rechecks" am: c21effd526 am: 166c27440d
am: fc0b863b44 Change-Id: I2e46e8271209798692d5074d336ef73eb96370bb
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java58
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/SimChangeListener.java99
-rw-r--r--services/net/java/android/net/util/VersionedBroadcastListener.java113
-rw-r--r--tests/net/java/android/net/util/VersionedBroadcastListenerTest.java131
-rw-r--r--tests/net/java/com/android/server/connectivity/tethering/SimChangeListenerTest.java2
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;